support AchievementTask and AchievementID rewards (#25)

- add task and id reward xml databases
   - thanks to Project Edge community for sniffed network data!
- implement SetAchievementAndGetReward, SetUserAchievementAndGetReward
- partially implement SetUserAchievementTask
- move rewards logic to AchievementService
- show real value of farming and fishing XP
- show UDP points

Co-authored-by: Spirtix <michal.prazak1@gmail.com>
This commit is contained in:
rpaciorek 2023-08-23 20:07:08 +00:00 committed by GitHub
parent dd9dfa4873
commit 665089d5ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 2773 additions and 77 deletions

View File

@ -76,6 +76,9 @@ Then run School of Dragons.
- UseInventory
- DeleteProfile
- DeleteAccountNotification
- SetAchievementAndGetReward
- GetAchievementsByUserID
- GetPetAchievementsByUserID
#### Implemented enough (probably)
- GetRules (doesn't return any rules, probably doesn't need to)
@ -85,6 +88,7 @@ Then run School of Dragons.
- GetCommonInventory
- GetUserRoomItemPositions
- SetUserRoomItemPositions
- SetUserAchievementAndGetReward (works like SetAchievementAndGetReward)
#### Partially implemented
- GetUserProfileByUserID (a lot is still placeholder)
@ -96,10 +100,10 @@ Then run School of Dragons.
- GetUserRoomList (room categories are not implemented)
- SetNextItemState (may require more work, we don't know yet)
- SetUserRoom
- SetUserAchievementTask (returns a real reward but still use task placeholder)
#### Currently static or stubbed
- GetAllRanks (needs to be populated with what ranks the user has)
- GetPetAchievementsByUserId (always returns null)
- GetAchievementTaskInfo (returns a static XML)
- GetAllRewardTypeMultiplier (returns a static XML)
- GetBuddyList (returns an emtpy array)
@ -109,9 +113,6 @@ Then run School of Dragons.
- SaveMessage (doesn't do anything and returns false)
- GetMMOServerInfoWithZone (returns an empty array)
- GetActiveChallenges (returns an empty array)
- GetAchievementsByUserID (returns an achievement with the provided user ID)
- SetAchievementAndGetReward (returns 5 gems)
- SetUserAchievementTask (returns a placeholder achievement)
- GetAnnouncementsByUser (returns no announcements, but that might be sufficient)
- GetAverageRatingForRoom (return max rating)
- GetUserActivityByUserID (returns an empty array)

View File

@ -115,8 +115,9 @@ public class AchievementController : Controller {
return Ok(new ArrayOfUserAchievementInfo {
UserAchievementInfo = new UserAchievementInfo[]{
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.PlayerXP),
achievementService.CreateUserAchievementInfo(viking.Id, 60000, AchievementPointTypes.PlayerFarmingXP), // TODO: placeholder until there is no leveling for farm XP
achievementService.CreateUserAchievementInfo(viking.Id, 20000, AchievementPointTypes.PlayerFishingXP), // TODO: placeholder until there is no leveling for fishing XP
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.PlayerFarmingXP),
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.PlayerFishingXP),
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.UDTPoints),
}
});
}
@ -136,45 +137,51 @@ public class AchievementController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("AchievementWebService.asmx/SetAchievementAndGetReward")]
[Route("AchievementWebService.asmx/SetUserAchievementAndGetReward")]
public IActionResult SetAchievementAndGetReward([FromForm] string apiToken, [FromForm] int achievementID) {
// TODO: This is a placeholder; returns 5 gems
Viking? viking = ctx.Sessions.FirstOrDefault(x => x.ApiToken == apiToken).Viking;
return Ok(new AchievementReward[1] {
new AchievementReward {
Amount = 5,
PointTypeID = AchievementPointTypes.CashCurrency,
EntityID = Guid.Parse(viking.Id),
EntityTypeID = 1,
RewardID = 552
}
});
if (viking is null) {
return Unauthorized();
}
var rewards = achievementService.ApplyAchievementRewardsByID(viking, achievementID);
ctx.SaveChanges();
return Ok(rewards);
}
[HttpPost]
[Produces("application/xml")]
[Route("V2/AchievementWebService.asmx/SetUserAchievementTask")]
[DecryptRequest("achievementTaskSetRequest")]
public IActionResult SetUserAchievementTask([FromForm] string apiToken, [FromForm] int achievementID) {
// TODO: This is a placeholder
string xml = Request.Form["achievementTaskSetRequest"];
AchievementTaskSetResponse response = new AchievementTaskSetResponse {
Success = true,
UserMessage = true,
AchievementName = "Placeholder Achievement",
Level = 1,
AchievementTaskGroupID = 1279,
LastLevelCompleted = true,
AchievementInfoID = 1279,
AchievementRewards = new AchievementReward[1] {
new AchievementReward {
Amount = 25,
PointTypeID = AchievementPointTypes.PlayerXP,
RewardID = 910,
EntityTypeID =1
public IActionResult SetUserAchievementTask([FromForm] string apiToken) {
Viking? viking = ctx.Sessions.FirstOrDefault(x => x.ApiToken == apiToken).Viking;
if (viking is null) {
return Unauthorized();
}
AchievementTaskSetRequest request = XmlUtil.DeserializeXml<AchievementTaskSetRequest>(Request.Form["achievementTaskSetRequest"]);
var response = new List<AchievementTaskSetResponse>();
foreach (var task in request.AchievementTaskSet) {
response.Add(
new AchievementTaskSetResponse {
Success = true,
UserMessage = true, // TODO: placeholder
AchievementName = "Placeholder Achievement", // TODO: placeholder
Level = 1, // TODO: placeholder
AchievementTaskGroupID = 1279, // TODO: placeholder
LastLevelCompleted = true, // TODO: placeholder
AchievementInfoID = 1279, // TODO: placeholder
AchievementRewards = achievementService.ApplyAchievementRewardsByTask(viking, task)
}
}
};
return Ok(new ArrayOfAchievementTaskSetResponse { AchievementTaskSetResponse = new AchievementTaskSetResponse[1] { response } });
);
}
ctx.SaveChanges();
return Ok(new ArrayOfAchievementTaskSetResponse { AchievementTaskSetResponse = response.ToArray() });
}
[HttpPost]

View File

@ -145,8 +145,9 @@ public class ProfileController : Controller {
AchievementInfo = null, // placeholder
Achievements = new UserAchievementInfo[] {
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.PlayerXP),
achievementService.CreateUserAchievementInfo(viking.Id, 60000, AchievementPointTypes.PlayerFarmingXP), // TODO: placeholder until there is no leveling for farm XP
achievementService.CreateUserAchievementInfo(viking.Id, 20000, AchievementPointTypes.PlayerFishingXP), // TODO: placeholder until there is no leveling for fishing XP
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.PlayerFarmingXP),
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.PlayerFishingXP),
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.UDTPoints),
}
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -32,4 +32,7 @@ public enum AchievementPointTypes {
[XmlEnum("13")]
Unknown13 = 13,
[XmlEnum("21")]
Unknown21 = 21,
}

View File

@ -0,0 +1,26 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "ATS", Namespace = "")]
[Serializable]
public class AchievementTask
{
[XmlElement(ElementName = "oid")]
public string OwnerID;
[XmlElement(ElementName = "tid")]
public int TaskID;
[XmlElement(ElementName = "aiid")]
public int AchievementInfoID;
[XmlElement(ElementName = "rid")]
public string RelatedID;
[XmlElement(ElementName = "pts")]
public int Points;
[XmlElement(ElementName = "etid")]
public int EntityTypeID;
}

View File

@ -0,0 +1,11 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "ATSRQ", Namespace = "")]
[Serializable]
public class AchievementTaskSetRequest
{
[XmlElement(ElementName = "as")]
public AchievementTask[] AchievementTaskSet { get; set; }
}

View File

@ -0,0 +1,14 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "AchievementsIdInfo", Namespace = "")]
[Serializable]
public class AchievementsIdInfo
{
[XmlElement(ElementName = "AID")]
public int AchievementID;
[XmlElement(ElementName = "AR")]
public AchievementReward[] AchievementReward;
}

View File

@ -0,0 +1,14 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "AchievementsTaskInfo", Namespace = "")]
[Serializable]
public class AchievementsTaskInfo
{
[XmlElement(ElementName = "TID")]
public int TaskID;
[XmlElement(ElementName = "AR")]
public AchievementReward[] AchievementReward;
}

View File

@ -9,17 +9,28 @@ namespace sodoff.Services {
public class AchievementService {
Dictionary<AchievementPointTypes, UserRank[]> ranks = new();
Dictionary<int, AchievementReward[]> achivmentsRewardByID = new();
Dictionary<int, AchievementReward[]> achivmentsRewardByTask = new();
int dragonAdultMinXP;
int dragonTitanMinXP;
public AchievementService() {
ArrayOfUserRank allranks = XmlUtil.DeserializeXml<ArrayOfUserRank>(XmlUtil.ReadResourceXmlString("allranks"));
foreach (var pointType in Enum.GetValues<AchievementPointTypes>()) {
ranks[pointType] = allranks.UserRank.Where(r => r.PointTypeID == pointType).ToArray();
}
AchievementsIdInfo[] allAchievementsIdInfo = XmlUtil.DeserializeXml<AchievementsIdInfo[]>(XmlUtil.ReadResourceXmlString("achievementsids"));
foreach (var achievementsIdInfo in allAchievementsIdInfo) {
achivmentsRewardByID[achievementsIdInfo.AchievementID] = achievementsIdInfo.AchievementReward;
}
AchievementsTaskInfo[] allAchievementsTaskInfo = XmlUtil.DeserializeXml<AchievementsTaskInfo[]>(XmlUtil.ReadResourceXmlString("achievementstasks"));
foreach (var achievementsTaskInfo in allAchievementsTaskInfo) {
achivmentsRewardByTask[achievementsTaskInfo.TaskID] = achievementsTaskInfo.AchievementReward;
}
dragonAdultMinXP = ranks[AchievementPointTypes.DragonXP][10].Value;
dragonTitanMinXP = ranks[AchievementPointTypes.DragonXP][20].Value;
}
@ -88,5 +99,56 @@ namespace sodoff.Services {
xpPoints.Value += value ?? 0;
}
}
public void ApplyAchievementReward(Viking viking, AchievementReward reward) {
if (reward.PointTypeID == AchievementPointTypes.ItemReward) {
// TODO: This is not a pretty solution. Use inventory service in the future
InventoryItem? ii = viking.Inventory.InventoryItems.FirstOrDefault(x => x.ItemId == reward.ItemID);
if (ii is null) {
ii = new InventoryItem {
ItemId = reward.ItemID,
Quantity = 0
};
viking.Inventory.InventoryItems.Add(ii);
}
ii.Quantity += (int)reward.Amount!;
} else { // currencies, all types of player XP and dragon XP
AddAchievementPoints(viking, reward.PointTypeID, reward.Amount);
}
}
public AchievementReward[] ApplyAchievementRewards(Viking viking, AchievementReward[] rewards) {
foreach (var reward in rewards) {
ApplyAchievementReward(viking, reward);
/* TODO we don't need this?
if (reward.PointTypeID == AchievementPointTypes.DragonXP) {
reward.EntityID = Guid.Parse(viking.SelectedDragon.EntityId)
} else {
reward.EntityID = Guid.Parse(viking.Id)
} */
}
// TODO: check trophies, etc criteria and id need apply and add to results extra reward here
return rewards;
}
public AchievementReward[] ApplyAchievementRewardsByID(Viking viking, int achievementID) {
if (achivmentsRewardByID.ContainsKey(achievementID)) {
var rewards = achivmentsRewardByID[achievementID];
return ApplyAchievementRewards(viking, rewards);
} else {
Console.WriteLine(string.Format("Unknown rewards for achievementID={0}", achievementID));
return new AchievementReward[0];
}
}
public AchievementReward[] ApplyAchievementRewardsByTask(Viking viking, AchievementTask task) {
if (achivmentsRewardByTask.ContainsKey(task.TaskID)) {
var rewards = achivmentsRewardByTask[task.TaskID];
return ApplyAchievementRewards(viking, rewards);
} else {
Console.WriteLine(string.Format("Unknown rewards for taskID={0}", task.TaskID));
return new AchievementReward[0];
}
}
}
}

View File

@ -38,11 +38,6 @@ public class MissionService {
if (completed) {
Mission mission = GetMissionWithProgress(missionId, userId);
if (MissionCompleted(mission)) {
// Get mission rewards
result.Add(new MissionCompletedResult {
MissionID = missionId,
Rewards = mission.Rewards.ToArray()
});
// Update mission from active to completed
Viking viking = ctx.Vikings.FirstOrDefault(x => x.Id == userId)!;
MissionState? missionState = viking.MissionStates.FirstOrDefault(x => x.MissionId == missionId);
@ -50,23 +45,14 @@ public class MissionService {
missionState.MissionStatus = MissionStatus.Completed;
missionState.UserAccepted = null;
}
foreach (var reward in mission.Rewards) {
if (reward.PointTypeID == AchievementPointTypes.ItemReward) {
// TODO: This is not a pretty solution. Use inventory service in the future
InventoryItem? ii = viking.Inventory.InventoryItems.FirstOrDefault(x => x.ItemId == reward.ItemID);
if (ii is null) {
ii = new InventoryItem {
ItemId = reward.ItemID,
Quantity = 0
};
viking.Inventory.InventoryItems.Add(ii);
}
ii.Quantity += (int)reward.Amount!;
} else { // currencies, all types of player XP and dragon XP
achievementService.AddAchievementPoints(viking, reward.PointTypeID, reward.Amount);
}
}
var rewards = achievementService.ApplyAchievementRewards(viking, mission.Rewards.ToArray());
ctx.SaveChanges();
// Get mission rewards
result.Add(new MissionCompletedResult {
MissionID = missionId,
Rewards = rewards
});
}
}
return result;

View File

@ -9,11 +9,14 @@ namespace sodoff.Services;
public class RoomService {
private readonly DBContext ctx;
private ItemService itemService;
public RoomService(DBContext ctx, ItemService itemService) {
private ItemService itemService;
private AchievementService achievementService;
public RoomService(DBContext ctx, ItemService itemService, AchievementService achievementService) {
this.ctx = ctx;
this.itemService = itemService;
this.achievementService = achievementService;
}
public void CreateRoom(Viking viking, string roomId) {
@ -131,21 +134,7 @@ public class RoomService {
}
if (rewards != null) {
response.Rewards = rewards;
foreach (var reward in rewards) {
if (reward.PointTypeID == AchievementPointTypes.ItemReward) {
// TODO: This is not a pretty solution. Use inventory service in the future
InventoryItem? ii = item.Room.Viking.Inventory.InventoryItems.FirstOrDefault(x => x.ItemId == reward.ItemID);
if (ii is null) {
ii = new InventoryItem {
ItemId = reward.ItemID,
Quantity = 0
};
item.Room.Viking.Inventory.InventoryItems.Add(ii);
}
ii.Quantity += (int)reward.Amount!;
}
}
response.Rewards = achievementService.ApplyAchievementRewards(item.Room.Viking, rewards);
}
DateTime stateChange = new DateTime(DateTime.Now.Ticks);

View File

@ -20,6 +20,8 @@
<None Remove="Resources\rewardmultiplier.xml" />
<None Remove="Resources\store.xml" />
<None Remove="Resources\allranks.xml" />
<None Remove="Resources\achievementsids.xml" />
<None Remove="Resources\achievementstasks.xml" />
<None Remove="Resources\defaultmissionlist.xml" />
</ItemGroup>
<ItemGroup>
@ -35,6 +37,12 @@
<None Update="Resources\tutorialmission.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\achievementsids.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\achievementstasks.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\rewardmultiplier.xml">
@ -52,6 +60,12 @@
<EmbeddedResource Include="Resources\allranks.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="Resources\achievementsids.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="Resources\achievementstasks.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="Resources\items.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>