diff --git a/mitm-redirect.py b/mitm-redirect.py
index c7a4c22..509c2fd 100644
--- a/mitm-redirect.py
+++ b/mitm-redirect.py
@@ -4,7 +4,7 @@ import mitmproxy.http
def routable(path):
methods = ['GetRules', 'LoginParent', 'RegisterParent', 'GetSubscriptionInfo', 'GetUserInfoByApiToken', 'IsValidApiToken_V2', 'ValidateName', 'GetDefaultNameSuggestion', 'RegisterChild', 'GetProfileByUserId', 'LoginChild', 'GetUserProfileByUserID', 'GetKeyValuePair', 'SetKeyValuePair', 'GetKeyValuePairByUserID', 'SetKeyValuePairByUserID', 'GetUserProfile', 'GetQuestions', 'GetCommonInventory',
-'GetAuthoritativeTime', 'SetAvatar', 'GetAllActivePetsByuserId', 'GetPetAchievementsByUserID', 'GetDetailedChildList', 'GetStore', 'GetAllRanks']
+'GetAuthoritativeTime', 'SetAvatar', 'GetAllActivePetsByuserId', 'GetPetAchievementsByUserID', 'GetDetailedChildList', 'GetStore', 'GetAllRanks', 'GetUserUpcomingMissionState', 'GetUserActiveMissionState', 'GetUserCompletedMissionState', 'SetTaskState']
for method in methods:
if method in path:
return True
diff --git a/src/Attributes/SignResponse.cs b/src/Attributes/SignResponse.cs
index 4533240..2d9a69a 100644
--- a/src/Attributes/SignResponse.cs
+++ b/src/Attributes/SignResponse.cs
@@ -8,7 +8,7 @@ using System.Text;
namespace sodoff.Attributes;
public class SignResponse : Attribute, IAsyncResultFilter {
const string key = "11A0CC5A-C4DF-4A0E-931C-09A44C9966AE";
- public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) {
+ public async System.Threading.Tasks.Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) {
var originalBodyStream = context.HttpContext.Response.Body;
try {
diff --git a/src/Controllers/Common/AchievementController.cs b/src/Controllers/Common/AchievementController.cs
index 28968dd..9018cc2 100644
--- a/src/Controllers/Common/AchievementController.cs
+++ b/src/Controllers/Common/AchievementController.cs
@@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Mvc;
using sodoff.Model;
+using sodoff.Util;
namespace sodoff.Controllers.Common;
public class AchievementController : Controller {
@@ -24,13 +25,6 @@ public class AchievementController : Controller {
[Route("AchievementWebService.asmx/GetAllRanks")]
public IActionResult GetAllRanks() {
// TODO, this is a placeholder
- var assembly = Assembly.GetExecutingAssembly();
- string resourceName = assembly.GetManifestResourceNames().Single(str => str.EndsWith("allranks.xml"));
-
- using (Stream stream = assembly.GetManifestResourceStream(resourceName))
- using (StreamReader reader = new StreamReader(stream)) {
- string result = reader.ReadToEnd();
- return Ok(result);
- }
+ return Ok(XmlUtil.ReadResourceXmlString("allranks"));
}
}
diff --git a/src/Controllers/Common/ContentController.cs b/src/Controllers/Common/ContentController.cs
index be98711..4227481 100644
--- a/src/Controllers/Common/ContentController.cs
+++ b/src/Controllers/Common/ContentController.cs
@@ -202,4 +202,55 @@ public class ContentController : Controller {
// TODO, this is a placeholder
return Ok("\n");
}
+
+ [HttpPost]
+ [Produces("application/xml")]
+ [Route("V2/ContentWebService.asmx/GetUserUpcomingMissionState")]
+ public IActionResult GetUserUpcomingMissionState([FromForm] string apiToken, [FromForm] string userId) {
+ Session? session = ctx.Sessions.FirstOrDefault(s => s.ApiToken == apiToken);
+ UserMissionStateResult result = new UserMissionStateResult();
+
+ if (session is null)
+ return Ok(result);
+
+ result.UserID = Guid.Parse(session.VikingId);
+ return Ok(result); // TODO: placeholder, returns no upcoming missions
+ }
+
+ [HttpPost]
+ [Produces("application/xml")]
+ [Route("V2/ContentWebService.asmx/GetUserActiveMissionState")]
+ public IActionResult GetUserActiveMissionState([FromForm] string apiToken, [FromForm] string userId) {
+ Session? session = ctx.Sessions.FirstOrDefault(s => s.ApiToken == apiToken);
+ UserMissionStateResult result = new UserMissionStateResult { Missions = new List() };
+ result.Missions.Add(XmlUtil.DeserializeXml(XmlUtil.ReadResourceXmlString("tutorialmission")));
+
+ if (session is null)
+ return Ok("error");
+
+ result.UserID = Guid.Parse(session.VikingId);
+ return Ok(result); // TODO: placeholder, returns the tutorial
+ }
+
+ [HttpPost]
+ [Produces("application/xml")]
+ [Route("V2/ContentWebService.asmx/GetUserCompletedMissionState")]
+ public IActionResult GetUserCompletedMissionState([FromForm] string apiToken, [FromForm] string userId) {
+ Session? session = ctx.Sessions.FirstOrDefault(s => s.ApiToken == apiToken);
+ UserMissionStateResult result = new UserMissionStateResult();
+
+ if (session is null)
+ return Ok(result);
+
+ result.UserID = Guid.Parse(session.VikingId);
+ return Ok(result); // TODO: placeholder, returns no completed missions
+ }
+
+ [HttpPost]
+ [Produces("application/xml")]
+ [Route("V2/ContentWebService.asmx/SetTaskState")]
+ public IActionResult SetTaskState([FromForm] string apiToken, [FromForm] int missionId, [FromForm] int taskId, [FromForm] bool completed) {
+ // TODO
+ return Ok(new SetTaskStateResult { Success = true, Status = SetTaskStateStatus.TaskCanBeDone });
+ }
}
diff --git a/src/Resources/tutorialmission.xml b/src/Resources/tutorialmission.xml
new file mode 100644
index 0000000..9dae3aa
--- /dev/null
+++ b/src/Resources/tutorialmission.xml
@@ -0,0 +1,479 @@
+
+
+ false
+ 999
+ Quest 1
+ 51
+ 3
+
+ <Data><Setup><Scene>HubFTUEDO</Scene><Asset>RS_DATA/PfGrpQMMO_Off.unity3d/PfGrpQMMO_Off</Asset><Recursive>false</Recursive><Persistent>false</Persistent></Setup><Setup><Scene>HubSchoolDO</Scene><Asset>RS_DATA/PfGrpFTUE2020T15.unity3d/PfGrpFTUE2020T15</Asset><Recursive>false</Recursive><Persistent>true</Persistent></Setup><Repeat>0</Repeat><Hidden>0</Hidden><Reward><Asset>PfUiMissionRewardDBDO</Asset></Reward><Random>0</Random><Title><Text>New Student</Text><ID>922059</ID></Title></Data>
+ false
+ 0
+
+
+ 2
+ False
+ false
+
+
+ 1
+ False
+ false
+
+
+ all
+ true
+ 4
+ 1
+
+ 1
+ 999
+ 5930
+ 0
+
+
+ 1
+ 999
+ 5931
+ 0
+
+
+ 1
+ 999
+ 5932
+ 0
+
+
+ 2
+ 999
+ 2880
+ 0
+
+
+ 2
+ 999
+ 2881
+ 0
+
+
+ 1
+ 999
+ 5935
+ 0
+
+
+ 1
+ 999
+ 5936
+ 0
+
+
+ 1
+ 999
+ 5937
+ 0
+
+
+ 1
+ 999
+ 5938
+ 0
+
+
+ 2
+ 999
+ 2882
+ 0
+
+
+ 1
+ 999
+ 5940
+ 0
+
+
+ 1
+ 999
+ 5941
+ 0
+
+
+ 1
+ 999
+ 5942
+ 0
+
+
+ 2
+ 999
+ 2883
+ 0
+
+
+ 1
+ 999
+ 5944
+ 0
+
+
+ 1
+ 999
+ 6619
+ 0
+
+
+
+ 8
+ 201320
+ 0
+
+ false
+ 2880
+ FTUE2020-04
+ 51
+ 3
+ 999
+ <Data><Repeat>0</Repeat><Hidden>0</Hidden><Reward><Asset /></Reward><Random>0</Random><Title><Text>{{Input}} on cage</Text><ID>939800</ID></Title></Data>
+ false
+ 0
+
+
+ 2
+ False
+ false
+
+
+ 1
+ False
+ false
+
+
+ all
+ true
+ 4
+ 1
+
+ 1
+ 2880
+ 5933
+ 0
+
+
+
+ 8
+ 0
+ 0
+
+ 5933
+ FTUE2020-04: Click cage
+ <Data><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>Be careful. Even baby dragons can be dangerous when frightened. Step forward and {{input}} on the cage door to open it. After that, just trust your instincts!@@Hey, don't worry, you got this.</Text><ID>939805</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><End><Type>CutScene</Type><Asset>RS_DATA/PfGrpFTUE2020T04CS.unity3d/PfGrpFTUE2020T04CS</Asset><ItemID>0</ItemID><Priority>0</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Type</Key><Value>DragonSelect</Value></Pair><Pair><Key>NPC</Key><Value>PfDWFTUECage</Value></Pair><Pair><Key>Asset</Key><Value>RS_DATA/PfUiFTUEDragonSelection.unity3d/PfUiFTUEDragonSelection</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Meet</Type><Title><Text>{{Input}} on the Dragon Cage</Text><ID>939804</ID></Title><Desc><Text>{{Input}} on the Dragon Cage</Text><ID>939804</ID></Desc><AutoComplete><Pair><Key>RaisedPetStage</Key><Value>BABY</Value></Pair></AutoComplete></Data>
+ 0
+
+
+
+ false
+ 2881
+ FTUE2020-05
+ 51
+ 3
+ 999
+ <Data><Repeat>0</Repeat><Hidden>0</Hidden><Reward><Asset>PfUiMissionRewardDBDO</Asset></Reward><Random>0</Random><Title><Text>Look for a way out of the cave</Text><ID>939801</ID></Title></Data>
+ false
+ 0
+
+
+ 2
+ False
+ false
+
+
+ 1
+ False
+ false
+
+
+ all
+ true
+ 4
+ 1
+
+ 1
+ 2881
+ 5934
+ 0
+
+
+
+ 8
+ 206555
+ 0
+
+ 5934
+ FTUE2020-05: Look for a way out
+ <Data><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>Whoa, that was unexpected. Who would have thought you two would bond so quickly?@@You’re a natural at this! Well, mission accomplished!@@Now, what do you say we find a way out of here?</Text><ID>939807</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><End><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>This must be the heaviest part of the cave-in. With all these boulders piled up, this might be a dead end for us.@@{{dragon name}} is only a Tiny Tooth, so their dragon fire isn't quite enough to shatter that rock. But they are very close, and we can help them to grow.</Text><ID>939808</ID><ItemID>0</ItemID><Priority>0</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Name</Key><Value>PfMarker_CaveIn</Value></Pair><Pair><Key>Range</Key><Value>8</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Visit</Type><Title><Text>Look for a way out of the Cave</Text><ID>939801</ID></Title><Desc><Text>Look for a way out of the Cave</Text><ID>939801</ID></Desc></Data>
+ 0
+
+
+ 1
+ 6
+
+ 1
+ 8399
+ 10996
+ 206555
+ true
+ 1
+ 1
+
+ 0
+
+ 0
+ 0
+
+
+
+ false
+ 2882
+ FTUE2020-10
+ 51
+ 3
+ 999
+ <Data><Repeat>0</Repeat><Hidden>0</Hidden><Reward><Asset /></Reward><Random>0</Random><Title><Text>Light fire</Text><ID>939802</ID></Title></Data>
+ false
+ 0
+
+
+ 2
+ False
+ false
+
+
+ 1
+ False
+ false
+
+
+ all
+ true
+ 4
+ 1
+
+ 1
+ 2882
+ 5939
+ 0
+
+
+
+ 8
+ 206242
+ 0
+
+ 5939
+ FTUE2020-10: Light fire
+ <Data><Setup><Scene>HubFTUEDO</Scene><Asset>PfDWHiccup</Asset><Location>PfMarker_HiccupFire</Location><Recursive>false</Recursive><Persistent>false</Persistent></Setup><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>There’s a good spot for a signal fire. Would you ask {{dragon name}} to shoot it?</Text><ID>939810</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><End><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>That’s not going to be enough; the fire is just too small...</Text><ID>939811</ID><ItemID>0</ItemID><Priority>0</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Location</Key><Value>PfMarker_FireSpot</Value></Pair><Pair><Key>Name</Key><Value>LightFire</Value></Pair><Pair><Key>ItemName</Key><Value>PfDragonLitFirePit</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Action</Type><Title><Text>{{Input}} on the Fire Pit to Light a Signal Fire</Text><ID>942392</ID></Title><Desc><Text>{{Input}} on the Fire Pit to Light a Signal Fire</Text><ID>942392</ID></Desc></Data>
+ 0
+
+
+
+ false
+ 2883
+ FTUE2020-14
+ 51
+ 3
+ 999
+ <Data><Repeat>0</Repeat><Hidden>0</Hidden><Reward><Asset>PfUiMissionRewardDBDO</Asset></Reward><Random>0</Random><Title><Text>Follow Hiccup</Text><ID>939803</ID></Title></Data>
+ false
+ 0
+
+
+ 2
+ False
+ false
+
+
+ 1
+ False
+ false
+
+
+ all
+ true
+ 4
+ 1
+
+ 1
+ 2883
+ 5943
+ 0
+
+
+
+ 8
+ 207960
+ 0
+
+ 5943
+ FTUE2020-14: Follow Hiccup
+ <Data><Setup><Scene>HubFTUEDO</Scene><Asset>PfDWToothlessAlphaHiccupRiderPortal</Asset><Location>PfMarker_HiccupAndToothless</Location><Recursive>false</Recursive><Persistent>false</Persistent></Setup><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>Now that you've got the hang of this, follow me and Toothless! Come on, I want to show you something cool!</Text><ID>939813</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><Objective><Pair><Key>Scene</Key><Value>HubSchoolDO</Value></Pair><Pair><Key>Location</Key><Value>PfMarker_LoadSchool</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Visit</Type><Title><Text>Follow Hiccup and Toothless</Text><ID>942393</ID></Title><Desc><Text>Follow Hiccup and Toothless</Text><ID>942393</ID></Desc></Data>
+ 0
+
+
+ 1
+ 6
+
+ 1
+ 10897
+ 20882
+ 207960
+ true
+ 1
+ 1
+
+ 0
+
+ 0
+ 0
+
+
+
+ 5930
+ FTUE2020-01: Talk to Hiccup
+ <Data><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>Hey, are you all right, {{Name}}? That cave collapse was no joke. Give me a hand, will you? Use the movement controls to walk here and {{input}} on me.</Text><ID>939815</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><End><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>I'm glad you're okay. I guess our investigation about potential Dragon Hunters lurking in these caves paid off, it seems they had a camp right under our feet this entire time.</Text><ID>939816</ID><ItemID>0</ItemID><Priority>0</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>NPC</Key><Value>PfDWHiccup</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Meet</Type><Title><Text>Walk forward and {{input}} on Hiccup</Text><ID>929442</ID></Title></Data>
+ 0
+
+
+ 5931
+ FTUE2020-02: Find axe
+ <Data><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>I’d like to meet up with Toothless again, but there are a few cages here that might have dragons in them.@@We can’t just leave them. I’ll start opening this other one, do you think you could handle the one on your right?@@That lock looks rusted, though... Maybe there’s an axe somewhere around here...</Text><ID>939818</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Name</Key><Value>PfCollectDWAxe</Value></Pair><Pair><Key>Quantity</Key><Value>1</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Collect</Type><Title><Text>Find the Axe in the Cave</Text><ID>939817</ID></Title><Desc><Text>Find the Axe in the Cave</Text><ID>939817</ID></Desc></Data>
+ 0
+
+
+ 5932
+ FTUE2020-03: Chop padlock
+ <Data><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>Perfect! That axe looks strong enough. I'm sure it can chop the padlock off the dragon cage without a problem!</Text><ID>939820</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><End><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>All right, that did the trick!</Text><ID>939821</ID><ItemID>0</ItemID><Priority>0</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Location</Key><Value>ChoppableCage</Value></Pair><Pair><Key>Name</Key><Value>Chop</Value></Pair><Pair><Key>ItemName</Key><Value>PfDragonCageChop</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Action</Type><Title><Text>Chop the Padlock on the Dragon Cage</Text><ID>939819</ID></Title><Desc><Text>Chop the Padlock on the Dragon Cage</Text><ID>939819</ID></Desc></Data>
+ 0
+
+
+ 5935
+ FTUE2020-06: Age Up
+ <Data><Setup><Scene>HubFTUEDO</Scene><Asset>PfDWHiccup</Asset><Location>PfMarker_HiccupCaveIn</Location><Recursive>false</Recursive><Persistent>false</Persistent></Setup><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>This will allow us to take a dragon straight to Broad Wing Stage! I'll show you how.</Text><ID>939823</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Name</Key><Value>AgeUp</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Action</Type><Title><Text>Age Up {{dragon name}}</Text><ID>942394</ID></Title><Desc><Text>Age Up {{dragon name}}</Text><ID>942394</ID></Desc><AutoComplete><Pair><Key>RaisedPetStage</Key><Value>ADULT</Value></Pair></AutoComplete></Data>
+ 0
+
+
+ 5936
+ FTUE2020-07: Shoot
+ <Data><Setup><Scene>HubFTUEDO</Scene><Asset>PfDWHiccup</Asset><Location>PfMarker_HiccupCaveIn</Location><Recursive>false</Recursive><Persistent>false</Persistent></Setup><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>All right, let's try this now! Look for a weak spot in the rocks, then have {{dragon name}} shoot some fireballs!@@{{Input}} on the cave-in and select the [c][3eebff]Fire[/c][ffffff] button.</Text><ID>939825</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Location</Key><Value>PfCaveInTarget</Value></Pair><Pair><Key>Name</Key><Value>LightFire</Value></Pair><Pair><Key>ItemName</Key><Value>PfCaveInTarget</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Action</Type><Title><Text>{{Input}} on the Cave-In</Text><ID>942395</ID></Title><Desc><Text>{{Input}} on the Cave-In</Text><ID>942395</ID></Desc></Data>
+ 0
+
+
+ 5937
+ FTUE2020-08: Collect Box
+ <Data><Setup><Scene>HubFTUEDO</Scene><Asset>PfDWHiccup</Asset><Location>PfMarker_HiccupCaveIn</Location><Recursive>false</Recursive><Persistent>false</Persistent></Setup><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>That was perfect!</Text><ID>939828</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>Wait... What's that? Is that a chest?</Text><ID>939829</ID><ItemID>0</ItemID><Priority>1</Priority></Offer><End><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>A box of dragon eggs...@@Those Dragon Hunters are getting bolder than we expected.</Text><ID>941113</ID><ItemID>0</ItemID><Priority>0</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Location</Key><Value>PfCollectDWOpenArcticChest</Value></Pair><Pair><Key>Name</Key><Value>PfCollectDWOpenArcticChest</Value></Pair><Pair><Key>Quantity</Key><Value>1</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Collect</Type><Title><Text>Pick up the Chest</Text><ID>939827</ID></Title><Desc><Text>Pick up the Chest</Text><ID>939827</ID></Desc></Data>
+ 0
+
+
+ 5938
+ FTUE2020-09: Exit cave
+ <Data><Setup><Scene>HubFTUEDO</Scene><Asset>PfDWHiccup</Asset><Location>PfMarker_HiccupChest</Location><Recursive>false</Recursive><Persistent>false</Persistent></Setup><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>Well, one problem at a time. {{Name}}, can you follow the path of the cave and figure out a way to set up a signal? @@My friends should be nearby, they'll be able to help us out!</Text><ID>939831</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Name</Key><Value>PfMarker_CaveExit</Value></Pair><Pair><Key>Range</Key><Value>3</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Visit</Type><Title><Text>Leave the Cave</Text><ID>939830</ID></Title><Desc><Text>Leave the Cave</Text><ID>939830</ID></Desc></Data>
+ 0
+
+
+ 5940
+ FTUE2020-11: Collect Leaves
+ <Data><Setup><Scene>HubFTUEDO</Scene><Asset>PfDWHiccup</Asset><Location>PfMarker_HiccupFire</Location><Recursive>false</Recursive><Persistent>false</Persistent></Setup><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>I’ll get some more wood stacked; can you grab some green leaves to help make this fire more smokey?</Text><ID>939833</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><End><Type>CutScene</Type><Asset>RS_DATA/PfGrpFTUE2020T11CS.unity3d/PfGrpFTUE2020T11CS</Asset><ItemID>0</ItemID><Priority>0</Priority></End><End><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWAstrid</NPC><Text>There you are! Glad to see you're safe. @@Oh? I don’t think we’ve met, have we?@@{{Name}}? It's great to meet you, I’m Astrid. Seems that Hiccup's gotten himself in over his head again, huh? @@He's lucky he has good people like you around to help him out. And that’s one incredible dragon you’ve found, wow!</Text><ID>941114</ID><ItemID>0</ItemID><Priority>1</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Name</Key><Value>PfCollectFTUELeaf</Value></Pair><Pair><Key>Quantity</Key><Value>5</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Collect</Type><Title><Text>Collect Green Leaves</Text><ID>942396</ID></Title><Desc><Text>Collect Green Leaves</Text><ID>942396</ID></Desc></Data>
+ 0
+
+
+ 5941
+ FTUE2020-12: Mount
+ <Data><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>{{Name}} bonded with {{dragon name}} right away, you should’ve seen it, Astrid. I bet they’ll be flying together in no time. Speaking of which...@@{{Name}} let’s do a quick flying lesson. I’ll show you the ropes!@@First, {{input}} on the [c][3eebff]Mount[/c][ffffff] button to climb aboard your dragon.</Text><ID>939835</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><Objective><Pair><Key>Name</Key><Value>MountDragon</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Action</Type><Title><Text>Mount {{dragon name}}</Text><ID>939834</ID></Title><Desc><Text>Mount {{dragon name}}</Text><ID>939834</ID></Desc><AutoComplete><Pair><Key>Mounted</Key><Value>true</Value></Pair></AutoComplete></Data>
+ 0
+
+
+ 5942
+ FTUE2020-13: Fly through rings
+ <Data><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>Alright! {{dragon name}} is ready to go. Now, you just have to learn how to fly! Why don't you get used to the controls and soar through these rings?@@{{Input}} on the [c][3eebff]Jump[/c][ffffff] button [c][3eebff]twice[/c][ffffff] to get up into the air and we can go from there.</Text><ID>939837</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><End><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>You're a natural!</Text><ID>905008</ID><ItemID>0</ItemID><Priority>0</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubFTUEDO</Value></Pair><Pair><Key>Location</Key><Value>PfDWToothlessAlphaGuide</Value></Pair><Pair><Key>Name</Key><Value>PfCollectRing</Value></Pair><Pair><Key>Quantity</Key><Value>5</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Collect</Type><Title><Text>Fly through the Rings</Text><ID>938870</ID></Title><Desc><Text>Fly through the Rings</Text><ID>938870</ID></Desc></Data>
+ 0
+
+
+ 5944
+ FTUE2020-15: Deliver Headmaster
+ <Data><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHiccup</NPC><Text>Welcome to the School of Dragons! I have a feeling you'll fit right in.@@Can you give the box of dragon eggs that you found to the Headmaster of the School? You can't miss him – he's the stout one with all the hair!</Text><ID>939840</ID><ItemID>0</ItemID><Priority>1</Priority></Offer><Offer><Type>CutScene</Type><Asset>RS_DATA/PfGrpFTUE2020T14CS.unity3d/PfGrpFTUE2020T14CS</Asset><ItemID>0</ItemID><Priority>0</Priority></Offer><End><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHeadmaster</NPC><Text>You come highly recommended from the Chieftain of Berk himself. Welcome, {{Name}}. So, are you ready to learn how to become the Ultimate Dragon Trainer?</Text><ID>939841</ID><ItemID>0</ItemID><Priority>0</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubSchoolDO</Value></Pair><Pair><Key>NPC</Key><Value>PfDWHeadmaster</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Meet</Type><Title><Text>Deliver the Dragon Eggs to the Headmaster</Text><ID>942397</ID></Title><Desc><Text>Deliver the Dragon Eggs to the Headmaster</Text><ID>942397</ID></Desc></Data>
+ 0
+
+
+ 6619
+ FTUE2020-16: Explain Branch Quest
+ <Data><Offer><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHeadmaster</NPC><Text>Now {{Name}}, you can learn about the many things we have to offer here at the School through our [c][3eebff]Branch Quests[/c][ffffff]</Text><ID>941799</ID><ItemID>0</ItemID><Priority>0</Priority></Offer><End><Type>Popup</Type><Asset>PfUiMissionActionDBDO</Asset><NPC>PfDWHeadmaster</NPC><Text>Feel free to also explore the campus, you might even encounter other students. @@We are happy to have you here with us!</Text><ID>941800</ID><ItemID>0</ItemID><Priority>0</Priority></End><Objective><Pair><Key>Scene</Key><Value>HubSchoolDO</Value></Pair><Pair><Key>Name</Key><Value>CompleteTutorial</Value></Pair><Pair><Key>ItemName</Key><Value>BranchUiTutorial</Value></Pair><Pair><Key>Time</Key><Value>0</Value></Pair><Pair><Key>HideArrow</Key><Value>False</Value></Pair></Objective><Type>Action</Type><Title><Text>Check out the Branch Quests</Text><ID>941798</ID></Title><Desc><Text>Check out the Branch Quests</Text><ID>941798</ID></Desc></Data>
+ 0
+
+
+ 5
+ 1
+
+ 1
+ 3
+ 0
+ 201320
+ true
+ 5
+ 5
+
+ 0
+
+ 0
+ 0
+
+
+ 100
+ 2
+
+ 1
+ 23
+ 0
+ 201320
+ true
+ 100
+ 100
+
+ 0
+
+ 0
+ 0
+
+
+ 50
+ 8
+
+ 1
+ 609
+ 0
+ 201320
+ true
+ 50
+ 50
+
+ 0
+
+ 0
+ 0
+
+
+ 50
+ 12
+
+ 1
+ 913
+ 0
+ 201320
+ true
+ 50
+ 50
+
+ 0
+
+ 0
+ 0
+
+
\ No newline at end of file
diff --git a/src/Schema/Mission.cs b/src/Schema/Mission.cs
new file mode 100644
index 0000000..66c5969
--- /dev/null
+++ b/src/Schema/Mission.cs
@@ -0,0 +1,57 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "Mission", Namespace = "")]
+public class Mission {
+ [XmlElement(ElementName = "I")]
+ public int MissionID;
+
+ [XmlElement(ElementName = "N")]
+ public string Name;
+
+ [XmlElement(ElementName = "G")]
+ public int GroupID;
+
+ [XmlElement(ElementName = "P", IsNullable = true)]
+ public int? ParentID;
+
+ [XmlElement(ElementName = "S")]
+ public string Static;
+
+ [XmlElement(ElementName = "A")]
+ public bool Accepted;
+
+ [XmlElement(ElementName = "C")]
+ public int Completed;
+
+ [XmlElement(ElementName = "R")]
+ public string Rule;
+
+ [XmlElement(ElementName = "MR")]
+ public MissionRule MissionRule;
+
+ [XmlElement(ElementName = "V")]
+ public int VersionID;
+
+ [XmlElement(ElementName = "AID")]
+ public int AchievementID;
+
+ [XmlElement(ElementName = "AAID")]
+ public int AcceptanceAchievementID;
+
+ [XmlElement(ElementName = "M")]
+ public List Missions;
+
+ [XmlElement(ElementName = "Task")]
+ public List Tasks;
+
+ [XmlElement(ElementName = "AR")]
+ public List Rewards;
+
+ [XmlElement(ElementName = "AAR")]
+ public List AcceptanceRewards;
+
+ [XmlElement(ElementName = "RPT")]
+ public bool Repeatable;
+}
diff --git a/src/Schema/MissionCompletedResult.cs b/src/Schema/MissionCompletedResult.cs
new file mode 100644
index 0000000..56fbd72
--- /dev/null
+++ b/src/Schema/MissionCompletedResult.cs
@@ -0,0 +1,13 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "MissionCompletedResult", Namespace = "")]
+[Serializable]
+public class MissionCompletedResult {
+ [XmlElement(ElementName = "M")]
+ public int MissionID;
+
+ [XmlElement(ElementName = "A")]
+ public AchievementReward[] Rewards;
+}
diff --git a/src/Schema/MissionCriteria.cs b/src/Schema/MissionCriteria.cs
new file mode 100644
index 0000000..5ff9d2d
--- /dev/null
+++ b/src/Schema/MissionCriteria.cs
@@ -0,0 +1,22 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "MissionCriteria", Namespace = "")]
+[Serializable]
+public class MissionCriteria {
+ [XmlElement(ElementName = "Type")]
+ public string Type;
+
+ [XmlElement(ElementName = "Ordered")]
+ public bool Ordered;
+
+ [XmlElement(ElementName = "Min")]
+ public int Min;
+
+ [XmlElement(ElementName = "Repeat")]
+ public int Repeat;
+
+ [XmlElement(ElementName = "RuleItems")]
+ public List RuleItems;
+}
diff --git a/src/Schema/MissionGroup.cs b/src/Schema/MissionGroup.cs
new file mode 100644
index 0000000..f2728c3
--- /dev/null
+++ b/src/Schema/MissionGroup.cs
@@ -0,0 +1,25 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "MGF", Namespace = "")]
+[Serializable]
+public class MissionGroup {
+ [XmlElement(ElementName = "P")]
+ public int ProductID;
+
+ [XmlElement(ElementName = "MG")]
+ public int MissionGroupID;
+
+ [XmlElement(ElementName = "DMC")]
+ public int DailyMissionCount;
+
+ [XmlElement(ElementName = "CC")]
+ public int CompletionCount;
+
+ [XmlElement(ElementName = "D")]
+ public int Day;
+
+ [XmlElement(ElementName = "RC")]
+ public int RewardCycle;
+}
diff --git a/src/Schema/MissionRule.cs b/src/Schema/MissionRule.cs
new file mode 100644
index 0000000..2a1877f
--- /dev/null
+++ b/src/Schema/MissionRule.cs
@@ -0,0 +1,13 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "MissionRule", Namespace = "")]
+[Serializable]
+public class MissionRule {
+ [XmlElement(ElementName = "Prerequisites")]
+ public List Prerequisites;
+
+ [XmlElement(ElementName = "Criteria")]
+ public MissionCriteria Criteria;
+}
diff --git a/src/Schema/PrerequisiteItem.cs b/src/Schema/PrerequisiteItem.cs
new file mode 100644
index 0000000..69cb33d
--- /dev/null
+++ b/src/Schema/PrerequisiteItem.cs
@@ -0,0 +1,19 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "PrerequisiteItem", Namespace = "")]
+[Serializable]
+public class PrerequisiteItem {
+ [XmlElement(ElementName = "Type")]
+ public PrerequisiteRequiredType Type;
+
+ [XmlElement(ElementName = "Value")]
+ public string Value;
+
+ [XmlElement(ElementName = "Quantity")]
+ public short Quantity;
+
+ [XmlElement(ElementName = "ClientRule")]
+ public bool ClientRule;
+}
diff --git a/src/Schema/PrerequisiteRequiredType.cs b/src/Schema/PrerequisiteRequiredType.cs
new file mode 100644
index 0000000..73fe6fb
--- /dev/null
+++ b/src/Schema/PrerequisiteRequiredType.cs
@@ -0,0 +1,20 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+public enum PrerequisiteRequiredType {
+ [XmlEnum("1")]
+ Member = 1,
+ [XmlEnum("2")]
+ Accept,
+ [XmlEnum("3")]
+ Mission,
+ [XmlEnum("4")]
+ Rank,
+ [XmlEnum("5")]
+ DateRange,
+ [XmlEnum("7")]
+ Item = 7,
+ [XmlEnum("8")]
+ Event
+}
diff --git a/src/Schema/RuleItem.cs b/src/Schema/RuleItem.cs
new file mode 100644
index 0000000..5c106d9
--- /dev/null
+++ b/src/Schema/RuleItem.cs
@@ -0,0 +1,19 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "RuleItem", Namespace = "")]
+[Serializable]
+public class RuleItem {
+ [XmlElement(ElementName = "Type")]
+ public RuleItemType Type;
+
+ [XmlElement(ElementName = "MissionID")]
+ public int MissionID;
+
+ [XmlElement(ElementName = "ID")]
+ public int ID;
+
+ [XmlElement(ElementName = "Complete")]
+ public int Complete;
+}
diff --git a/src/Schema/RuleItemType.cs b/src/Schema/RuleItemType.cs
new file mode 100644
index 0000000..45e9722
--- /dev/null
+++ b/src/Schema/RuleItemType.cs
@@ -0,0 +1,10 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+public enum RuleItemType {
+ [XmlEnum("1")]
+ Task = 1,
+ [XmlEnum("2")]
+ Mission
+}
diff --git a/src/Schema/SetTaskStateResult.cs b/src/Schema/SetTaskStateResult.cs
new file mode 100644
index 0000000..df7e993
--- /dev/null
+++ b/src/Schema/SetTaskStateResult.cs
@@ -0,0 +1,23 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "SetTaskStateResult", Namespace = "")]
+[Serializable]
+public class SetTaskStateResult {
+ [XmlElement(ElementName = "S")]
+ public bool Success;
+
+ [XmlElement(ElementName = "T")]
+ public SetTaskStateStatus Status;
+
+ [XmlElement(ElementName = "A")]
+ public string AdditionalStatusParams;
+
+ [XmlElement(ElementName = "R")]
+ public MissionCompletedResult[] MissionsCompleted;
+
+ // TODO: Not needed now (requires many additional schemas)
+ // [XmlElement(ElementName = "C")]
+ // public CommonInventoryResponse CommonInvRes;
+}
diff --git a/src/Schema/SetTaskStateStatus.cs b/src/Schema/SetTaskStateStatus.cs
new file mode 100644
index 0000000..f4d5f47
--- /dev/null
+++ b/src/Schema/SetTaskStateStatus.cs
@@ -0,0 +1,38 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+public enum SetTaskStateStatus {
+ [XmlEnum("1")]
+ RequiresMembership = 1,
+ [XmlEnum("2")]
+ RequiresAcceptance,
+ [XmlEnum("3")]
+ NotWithinDateRange,
+ [XmlEnum("4")]
+ PreRequisiteMissionIncomplete,
+ [XmlEnum("5")]
+ UserRankLessThanMinRank,
+ [XmlEnum("6")]
+ UserRankGreaterThanMaxRank,
+ [XmlEnum("7")]
+ UserHasNoRankData,
+ [XmlEnum("8")]
+ MissionStateNotFound,
+ [XmlEnum("9")]
+ RequiredPriorTaskIncomplete,
+ [XmlEnum("10")]
+ ParentsTaskIncomplete,
+ [XmlEnum("11")]
+ ParentsSubMissionIncomplete,
+ [XmlEnum("12")]
+ TaskCanBeDone,
+ [XmlEnum("13")]
+ OneOrMoreMissionsHaveNoRewardsAttached,
+ [XmlEnum("14")]
+ PayLoadUpdated,
+ [XmlEnum("15")]
+ NonRepeatableMission,
+ [XmlEnum("255")]
+ Unknown = 255
+}
diff --git a/src/Schema/Task.cs b/src/Schema/Task.cs
new file mode 100644
index 0000000..1f66a0b
--- /dev/null
+++ b/src/Schema/Task.cs
@@ -0,0 +1,28 @@
+using System.Diagnostics;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "Task", Namespace = "")]
+[Serializable]
+public class Task {
+ [XmlElement(ElementName = "I")]
+ public int TaskID;
+
+ [XmlElement(ElementName = "N")]
+ public string Name;
+
+ [XmlElement(ElementName = "S")]
+ public string Static;
+
+ [XmlElement(ElementName = "C")]
+ public int Completed;
+
+ [XmlElement(ElementName = "F")]
+ public bool Failed;
+
+ [XmlElement(ElementName = "P")]
+ public string Payload;
+}
diff --git a/src/Schema/UserMissionStateResult.cs b/src/Schema/UserMissionStateResult.cs
new file mode 100644
index 0000000..13392ce
--- /dev/null
+++ b/src/Schema/UserMissionStateResult.cs
@@ -0,0 +1,23 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "UserMissionStateResult", Namespace = "")]
+[Serializable]
+public class UserMissionStateResult {
+ [XmlElement(ElementName = "UID")]
+ public Guid UserID;
+
+ [XmlElement(ElementName = "Mission")]
+ public List Missions;
+
+ [XmlElement(ElementName = "UTA")]
+ public List UserTimedAchievement;
+
+ // NOTE: It appears that the server doesn't send this
+ // [XmlElement(ElementName = "D")]
+ // public int Day;
+
+ [XmlElement(ElementName = "MG")]
+ public List MissionGroup;
+}
diff --git a/src/Schema/UserTimedAchievement.cs b/src/Schema/UserTimedAchievement.cs
new file mode 100644
index 0000000..cf742e3
--- /dev/null
+++ b/src/Schema/UserTimedAchievement.cs
@@ -0,0 +1,40 @@
+using System.Xml.Serialization;
+
+namespace sodoff.Schema;
+
+[XmlRoot(ElementName = "UTA", Namespace = "")]
+[Serializable]
+public class UserTimedAchievement {
+ [XmlElement(ElementName = "uid")]
+ public Guid? UserID;
+
+ [XmlElement(ElementName = "uta")]
+ public int UserTimedAchievementMapID;
+
+ [XmlElement(ElementName = "a")]
+ public int AchievementID;
+
+ [XmlElement(ElementName = "s")]
+ public int Sequence;
+
+ [XmlElement(ElementName = "st")]
+ public int StatusID;
+
+ [XmlElement(ElementName = "c")]
+ public DateTime CreatedDate;
+
+ [XmlElement(ElementName = "u", IsNullable = true)]
+ public DateTime? UpdatedDate;
+
+ [XmlElement(ElementName = "isdel")]
+ public bool IsDeleted;
+
+ [XmlElement(ElementName = "gid")]
+ public int GroupID;
+
+ [XmlElement(ElementName = "ar")]
+ public List AchievementReward;
+
+ [XmlElement(ElementName = "sid")]
+ public int SetID;
+}
diff --git a/src/Util/XmlUtil.cs b/src/Util/XmlUtil.cs
index e54ea73..ec31f93 100644
--- a/src/Util/XmlUtil.cs
+++ b/src/Util/XmlUtil.cs
@@ -1,4 +1,5 @@
-using System.Xml.Serialization;
+using System.Reflection;
+using System.Xml.Serialization;
namespace sodoff.Util;
public class XmlUtil {
@@ -15,4 +16,15 @@ public class XmlUtil {
return writer.ToString();
}
}
+
+ public static string ReadResourceXmlString(string name) {
+ string result = "";
+ var assembly = Assembly.GetExecutingAssembly();
+ string resourceName = assembly.GetManifestResourceNames().Single(str => str.EndsWith($"{name}.xml"));
+
+ using (Stream stream = assembly.GetManifestResourceStream(resourceName))
+ using (StreamReader reader = new StreamReader(stream))
+ result = reader.ReadToEnd();
+ return result;
+ }
}
diff --git a/src/sodoff.csproj b/src/sodoff.csproj
index 670ef0b..472717a 100644
--- a/src/sodoff.csproj
+++ b/src/sodoff.csproj
@@ -14,6 +14,7 @@
+
@@ -22,6 +23,9 @@
PreserveNewest
+
+ PreserveNewest
+
@@ -30,5 +34,8 @@
PreserveNewest
+
+ PreserveNewest
+