Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions GameDataParser/Hash/ms2-dungeon-metadata-hash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5f6ef7264d141fb7a8c85adfd789b797
1 change: 0 additions & 1 deletion GameDataParser/Hash/ms2-guild-exp-metadata-hash

This file was deleted.

2 changes: 1 addition & 1 deletion GameDataParser/Hash/ms2-map-entity-metadata-hash
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ab41df20625b069263bb43d83603fbd6
b61325024abdfe4afa1faed0da1d324b
2 changes: 1 addition & 1 deletion GameDataParser/Hash/ms2-skill-metadata-hash
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ddbcedd479cfa92a95606643b5f37f8d
79fd3ea113d04b67cd081ad29af3ada1
88 changes: 88 additions & 0 deletions GameDataParser/Parsers/DungeonParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Xml;
using GameDataParser.Crypto.Common;
using GameDataParser.Files;
using Maple2Storage.Types.Metadata;

namespace GameDataParser.Parsers
{
public class DungeonParser : Exporter<List<DungeonMetadata>>
{
public DungeonParser(MetadataResources resources) : base(resources, "dungeon") { }

protected override List<DungeonMetadata> Parse()
{
List<DungeonMetadata> dungeons = new List<DungeonMetadata>();
foreach (PackFileEntry entry in Resources.XmlFiles)
{
if (!entry.Name.StartsWith("table/na/dungeonroom"))
{
continue;
}

XmlDocument document = Resources.XmlMemFile.GetDocument(entry.FileHeader);
foreach (XmlNode dungeonNode in document.DocumentElement.ChildNodes)
{
DungeonMetadata metadata = new DungeonMetadata();

if (dungeonNode.Name == "dungeonRoom")
{
metadata.DungeonRoomId = int.Parse(dungeonNode.Attributes["dungeonRoomID"]?.Value);
if (dungeonNode.Attributes["limitPlayerLevel"] != null)
{
metadata.DungeonLevelRequirement = int.Parse(dungeonNode.Attributes["limitPlayerLevel"].Value);
}
if (dungeonNode.Attributes["groupType"] != null)
{
metadata.GroupType = dungeonNode.Attributes["groupType"].Value;
}
if (dungeonNode.Attributes["cooldownType"] != null)
{
metadata.CooldownType = dungeonNode.Attributes["cooldownType"]?.Value;
}
if (dungeonNode.Attributes["unionRewardID"] != null)
{
metadata.UnionRewardId = int.Parse(dungeonNode.Attributes["unionRewardID"].Value);
}
if (dungeonNode.Attributes["rewardCount"] != null)
{
metadata.RewardCount = short.Parse(dungeonNode.Attributes["rewardCount"].Value);
}
if (dungeonNode.Attributes["rewardExp"] != null)
{
metadata.RewardExp = int.Parse(dungeonNode.Attributes["rewardExp"].Value);
}
if (dungeonNode.Attributes["rewardMeso"] != null)
{
metadata.RewardMeso = int.Parse(dungeonNode.Attributes["rewardMeso"].Value);
}
if (dungeonNode.Attributes["lobbyFieldID"] != null)
{
metadata.LobbyFieldId = int.Parse(dungeonNode.Attributes["lobbyFieldID"].Value);
}
if (dungeonNode.Attributes["fieldIDs"] != null)
{
int[] fieldIDsList = Array.ConvertAll(dungeonNode.Attributes["fieldIDs"].Value.Split(","), int.Parse);
foreach (int fieldId in fieldIDsList)
{
metadata.FieldIds.Add(fieldId);
}
}
if (dungeonNode.Attributes["maxUserCount"] != null)
{
metadata.MaxUserCount = byte.Parse(dungeonNode.Attributes["maxUserCount"].Value);
}
if (dungeonNode.Attributes["limitPlayerLevel"] != null)
{
metadata.LimitPlayerLevel = byte.Parse(dungeonNode.Attributes["limitPlayerLevel"].Value);
}
}

dungeons.Add(metadata);
}
}
return dungeons;
}
}
}
12 changes: 10 additions & 2 deletions GameDataParser/Parsers/MapEntityParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ protected override List<MapEntityMetadata> Parse()
* }
*/
Dictionary<string, Dictionary<string, string>> mapObjects = new Dictionary<string, Dictionary<string, string>>();
List<string> portalNames = new List<string> { "MS2RoomEnterPortal", "MS2RoomLeavePortal", "MS2TriggerPortal", "Portal_Type_A", "Portal_BossGate",
"Portal_BossGate_02", "Portal_cube", "Portal_entrance", "Portal_memberance_A", "Portal_shadowWorld", "Portal_UGC", "Portal_UGCspawn", "Portal_underworld", "Eff_portal_test_A_"};

foreach (PackFileEntry entry in Resources.ExportedFiles
.Where(entry => Regex.Match(entry.Name, @"^flat/presets/presets (common|object|npc)/").Success)
Expand Down Expand Up @@ -232,7 +234,7 @@ protected override List<MapEntityMetadata> Parse()

metadata.MobSpawns.Add(new MapMobSpawn(int.Parse(mobSpawnPointID), CoordS.Parse(mobPositionValue), mobNpcCount, mobNpcList, mobSpawnRadius, mobSpawnData));
}
else if (modelName == "Portal_entrance" || modelName == "Portal_cube" || modelName.Contains("Portal_memberance"))
else if (portalNames.Contains(modelName))
{
XmlNode portalIdNode = node.SelectSingleNode("property[@name='PortalID']");
XmlNode targetFieldNode = node.SelectSingleNode("property[@name='TargetFieldSN']");
Expand All @@ -242,6 +244,7 @@ protected override List<MapEntityMetadata> Parse()
continue;
}

XmlNode portalTypeNode = node.SelectSingleNode("property[@name='PortalType']");
XmlNode visibleNode = node.SelectSingleNode("property[@name='IsVisible']");
XmlNode enabledNode = node.SelectSingleNode("property[@name='PortalEnable']");
XmlNode minimapVisibleNode = node.SelectSingleNode("property[@name='MinimapIconVisible']");
Expand Down Expand Up @@ -272,6 +275,11 @@ protected override List<MapEntityMetadata> Parse()
{
targetPortalId = int.Parse(targetIdNode?.FirstChild.Attributes["value"].Value);
}
byte portalType = 0;
if (portalTypeNode != null)
{
portalType = byte.Parse(portalTypeNode?.FirstChild.Attributes["value"].Value);
}
string positionValue = coordNode?.FirstChild.Attributes["value"].Value ?? "0, 0, 0";
string rotationValue = rotationNode?.FirstChild.Attributes["value"].Value ?? "0, 0, 0";

Expand All @@ -281,7 +289,7 @@ protected override List<MapEntityMetadata> Parse()

CoordS position = CoordS.Parse(positionValue);
CoordS rotation = CoordS.Parse(rotationValue);
metadata.Portals.Add(new MapPortal(portalId, modelName, flags, target, position, rotation, targetPortalId));
metadata.Portals.Add(new MapPortal(portalId, modelName, flags, target, position, rotation, targetPortalId, portalType));
}
else if (modelName == "SpawnPointNPC" && !name.StartsWith("SpawnPointNPC"))
{
Expand Down
27 changes: 26 additions & 1 deletion GameDataParser/Parsers/SkillParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ protected override List<SkillMetadata> Parse()
string skillState = kinds.Attributes["state"]?.Value ?? "";
byte skillAttackType = byte.Parse(ui.Attributes["attackType"]?.Value ?? "0");
byte skillType = byte.Parse(kinds.Attributes["type"].Value);
byte skillSubType = byte.Parse(kinds.Attributes["subType"]?.Value ?? "0");
byte skillElement = byte.Parse(kinds.Attributes["element"].Value);
byte skillSuperArmor = byte.Parse(stateAttr.Attributes["superArmor"].Value);
bool skillRecovery = int.Parse(kinds.Attributes["spRecoverySkill"]?.Value ?? "0") == 1;
Expand All @@ -54,7 +55,7 @@ protected override List<SkillMetadata> Parse()
skillLevels.Add(new SkillLevel(levelValue, spirit, stamina, damageRate, feature, skillMotion));
}

skillList.Add(new SkillMetadata(skillId, skillLevels, skillState, skillAttackType, skillType, skillElement, skillSuperArmor, skillRecovery, skillBuff));
skillList.Add(new SkillMetadata(skillId, skillLevels, skillState, skillAttackType, skillType, skillSubType, skillElement, skillSuperArmor, skillRecovery, skillBuff));
}
// Parsing SubSkills
else if (entry.Name.StartsWith("table/job"))
Expand Down Expand Up @@ -115,6 +116,30 @@ protected override List<SkillMetadata> Parse()
}
}
}
// Parsing Additional Data
foreach (PackFileEntry entry in Resources.XmlFiles)
{
if (!entry.Name.StartsWith("additionaleffect"))
{
continue;
}
XmlDocument document = Resources.XmlMemFile.GetDocument(entry.FileHeader);
XmlNodeList levels = document.SelectNodes("/ms2/level");

int skillId = int.Parse(Path.GetFileNameWithoutExtension(entry.Name));
if (skillList.Select(x => x.SkillId).Contains(skillId))
{
foreach (XmlNode level in levels)
{
int duration = int.Parse(level.SelectSingleNode("BasicProperty").Attributes["durationTick"]?.Value ?? "0");
int currentLevel = int.Parse(level.SelectSingleNode("BasicProperty").Attributes["level"]?.Value ?? "0");
if (skillList.Find(x => x.SkillId == skillId).SkillLevels.Select(x => x.Level).Contains(currentLevel))
{
skillList.Find(x => x.SkillId == skillId).SkillLevels.Find(x => x.Level == currentLevel).SkillAdditionalData = new SkillAdditionalData(duration);
}
}
}
}
return skillList;
}
}
Expand Down
1 change: 1 addition & 0 deletions GameDataParser/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ static async Task Main()
IEnumerable<MetadataExporter> exporters = new List<MetadataExporter>()
{
new AnimationParser(resources),
new DungeonParser(resources),
new ItemParser(resources),
new ItemOptionConstantParser(resources),
new ItemOptionStaticParser(resources),
Expand Down
28 changes: 28 additions & 0 deletions Maple2Storage/Tools/RandomProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Threading;

namespace Maple2Storage.Tools
{
/// <summary>
/// Thread-safe provider for "Random" instances. Use whenever no custom
/// seed is required.
/// </summary>
public static class RandomProvider
{
private static readonly Random _seed = new Random();

private static readonly ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() =>
{
lock (_seed)
{
return new Random(_seed.Next());
}
});

/// <summary>
/// Returns an instance of Random for the calling thread.
/// </summary>
/// <returns></returns>
public static Random Get() => randomWrapper.Value;
}
}
34 changes: 34 additions & 0 deletions Maple2Storage/Types/DungeonMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Xml.Serialization;

namespace Maple2Storage.Types.Metadata
{
[XmlType]
public class DungeonMetadata
{
[XmlElement(Order = 1)]
public int DungeonRoomId;
[XmlElement(Order = 2)]
public int DungeonLevelRequirement;
[XmlElement(Order = 3)]
public string GroupType;
[XmlElement(Order = 4)]
public string CooldownType;
[XmlElement(Order = 5)]
public int UnionRewardId;
[XmlElement(Order = 6)]
public short RewardCount;
[XmlElement(Order = 7)]
public int RewardExp;
[XmlElement(Order = 8)]
public int RewardMeso;
[XmlElement(Order = 9)]
public int LobbyFieldId;
[XmlElement(Order = 10)]
public List<int> FieldIds = new List<int>();
[XmlElement(Order = 11)]
public byte MaxUserCount;
[XmlElement(Order = 12)]
public byte LimitPlayerLevel;
}
}
10 changes: 7 additions & 3 deletions Maple2Storage/Types/Metadata/MapEntityMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,13 @@ public class MapPortal
public readonly CoordS Rotation;
[XmlElement(Order = 7)]
public readonly int TargetPortalId;
[XmlElement(Order = 8)]
public readonly byte PortalType;

// Required for deserialization
public MapPortal() { }

public MapPortal(int id, string name, MapPortalFlag flags, int target, CoordS coord, CoordS rotation, int targetPortalId)
public MapPortal(int id, string name, MapPortalFlag flags, int target, CoordS coord, CoordS rotation, int targetPortalId, byte portalType)
{
Id = id;
Name = name;
Expand All @@ -274,10 +276,11 @@ public MapPortal(int id, string name, MapPortalFlag flags, int target, CoordS co
Coord = coord;
Rotation = rotation;
TargetPortalId = targetPortalId;
PortalType = portalType;
}

public override string ToString() =>
$"MapPortal(Id:{Id},String:{Name},Flags:{Flags},Target:{Target},Rotation:{Rotation},Coord:{Coord},TargetPortalId:{TargetPortalId})";
$"MapPortal(Id:{Id},String:{Name},Flags:{Flags},Target:{Target},Rotation:{Rotation},Coord:{Coord},TargetPortalId:{TargetPortalId}, PortalType:{PortalType})";

protected bool Equals(MapPortal other)
{
Expand All @@ -287,7 +290,8 @@ protected bool Equals(MapPortal other)
&& Target == other.Target
&& Coord.Equals(other.Coord)
&& Rotation.Equals(other.Rotation)
&& TargetPortalId == other.TargetPortalId;
&& TargetPortalId == other.TargetPortalId
&& PortalType == other.PortalType;
}

public override bool Equals(object obj)
Expand Down
34 changes: 28 additions & 6 deletions Maple2Storage/Types/Metadata/SkillMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,33 +25,36 @@ public class SkillMetadata
[XmlElement(Order = 8)]
public readonly byte Type;
[XmlElement(Order = 9)]
public readonly byte Element;
public readonly byte SubType;
[XmlElement(Order = 10)]
public readonly byte SuperArmor;
public readonly byte Element;
[XmlElement(Order = 11)]
public readonly bool IsSpRecovery;
public readonly byte SuperArmor;
[XmlElement(Order = 12)]
public readonly bool IsSpRecovery;
[XmlElement(Order = 13)]
public readonly bool IsBuff;

public SkillMetadata()
{
SkillLevels = new List<SkillLevel>();
}
public SkillMetadata(int id, List<SkillLevel> skillLevels, string state, byte damageType, byte type, byte element, byte superArmor, bool isSpRecovery, bool isBuff)
public SkillMetadata(int id, List<SkillLevel> skillLevels, string state, byte damageType, byte type, byte subType, byte element, byte superArmor, bool isSpRecovery, bool isBuff)
{
SkillId = id;
SkillLevels = skillLevels;
State = state;
DamageType = damageType;
Type = type;
SubType = subType;
Element = element;
SuperArmor = superArmor;
IsSpRecovery = isSpRecovery;
IsBuff = isBuff;
}

public SkillMetadata(int id, List<SkillLevel> skillLevels, int[] subSkills, int job, string state, byte damageType, byte type, byte element, byte superArmor, bool isSpRecovery, bool isBuff)
: this(id, skillLevels, state, damageType, type, element, superArmor, isSpRecovery, isBuff)
public SkillMetadata(int id, List<SkillLevel> skillLevels, int[] subSkills, int job, string state, byte damageType, byte type, byte subType, byte element, byte superArmor, bool isSpRecovery, bool isBuff)
: this(id, skillLevels, state, damageType, type, subType, element, superArmor, isSpRecovery, isBuff)
{
SubSkills = subSkills;
Job = job;
Expand Down Expand Up @@ -81,6 +84,8 @@ public class SkillLevel
public readonly string Feature = "";
[XmlElement(Order = 6)]
public readonly SkillMotion SkillMotions;
[XmlElement(Order = 7)]
public SkillAdditionalData SkillAdditionalData;

// Required for deserialization
public SkillLevel() { }
Expand All @@ -93,6 +98,7 @@ public SkillLevel(int level, int spirit, int stamina, float damageRate, string f
DamageRate = damageRate;
Feature = feature;
SkillMotions = skillMotions;
SkillAdditionalData = new SkillAdditionalData();
}

public override int GetHashCode()
Expand Down Expand Up @@ -131,4 +137,20 @@ public override int GetHashCode()
public override string ToString() => $"SequenceName:{SequenceName},MotionEffect:{MotionEffect}";

}

[XmlType] // TODO: More to implement, like skill sequences, stats power up, additional MotionEffects...
public class SkillAdditionalData
{
[XmlElement(Order = 1)]
public int Duration;

public SkillAdditionalData() { }

public SkillAdditionalData(int duration)
{
Duration = duration;
}

public override string ToString() => $"DurationTick: {Duration}";
}
}
Loading