From 2434da44cb8a7ba0ea46dbcbb39ee72e386cf4f2 Mon Sep 17 00:00:00 2001
From: soul4soul <5142635+soul4soul@users.noreply.github.com>
Date: Tue, 25 May 2021 11:15:21 -0400
Subject: [PATCH] Fix many issues parsing and writing TFS monster abilities
(#18)
These changes fix issues #6, #8, and #14 (both the melee issue and combat damage type issue)
Other improvements beyond the reported issues
TFS XML: Fixed parsing of condition causing spells, melee attack name, duration, animations, effects, and area information for all spells
TFS RevScriptSys: Writing condition causing spells, duration, area information, outfit changing spells, animations, and effects for all, and combat type
TFSXML->TFS RevScriptSys: Spells from files now in some cases but needs more testing and checking overall
---
README.md | 20 +-
.../Enums/Animation.cs | 61 ++
app/MonsterConverterInterface/Enums/Blood.cs | 15 +
.../Enums/ChangeAppearanceType.cs | 13 +
.../Enums/CombatDamage.cs | 21 +
.../Enums/ConditionType.cs | 39 ++
app/MonsterConverterInterface/Enums/Effect.cs | 99 +++
.../Enums/SoundLevel.cs | 13 +
.../{MonsterTypes => Enums}/SpellCategory.cs | 0
.../MonsterTypes/EnumTypes.cs | 225 -------
.../MonsterTypes/Spell.cs | 25 +-
.../TfsRevScriptSysConverter.cs | 230 +++----
.../SerializerClasses.cs | 373 ++++++++++++
app/MonsterConverterTfsXml/TfsXmlConverter.cs | 572 ++++--------------
.../TibiaWikiConverter.cs | 11 +-
15 files changed, 890 insertions(+), 827 deletions(-)
create mode 100644 app/MonsterConverterInterface/Enums/Animation.cs
create mode 100644 app/MonsterConverterInterface/Enums/Blood.cs
create mode 100644 app/MonsterConverterInterface/Enums/ChangeAppearanceType.cs
create mode 100644 app/MonsterConverterInterface/Enums/CombatDamage.cs
create mode 100644 app/MonsterConverterInterface/Enums/ConditionType.cs
create mode 100644 app/MonsterConverterInterface/Enums/Effect.cs
create mode 100644 app/MonsterConverterInterface/Enums/SoundLevel.cs
rename app/MonsterConverterInterface/{MonsterTypes => Enums}/SpellCategory.cs (100%)
delete mode 100644 app/MonsterConverterInterface/MonsterTypes/EnumTypes.cs
create mode 100644 app/MonsterConverterTfsXml/SerializerClasses.cs
diff --git a/README.md b/README.md
index 05e5df79..27356741 100644
--- a/README.md
+++ b/README.md
@@ -8,18 +8,14 @@ Tibia OT Monster Converter is a tool for converting monster files between the va
- WPF
- Visual Studio 2019
-## Status
-
-Parsing and writing of nested loot is not yet supported by any formats. This is a low priority item as cipbia removed nested loot and its no longer commonly found in OT servers.
-
-### Supported Formats
-
-| Format | Input % Complete | Output % Complete | Notes |
-| ---------------------------------------------------- | ---------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| TFS XML | [95%](https://github.com/soul4soul/ot-monster-converter/wiki/TFS-XML-Input-Status) | [0%](https://github.com/soul4soul/ot-monster-converter/wiki/TFS-XML-Output-Status) | - Most common OT Monster format which has been around for over a decade |
-| [PyOT](https://bitbucket.org/vapus/pyot/) | [0%](https://github.com/soul4soul/ot-monster-converter/wiki/PyOT-Input-Status) | [90%](https://github.com/soul4soul/ot-monster-converter/wiki/PyOT-Output-Status) | - This format can be consider dead as PyOT development has ceased. Unless development is picked back up support for this format is unlikely to be completed. |
-| TFS revscriptsys | [0%](https://github.com/soul4soul/ot-monster-converter/wiki/TFS-revscriptsys-Input-Status) | [95%](https://github.com/soul4soul/ot-monster-converter/wiki/TFS-revscriptsys-Output-Status) | - Very new OT monster format that was theorized many years ago. In the future there is a good chance it will replace TFS XML completely. This is likely the output type that most users of this program will use. |
-| [TibiaWiki](https://tibia.fandom.com/wiki/Main_Page) | [80%](https://github.com/soul4soul/ot-monster-converter/wiki/TibiaWiki-Input-Status) | [60%](https://github.com/soul4soul/ot-monster-converter/wiki/TibiaWiki-Output-Status) | - Helpful for keeping monsters up to date with cipbia
- See The [Infobox Creature Template](https://tibia.fandom.com/wiki/Template:Infobox_Creature) for information about TibiaWiki Format
- Monsters created from TibiaWiki will require corpse id, looktype, and spells to be created manually |
+## Supported Formats
+
+| Format | Input % Complete | Output % Complete | Notes |
+| - | - | - | - |
+| TFS XML | [95%](https://github.com/soul4soul/ot-monster-converter/wiki/TFS-XML-Input-Status) | [0%](https://github.com/soul4soul/ot-monster-converter/wiki/TFS-XML-Output-Status) | - Most common OT Monster format which has been around for over a decade |
+| [PyOT](https://bitbucket.org/vapus/pyot/) | [0%](https://github.com/soul4soul/ot-monster-converter/wiki/PyOT-Input-Status) | [90%](https://github.com/soul4soul/ot-monster-converter/wiki/PyOT-Output-Status) | - This format can be consider dead as PyOT development has ceased. Unless development is picked back up support for this format is unlikely to be completed. |
+| TFS revscriptsys | [0%](https://github.com/soul4soul/ot-monster-converter/wiki/TFS-revscriptsys-Input-Status) | [95%](https://github.com/soul4soul/ot-monster-converter/wiki/TFS-revscriptsys-Output-Status) | - Very new OT monster format that was theorized many years ago. In the future there is a good chance it will replace TFS XML completely. This is likely the output type that most users of this program will use.
- Opentibiabr RevScriptSys format is not completely compatible with TFS RevScriptSys format |
+| [TibiaWiki](https://tibia.fandom.com/wiki/Main_Page) | [80%](https://github.com/soul4soul/ot-monster-converter/wiki/TibiaWiki-Input-Status) | [60%](https://github.com/soul4soul/ot-monster-converter/wiki/TibiaWiki-Output-Status) | - Helpful for keeping monsters up to date with cipbia
- See The [Infobox Creature Template](https://tibia.fandom.com/wiki/Template:Infobox_Creature) for information about TibiaWiki Format
- Monsters created from TibiaWiki will require corpse id, looktype, and spells to be created manually |
## Graphical Interface
diff --git a/app/MonsterConverterInterface/Enums/Animation.cs b/app/MonsterConverterInterface/Enums/Animation.cs
new file mode 100644
index 00000000..ceb47983
--- /dev/null
+++ b/app/MonsterConverterInterface/Enums/Animation.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MonsterConverterInterface.MonsterTypes
+{
+ public enum Animation
+ {
+ None = 0,
+ Spear,
+ Bolt,
+ Arrow,
+ Fire,
+ Energy,
+ PoisonArrow,
+ BurstArrow,
+ ThrowingStar,
+ ThrowingKnife,
+ SmallStone,
+ Death,
+ LargeRock,
+ Snowball,
+ PowerBolt,
+ Poison,
+ InfernalBolt,
+ HuntingSpear,
+ EnchantedSpear,
+ RedStar,
+ GreenStar,
+ RoyalSpear,
+ SniperArrow,
+ OnyxArrow,
+ PiercingBolt,
+ WhirlwindSword,
+ WhirlwindAxe,
+ WhirlwindClub,
+ EtherealSpear,
+ Ice,
+ Earth,
+ Holy,
+ SuddenDeath,
+ FlashArrow,
+ FlammingArrow,
+ ShiverArrow,
+ EnergyBall,
+ SmallIce,
+ SmallHoly,
+ SmallEarth,
+ EarthArrow,
+ Explosion,
+ Cake,
+ TarsalArrow = 44,
+ VortexBolt = 45,
+ PrismaticBolt,
+ CrystallineArrow,
+ DrillBolt,
+ EnvenomedArrow,
+ GloothSpear,
+ SimpleArrow
+ }
+}
diff --git a/app/MonsterConverterInterface/Enums/Blood.cs b/app/MonsterConverterInterface/Enums/Blood.cs
new file mode 100644
index 00000000..a874de18
--- /dev/null
+++ b/app/MonsterConverterInterface/Enums/Blood.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MonsterConverterInterface.MonsterTypes
+{
+ public enum Blood
+ {
+ blood,
+ venom,
+ undead,
+ fire,
+ energy
+ }
+}
diff --git a/app/MonsterConverterInterface/Enums/ChangeAppearanceType.cs b/app/MonsterConverterInterface/Enums/ChangeAppearanceType.cs
new file mode 100644
index 00000000..9acf900d
--- /dev/null
+++ b/app/MonsterConverterInterface/Enums/ChangeAppearanceType.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MonsterConverterInterface.MonsterTypes
+{
+ public enum ChangeAppearanceType
+ {
+ MonsterName,
+ OutfitId,
+ Item
+ }
+}
diff --git a/app/MonsterConverterInterface/Enums/CombatDamage.cs b/app/MonsterConverterInterface/Enums/CombatDamage.cs
new file mode 100644
index 00000000..c2589d20
--- /dev/null
+++ b/app/MonsterConverterInterface/Enums/CombatDamage.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MonsterConverterInterface.MonsterTypes
+{
+ public enum CombatDamage
+ {
+ Physical,
+ Energy,
+ Earth,
+ Fire,
+ LifeDrain,
+ ManaDrain,
+ Healing,
+ Drown,
+ Ice,
+ Holy,
+ Death
+ }
+}
diff --git a/app/MonsterConverterInterface/Enums/ConditionType.cs b/app/MonsterConverterInterface/Enums/ConditionType.cs
new file mode 100644
index 00000000..32111ec3
--- /dev/null
+++ b/app/MonsterConverterInterface/Enums/ConditionType.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MonsterConverterInterface.MonsterTypes
+{
+ public enum ConditionType
+ {
+ None,
+ Poison,
+ Fire,
+ Energy,
+ Bleeding,
+ Haste,
+ Paralyze,
+ Outfit,
+ Invisible,
+ Light,
+ ManaShield,
+ InFight,
+ Drunk,
+ ExhaustWeapon, // unused
+ Regeneration,
+ Soul,
+ Drown,
+ Muted,
+ ChannelMutedTicks,
+ YellTicks,
+ Attributes,
+ Freezing,
+ Dazzled,
+ Cursed,
+ ExhaustCombat, // unused
+ Exhaust_heal, // unused
+ Pacified,
+ SpellCoolDown,
+ SpellGroupCoolDown
+ }
+}
diff --git a/app/MonsterConverterInterface/Enums/Effect.cs b/app/MonsterConverterInterface/Enums/Effect.cs
new file mode 100644
index 00000000..84d94230
--- /dev/null
+++ b/app/MonsterConverterInterface/Enums/Effect.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MonsterConverterInterface.MonsterTypes
+{
+ public enum Effect
+ {
+ None = 0,
+ DrawBlood,
+ LoseEnergy,
+ Poff,
+ BlockHit,
+ ExplosionArea,
+ ExplosionHit,
+ FireArea,
+ YellowRings,
+ GreenRings,
+ HitArea,
+ Teleport,
+ EnergyHit,
+ MagicBlue,
+ MagicRed,
+ MagicGreen,
+ HitByFire,
+ HitByPoison,
+ MortArea,
+ SoundGreen,
+ SoundRed,
+ PoisonArea,
+ SoundYellow,
+ SoundPurple,
+ SoundBlue,
+ SoundWhite,
+ Bubbles,
+ Craps,
+ GiftWraps,
+ FireworkYellow,
+ FireworkRed,
+ FireworkBlue,
+ Stun,
+ Sleep,
+ WaterCreature,
+ GroundShaker,
+ Hearts,
+ FireAttack,
+ EnergyArea,
+ SmallClouds,
+ HolyDamage,
+ BigClouds,
+ IceArea,
+ IceTornado,
+ IceAttack,
+ Stones,
+ SmallPlants,
+ Carniphila,
+ PurpleEnergy,
+ YellowEnergy,
+ HolyArea,
+ BigPlants,
+ Cake,
+ GiantIce,
+ WaterSplash,
+ PlantAttack,
+ TutorialArrow,
+ TutorialSquare,
+ MirrorHorizontal,
+ MirrorVertical,
+ SkullHorizontal,
+ SkullVertical,
+ Assassin,
+ StepsHorizontal,
+ BloodySteps,
+ StepsVertical,
+ YalahariGhost,
+ Bats,
+ Smoke,
+ Insects,
+ Dragonhead,
+ OrcShaman,
+ OrcShamanFire,
+ Thunder,
+ Ferumbras,
+ ConfettiHorizontal,
+ ConfettiVertical,
+ // 77-157 are empty
+ BlackSmoke = 158,
+ // 159-166 are empty
+ RedSmoke = 167,
+ YellowSmoke = 168,
+ GreenSmoke = 169,
+ PurpleSmoke = 170,
+ EarlyThunder = 171,
+ RagiazBoneCapsule = 172,
+ CriticalDamage = 173,
+ // 174 is empty
+ PlungingFish = 175
+ }
+}
diff --git a/app/MonsterConverterInterface/Enums/SoundLevel.cs b/app/MonsterConverterInterface/Enums/SoundLevel.cs
new file mode 100644
index 00000000..966ec124
--- /dev/null
+++ b/app/MonsterConverterInterface/Enums/SoundLevel.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MonsterConverterInterface.MonsterTypes
+{
+ public enum SoundLevel
+ {
+ Whisper,
+ Say,
+ Yell
+ }
+}
diff --git a/app/MonsterConverterInterface/MonsterTypes/SpellCategory.cs b/app/MonsterConverterInterface/Enums/SpellCategory.cs
similarity index 100%
rename from app/MonsterConverterInterface/MonsterTypes/SpellCategory.cs
rename to app/MonsterConverterInterface/Enums/SpellCategory.cs
diff --git a/app/MonsterConverterInterface/MonsterTypes/EnumTypes.cs b/app/MonsterConverterInterface/MonsterTypes/EnumTypes.cs
deleted file mode 100644
index 1a52b4aa..00000000
--- a/app/MonsterConverterInterface/MonsterTypes/EnumTypes.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace MonsterConverterInterface.MonsterTypes
-{
- public enum Condition
- {
- None,
- Poison,
- Fire,
- Energy,
- Bleeding,
- Haste,
- Paralyze,
- Outfit,
- Invisible,
- Light,
- ManaShield,
- InFight,
- Drunk,
- ExhaustWeapon, // unused
- Regeneration,
- Soul,
- Drown,
- Muted,
- ChannelMutedTicks,
- YellTicks,
- Attributes,
- Freezing,
- Dazzled,
- Cursed,
- ExhaustCombat, // unused
- Exhaust_heal, // unused
- Pacified,
- SpellCoolDown,
- SpellGroupCoolDown
- }
-
- public enum CombatDamage
- {
- Physical,
- Energy,
- Earth,
- Fire,
- LifeDrain,
- ManaDrain,
- Healing,
- Drown,
- Ice,
- Holy,
- Death
- }
-
- public enum TargetType
- {
- Direction,
- Self,
- Area
- }
-
- public enum Effect
- {
- None = 0,
- DrawBlood,
- LoseEnergy,
- Poff,
- BlockHit,
- ExplosionArea,
- ExplosionHit,
- FireArea,
- YellowRings,
- GreenRings,
- HitArea,
- Teleport,
- EnergyHit,
- MagicBlue,
- MagicRed,
- MagicGreen,
- HitByFire,
- HitByPoison,
- MortArea,
- SoundGreen,
- SoundRed,
- PoisonArea,
- SoundYellow,
- SoundPurple,
- SoundBlue,
- SoundWhite,
- Bubbles,
- Craps,
- GiftWraps,
- FireworkYellow,
- FireworkRed,
- FireworkBlue,
- Stun,
- Sleep,
- WaterCreature,
- GroundShaker,
- Hearts,
- FireAttack,
- EnergyArea,
- SmallClouds,
- HolyDamage,
- BigClouds,
- IceArea,
- IceTornado,
- IceAttack,
- Stones,
- SmallPlants,
- Carniphila,
- PurpleEnergy,
- YellowEnergy,
- HolyArea,
- BigPlants,
- Cake,
- GiantIce,
- WaterSplash,
- PlantAttack,
- TutorialArrow,
- TutorialSquare,
- MirrorHorizontal,
- MirrorVertical,
- SkullHorizontal,
- SkullVertical,
- Assassin,
- StepsHorizontal,
- BloodySteps,
- StepsVertical,
- YalahariGhost,
- Bats,
- Smoke,
- Insects,
- Dragonhead,
- OrcShaman,
- OrcShamanFire,
- Thunder,
- Ferumbras,
- ConfettiHorizontal,
- ConfettiVertical,
- // 77-157 are empty
- BlackSmoke = 158,
- // 159-166 are empty
- RedSmoke = 167,
- YellowSmoke = 168,
- GreenSmoke = 169,
- PurpleSmoke = 170,
- EarlyThunder = 171,
- RagiazBoneCapsule = 172,
- CriticalDamage = 173,
- // 174 is empty
- PlungingFish = 175
- }
-
- public enum Animation
- {
- None = 0,
- Spear,
- Bolt,
- Arrow,
- Fire,
- Energy,
- PoisonArrow,
- BurstArrow,
- ThrowingStar,
- ThrowingKnife,
- SmallStone,
- Death,
- LargeRock,
- Snowball,
- PowerBolt,
- Poison,
- InfernalBolt,
- HuntingSpear,
- EnchantedSpear,
- RedStar,
- GreenStar,
- RoyalSpear,
- SniperArrow,
- OnyxArrow,
- PiercingBolt,
- WhirlwindSword,
- WhirlwindAxe,
- WhirlwindClub,
- EtherealSpear,
- Ice,
- Earth,
- Holy,
- SuddenDeath,
- FlashArrow,
- FlammingArrow,
- ShiverArrow,
- EnergyBall,
- SmallIce,
- SmallHoly,
- SmallEarth,
- EarthArrow,
- Explosion,
- Cake,
- TarsalArrow = 44,
- VortexBolt = 45,
- PrismaticBolt,
- CrystallineArrow,
- DrillBolt,
- EnvenomedArrow,
- GloothSpear,
- SimpleArrow
- }
-
- public enum Blood
- {
- blood,
- venom,
- undead,
- fire,
- energy
- }
-
- public enum SoundLevel
- {
- Whisper,
- Say,
- Yell
- }
-}
diff --git a/app/MonsterConverterInterface/MonsterTypes/Spell.cs b/app/MonsterConverterInterface/MonsterTypes/Spell.cs
index 6bd1bf0d..6c31404a 100644
--- a/app/MonsterConverterInterface/MonsterTypes/Spell.cs
+++ b/app/MonsterConverterInterface/MonsterTypes/Spell.cs
@@ -10,29 +10,32 @@ public class Spell
public SpellCategory SpellCategory { get; set; }
public int? MinDamage { get; set; }
public int? MaxDamage { get; set; }
- public TargetType? TargetStyle { get; set; }
- public CombatDamage? DamageElement { get; set; }
- public Effect? AreaEffect { get; set; }
- public Animation? ShootEffect { get; set; }
- public uint? Chance { get; set; }
- public uint? Interval { get; set; }
+ public Effect AreaEffect { get; set; }
+ public Animation ShootEffect { get; set; }
+ public double Chance { get; set; }
+ public uint Interval { get; set; }
public uint? Range { get; set; }
public uint? Radius { get; set; }
public uint? Length { get; set; }
public uint? Spread { get; set; }
- public bool? Target { get; set; }
+ public bool? OnTarget { get; set; }
+ // Magic damage
+ public CombatDamage? DamageElement { get; set; }
// Speed
- public int? SpeedChange { get; set; }
+ public int? MinSpeedChange { get; set; }
+ public int? MaxSpeedChange { get; set; }
public int? Duration { get; set; }
- // Melee Stuff
+ // Melee damage overrides
public int? AttackValue { get; set; }
public int? Skill { get; set; }
- // Spell Condition stuff
+ // Spell Condition stuff (used for condition only + melee conditions)
public int? Tick { get; set; }
public int? StartDamage { get; set; }
- public Condition? Condition { get; set; }
+ public ConditionType Condition { get; set; }
// Outfit stuff
public string MonsterName { get; set; }
public int? ItemId { get; set; }
+ // Drunk
+ public double? Drunkenness { get; set; }
}
}
diff --git a/app/MonsterConverterTfsRevScriptSys/TfsRevScriptSysConverter.cs b/app/MonsterConverterTfsRevScriptSys/TfsRevScriptSysConverter.cs
index a1d0507c..520e8f55 100644
--- a/app/MonsterConverterTfsRevScriptSys/TfsRevScriptSysConverter.cs
+++ b/app/MonsterConverterTfsRevScriptSys/TfsRevScriptSysConverter.cs
@@ -15,45 +15,45 @@ public class TfsRevScriptSysConverter : MonsterConverter
const uint MAX_LOOTCHANCE = 100000;
- IDictionary ConditioToTFsConstants = new Dictionary
+ IDictionary ConditionToTfsConstant = new Dictionary
{
- {Condition.Poison, "CONDITION_POISON"},
- {Condition.Fire, "CONDITION_FIRE"},
- {Condition.Energy, "CONDITION_ENERGY"},
- {Condition.Bleeding, "CONDITION_BLEEDING"},
- {Condition.Paralyze, "CONDITION_POISON"},
- {Condition.Drown, "CONDITION_DROWN"},
- {Condition.Freezing, "CONDITION_FREEZING"},
- {Condition.Dazzled, "CONDITION_DAZZLED"},
- {Condition.Cursed, "CONDITION_CURSED"}
+ {ConditionType.Poison, "CONDITION_POISON"},
+ {ConditionType.Fire, "CONDITION_FIRE"},
+ {ConditionType.Energy, "CONDITION_ENERGY"},
+ {ConditionType.Bleeding, "CONDITION_BLEEDING"},
+ {ConditionType.Paralyze, "CONDITION_POISON"},
+ {ConditionType.Drown, "CONDITION_DROWN"},
+ {ConditionType.Freezing, "CONDITION_FREEZING"},
+ {ConditionType.Dazzled, "CONDITION_DAZZLED"},
+ {ConditionType.Cursed, "CONDITION_CURSED"}
};
- IDictionary ConditionToString = new Dictionary
+ IDictionary ConditionToString = new Dictionary
{
- {Condition.Poison, "poison"},
- {Condition.Fire, "fire"},
- {Condition.Energy, "energy"},
- {Condition.Bleeding, "bleeding"},
- {Condition.Paralyze, "poison"},
- {Condition.Drown, "drown"},
- {Condition.Freezing, "freezing"},
- {Condition.Dazzled, "dazzled"},
- {Condition.Cursed, "cursed"}
+ {ConditionType.Poison, "poison"},
+ {ConditionType.Fire, "fire"},
+ {ConditionType.Energy, "energy"},
+ {ConditionType.Bleeding, "bleeding"},
+ {ConditionType.Paralyze, "poison"},
+ {ConditionType.Drown, "drown"},
+ {ConditionType.Freezing, "freezing"},
+ {ConditionType.Dazzled, "dazzled"},
+ {ConditionType.Cursed, "cursed"}
};
IDictionary CombatDamageNames = new Dictionary
{
- {CombatDamage.Physical, "COMBAT_PHYSICAL"},
- {CombatDamage.Energy, "COMBAT_ENERGY"},
- {CombatDamage.Earth, "COMBAT_EARTH"},
- {CombatDamage.Fire, "COMBAT_FIRE"},
+ {CombatDamage.Physical, "COMBAT_PHYSICALDAMAGE"},
+ {CombatDamage.Energy, "COMBAT_ENERGYDAMAGE"},
+ {CombatDamage.Earth, "COMBAT_EARTHDAMAGE"},
+ {CombatDamage.Fire, "COMBAT_FIREDAMAGE"},
{CombatDamage.LifeDrain, "COMBAT_LIFEDRAIN"},
{CombatDamage.ManaDrain, "COMBAT_MANADRAIN"},
{CombatDamage.Healing, "COMBAT_HEALING"},
- {CombatDamage.Drown, "COMBAT_DROWN"},
- {CombatDamage.Ice, "COMBAT_ICE"},
- {CombatDamage.Holy, "COMBAT_HOLY"},
- {CombatDamage.Death, "COMBAT_DEATH"}
+ {CombatDamage.Drown, "COMBAT_DROWNDAMAGE"},
+ {CombatDamage.Ice, "COMBAT_ICEDAMAGE"},
+ {CombatDamage.Holy, "COMBAT_HOLYDAMAGE"},
+ {CombatDamage.Death, "COMBAT_DEATHDAMAGE"}
//{"undefined", CombatDamage.Undefined}
};
@@ -454,17 +454,17 @@ public override bool WriteMonster(string directory, ref Monster monster)
dest.WriteLine("");
dest.WriteLine("monster.elements = {");
- dest.WriteLine($" {{type = COMBAT_PHYSICALDAMAGE, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Physical)}}},");
- dest.WriteLine($" {{type = COMBAT_ENERGYDAMAGE, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Energy)}}},");
- dest.WriteLine($" {{type = COMBAT_EARTHDAMAGE, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Earth)}}},");
- dest.WriteLine($" {{type = COMBAT_FIREDAMAGE, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Fire)}}},");
- dest.WriteLine($" {{type = COMBAT_LIFEDRAIN, percent = {GenericToTfsRevScriptSysElemementPercent(monster.LifeDrain)}}},");
- dest.WriteLine($" {{type = COMBAT_MANADRAIN, percent = {GenericToTfsRevScriptSysElemementPercent(monster.ManaDrain)}}},");
- //dest.WriteLine($" {{type = COMBAT_HEALING, percent = {GenericToTfsElemementPercent(monster.XXXX)}}},");
- dest.WriteLine($" {{type = COMBAT_DROWNDAMAGE, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Drown)}}},");
- dest.WriteLine($" {{type = COMBAT_ICEDAMAGE, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Ice)}}},");
- dest.WriteLine($" {{type = COMBAT_HOLYDAMAGE , percent = {GenericToTfsRevScriptSysElemementPercent(monster.Holy)}}},");
- dest.WriteLine($" {{type = COMBAT_DEATHDAMAGE , percent = {GenericToTfsRevScriptSysElemementPercent(monster.Death)}}}");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.Physical]}, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Physical)}}},");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.Energy]}, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Energy)}}},");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.Earth]}, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Earth)}}},");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.Fire]}, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Fire)}}},");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.LifeDrain]}, percent = {GenericToTfsRevScriptSysElemementPercent(monster.LifeDrain)}}},");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.ManaDrain]}, percent = {GenericToTfsRevScriptSysElemementPercent(monster.ManaDrain)}}},");
+ //dest.WriteLine($" {{type = { CombatDamageNames[CombatDamage.Healing]}, percent = {GenericToTfsElemementPercent(monster.XXXX)}}},");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.Drown]}, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Drown)}}},");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.Ice]}, percent = {GenericToTfsRevScriptSysElemementPercent(monster.Ice)}}},");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.Holy]} , percent = {GenericToTfsRevScriptSysElemementPercent(monster.Holy)}}},");
+ dest.WriteLine($" {{type = {CombatDamageNames[CombatDamage.Death]} , percent = {GenericToTfsRevScriptSysElemementPercent(monster.Death)}}}");
dest.WriteLine("}");
dest.WriteLine("");
@@ -495,12 +495,10 @@ double GenericToTfsRevScriptSysElemementPercent(double percent)
public string GenericToTfsRevScriptSysSpells(ref Spell spell)
{
- string attack;
+ string attack = $" {{name =\"{spell.Name}\", interval = {spell.Interval}, chance = {spell.Chance * 100:0}";
if (spell.Name == "melee")
{
- attack = $" {{name =\"combat\", interval = {spell.Interval}, chance = {spell.Chance}";
-
if ((spell.MinDamage != null) && (spell.MaxDamage != null))
{
attack += $", minDamage = {spell.MinDamage}, maxDamage = {spell.MaxDamage}";
@@ -517,116 +515,76 @@ public string GenericToTfsRevScriptSysSpells(ref Spell spell)
attack += $", effect = {magicEffectNames[Effect.DrawBlood]}";
- // Conditions only ever appear on melee damage?
- if (spell.Condition != null)
+ if (spell.Condition != ConditionType.None)
{
- attack += $", condition = {{type = {ConditioToTFsConstants[(Condition)spell.Condition]}, startDamage = {spell.StartDamage}, interval = {spell.Tick}}}";
+ attack += $", condition = {{type = {ConditionToTfsConstant[spell.Condition]}, startDamage = {spell.StartDamage}, interval = {spell.Tick}}}";
}
}
- else if (spell.Name == "speed")
+ else
{
- attack = $" {{name =\"{spell.Name}\", interval = {spell.Interval}, chance = {spell.Chance}";
+ if (spell.Name == "speed")
+ {
+ attack += $", speed = {{min = {spell.MinSpeedChange}, max = {spell.MaxSpeedChange}}}";
+ }
+ else if (spell.Name == "condition")
+ {
+ attack += $", type = {ConditionToTfsConstant[spell.Condition]}, startDamage = {spell.StartDamage}, tick = {spell.Tick}";
+ }
+ else if (spell.Name == "outfit")
+ {
+ if (!string.IsNullOrEmpty(spell.MonsterName))
+ {
+ attack += $", monster = \"{spell.MonsterName}\"";
+ }
+ else if (spell.ItemId != null)
+ {
+ attack += $", item = {spell.ItemId}";
+ }
+ }
+ else if ((spell.Name == "combat") && (spell.DamageElement != null))
+ {
+ attack += $", type = {CombatDamageNames[(CombatDamage)spell.DamageElement]}";
+ }
+ else if (spell.Name == "drunk")
+ {
+ attack += $", drunkenness = {spell.Drunkenness * 100}";
+ }
- if ((spell.SpeedChange != null) && (spell.Duration != null))
+ if ((spell.MinDamage != null) && (spell.MaxDamage != null))
{
- attack += $", SpeedChange = {spell.SpeedChange}, Duration = {spell.Duration}";
+ attack += $", minDamage = {spell.MinDamage}, maxDamage = {spell.MaxDamage}";
}
- }
- else if (spell.Name.Contains("invisible"))
- {
- attack = $" {{name =\"{spell.Name}\", interval = {spell.Interval}, chance = {spell.Chance}";
- if (spell.AreaEffect != null)
+ else if (spell.MaxDamage != null)
{
- attack += $", effect = {magicEffectNames[(Effect)spell.AreaEffect]}";
+ attack += $", minDamage = {spell.MinDamage}";
}
- }
- else
- {
- if (spell.Name.Contains("field") ||
- spell.Name.Contains("drunk") ||
- spell.Name.Contains("outfit"))
+ if (spell.Duration != null)
{
- attack = $" {{name =\"{spell.Name}\", interval = {spell.Interval}, chance = {spell.Chance}";
+ attack += $", duration = {spell.Duration}";
}
- else if (spell.Name.Contains("condition"))
+ if (spell.Range != null)
{
- string condition = spell.Name.Replace("condition", "");
- attack = $" {{name =\"{condition}\", interval = {spell.Interval}, chance = {spell.Chance}";
+ attack += $", range = {spell.Range}";
}
- else
+ if (spell.Radius != null)
{
- attack = $" {{name =\"combat\", interval = {spell.Interval}, chance = {spell.Chance}";
+ attack += $", radius = {spell.Radius}, target = {spell.OnTarget.ToString().ToLower()}";
}
- if (spell.Name != "outfit")
+ if (spell.Length != null)
{
- if ((spell.MinDamage != null) && (spell.MaxDamage != null))
- {
- attack += $", minDamage = {spell.MinDamage}, maxDamage = {spell.MaxDamage}";
- }
- else if (spell.MaxDamage != null)
- {
- attack += $", minDamage = {spell.MinDamage}";
- }
- if (spell.DamageElement != null)
- {
- attack += $", type = {CombatDamageNames[(CombatDamage)spell.DamageElement]}";
- }
- if (spell.Range != null)
- {
- attack += $", range = {spell.Range}";
- }
- if (spell.Radius != null)
- {
- attack += $", radius = {spell.Radius}";
- }
- if (spell.Length != null)
- {
- attack += $", length = {spell.Length}";
- }
- if (spell.Spread != null)
- {
- attack += $", spread = {spell.Spread}";
- }
- if (spell.ShootEffect != null)
- {
- attack += $", ShootEffect = {shootTypeNames[(Animation)spell.ShootEffect]}";
- }
- if (spell.AreaEffect != null)
- {
- attack += $", effect = {magicEffectNames[(Effect)spell.AreaEffect]}";
- }
- if (spell.Target != null)
- {
- attack += $", target = {spell.Target.ToString().ToLower()}";
- }
- if (spell.Duration != null)
- {
- attack += $", duration = {spell.Duration}";
- }
- if (spell.Name.Contains("condition"))
- {
- // Not implemented?
- if (spell.Tick != null)
- {
- attack += $", tick = {spell.Tick}";
- }
- // Not implemented?
- if (spell.StartDamage != null)
- {
- attack += $", start = {spell.StartDamage}";
- }
- }
- if (spell.Name == "outfit")
- {
- if (!string.IsNullOrEmpty(spell.MonsterName))
- {
- attack += $", monster = \"{spell.MonsterName}\"";
- }
- else if (spell.ItemId != null)
- {
- attack += $", item = {spell.ItemId}";
- }
- }
+ attack += $", length = {spell.Length}";
+ }
+ if (spell.Spread != null)
+ {
+ attack += $", spread = {spell.Spread}";
+ }
+ if (spell.ShootEffect != Animation.None)
+ {
+ attack += $", ShootEffect = {shootTypeNames[spell.ShootEffect]}";
+ }
+ if (spell.AreaEffect != Effect.None)
+ {
+ attack += $", effect = {magicEffectNames[spell.AreaEffect]}";
}
}
attack += "}";
diff --git a/app/MonsterConverterTfsXml/SerializerClasses.cs b/app/MonsterConverterTfsXml/SerializerClasses.cs
new file mode 100644
index 00000000..c52b738c
--- /dev/null
+++ b/app/MonsterConverterTfsXml/SerializerClasses.cs
@@ -0,0 +1,373 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace MonsterConverterTfsXml
+{
+ public enum namedImmunityXml
+ {
+ physical,
+ energy,
+ fire,
+ poison,
+ earth = namedImmunityXml.poison,
+ drown,
+ ice,
+ holy,
+ death,
+ lifedrain,
+ manadrain,
+ paralyze,
+ outfit,
+ drunk,
+ invisible,
+ invisibility = namedImmunityXml.invisible,
+ bleed,
+ NA
+ }
+
+ enum skullsXml
+ {
+ none = 0,
+ yellow,
+ green,
+ white,
+ red,
+ black,
+ orange
+ }
+
+ [Serializable, XmlRoot("monster")]
+ public class TFSXmlMonster
+ {
+ [XmlAttribute]
+ public string name;
+ [XmlAttribute]
+ public string nameDescription;
+ [XmlAttribute]
+ public string namedescription;
+ [XmlAttribute]
+ public string race = "blood";
+ [XmlAttribute]
+ public int experience = 0;
+ [XmlAttribute]
+ public int speed = 200;
+ [XmlAttribute]
+ public int manacost = 0;
+ [XmlAttribute]
+ public string skull = "none"; //uses strings, "none", "yellow", "green", "white", "red", "black", "orange"
+ //[XmlAttribute]
+ //public int script; //todo: how to handle
+
+ public TFSXmlHealth health;
+ public Flags flags;
+ public Look look;
+ public TargetChange targetchange;
+ public Attacks attacks;
+ public Defenses defenses;
+ public Immunities immunities;
+ public Voices voices;
+ public TfsXmlLoot loot;
+ public Elements elements;
+ public TFSXmlSummons summons;
+ }
+
+ [XmlRoot(ElementName = "health")]
+ public class TFSXmlHealth
+ {
+ [XmlAttribute]
+ public int now = 100;
+ [XmlAttribute]
+ public int max = 100;
+ }
+
+ [XmlRoot(ElementName = "flags")]
+ public class Flags
+ {
+ [XmlElementAttribute]
+ public MultiAttr[] flag;
+ }
+
+ public class MultiAttr
+ {
+ [XmlAnyAttribute]
+ public XmlAttribute[] attr;
+ }
+
+ public class TargetChange
+ {
+ [XmlAttribute]
+ public int interval = 0; //interval and speed are the same, default is 0
+ [XmlAttribute]
+ public int speed = 0; //interval and speed are the same, default is 0
+ [XmlAttribute]
+ public int chance = 0; //default is 0
+ }
+
+ public class Look
+ {
+ [XmlAttribute]
+ public int type = 0;
+ [XmlAttribute]
+ public int head = 0; //only can exist if type exists
+ [XmlAttribute]
+ public int body = 0; //only can exist if type exists
+ [XmlAttribute]
+ public int legs = 0; //only can exist if type exists
+ [XmlAttribute]
+ public int feet = 0; //only can exist if type exists
+ [XmlAttribute]
+ public int addons = 0; //only can exist if type exists
+ [XmlAttribute]
+ public int typeex = 0;
+ [XmlAttribute]
+ public int mount = 0;
+ [XmlAttribute]
+ public int corpse = 0;
+ }
+
+ public class TfsXmlSpellAttributes
+ {
+ [XmlAttribute]
+ public string key { get; set; }
+
+ [XmlAttribute]
+ public string value { get; set; }
+ }
+
+ public class Attacks
+ {
+ [XmlElementAttribute]
+ public Attack[] attack;
+ }
+
+ public class Attack
+ {
+ // only script or name not both
+ [XmlAttribute]
+ public string script;
+ [XmlAttribute]
+ public string name;
+
+ // Only one should exist, they represent the same information
+ [XmlAttribute]
+ public int interval = 0; //defaults to 2000 if missing, default is handled in parsing
+ [XmlAttribute]
+ public int speed = 0; //defaults to 2000 if missing, default is handled in parsing
+
+ [XmlAttribute]
+ public int chance = 100; //defaults to 100 if missing
+ [XmlAttribute]
+ public int range = 0; //defaults to 0 if missing
+ [XmlAttribute]
+ public int min = 0; //defaults to 0 if missing
+ [XmlAttribute]
+ public int max = 0; //defaults to 0 if missing
+ [XmlAttribute]
+ public int length = 0; //if length exists spread defaults to 3
+ [XmlAttribute]
+ public int spread = -1; //if length exists spread defaults to 3, default is handled in parsing
+ [XmlAttribute]
+ public int radius = 0;
+ [XmlAttribute]
+ public int direction; // 0 or 1 script only
+ [XmlAttribute]
+ public int target = 0; // Defaults to 0 if missing, used by script too
+
+ [XmlAttribute]
+ public int speedchange = 0;
+ [XmlAttribute]
+ public int minspeedchange = 0;
+ [XmlAttribute]
+ public int maxspeedchange = 0;
+ [XmlAttribute]
+ public int duration = 10000;
+ [XmlAttribute]
+ public int drunkenness = 25;
+
+ [XmlElementAttribute(ElementName = "attribute")]
+ public TfsXmlSpellAttributes[] attribute { get; set; }
+
+ // the following only exist when attack name is melee
+ // when melee exists minMax and Max are set to 0
+ [XmlAttribute]
+ public int skill;
+ [XmlAttribute]
+ public int attack;
+ [XmlAttribute]
+ public int fire;
+ [XmlAttribute]
+ public int poison;
+ [XmlAttribute]
+ public int energy;
+ [XmlAttribute]
+ public int drown;
+ [XmlAttribute]
+ public int freeze;
+ [XmlAttribute]
+ public int dazzle;
+ [XmlAttribute]
+ public int curse;
+ [XmlAttribute]
+ public int bleed; //bleed and physical are the same
+ [XmlAttribute]
+ public int physical; //bleed and physical are the same
+
+ [XmlAttribute]
+ public int tick; //only used if a condition is set each type has its own default tick which can be overriden with this attr
+ [XmlAttribute]
+ public int start; //Start condition damage
+
+ [XmlAttribute]
+ public string monster;
+ [XmlAttribute]
+ public int item;
+ }
+
+ public class Defenses
+ {
+ [XmlAttribute]
+ public uint defense;
+ [XmlAttribute]
+ public uint armor;
+
+ [XmlElementAttribute(ElementName = "defense")]
+ public Attack[] defenses;
+ }
+
+ public class Immunities
+ {
+ [XmlElementAttribute]
+ public Immunity[] immunity;
+ }
+
+ public class Immunity
+ {
+ [XmlAttribute]
+ public namedImmunityXml name = namedImmunityXml.NA;
+ [XmlAttribute]
+ public int physical = 0; //Immune to physical and bleeding condition
+ [XmlAttribute]
+ public int energy = 0;
+ [XmlAttribute]
+ public int fire = 0;
+ [XmlAttribute]
+ public int poison = 0; //poison and earth are the same
+ [XmlAttribute]
+ public int earth = 0; //poison and earth are the same
+ [XmlAttribute]
+ public int drown = 0;
+ [XmlAttribute]
+ public int ice = 0;
+ [XmlAttribute]
+ public int holy = 0;
+ [XmlAttribute]
+ public int death = 0;
+ [XmlAttribute]
+ public int lifedrain = 0;
+ [XmlAttribute]
+ public int manadrain = 0;
+ [XmlAttribute]
+ public int paralyze = 0;
+ [XmlAttribute]
+ public int outfit = 0; // TODO should be true by default?
+ [XmlAttribute]
+ public int bleed = 0; // immue to only bleed condition
+ [XmlAttribute]
+ public int drunk = 0;
+ [XmlAttribute]
+ public int invisible = 0; //invisible and invisibility are the same
+ [XmlAttribute]
+ public int invisibility = 0; //invisible and invisibility are the same
+ }
+
+ public class Voices
+ {
+ [XmlAttribute]
+ public int interval; //interval and speed are the same
+ [XmlAttribute]
+ public int speed; //interval and speed are the same
+ [XmlAttribute]
+ public int chance;
+ [XmlElementAttribute]
+ public VoiceXml[] voice;
+ }
+
+ public class VoiceXml
+ {
+ [XmlAttribute]
+ public string sentence;
+ ///
+ /// Can be 1 or true
+ /// if it doesnt exist the value is false
+ ///
+ [XmlAttribute]
+ public string yell;
+ }
+
+ [Serializable, XmlRoot("Loot")]
+ public class TfsXmlLoot
+ {
+ [XmlElementAttribute]
+ public Item[] item;
+ }
+
+ public class Item
+ {
+ // Only name or ID will be used not both
+ [XmlAttribute]
+ public string name;
+ [XmlAttribute]
+ public int id;
+ [XmlAttribute]
+ public int countmax = 1; //default value is 1
+ [XmlAttribute]
+ public int chance; //chance and chance1 are the same
+ [XmlAttribute]
+ public int chance1; //chance and chance1 are the same
+
+ //optional
+ //[XmlAttribute]
+ //public int subtype; //used for charges?
+ //[XmlAttribute]
+ //public int actionId;
+ //[XmlAttribute]
+ //public string test; //used for? //Id guess to override the default item name string?
+ }
+
+ public class Elements
+ {
+ [XmlElementAttribute]
+ public MultiAttr[] element;
+ }
+
+ [XmlRoot(ElementName = "summons")]
+ public class TFSXmlSummons
+ {
+ [XmlAttribute]
+ public int maxSummons;
+ [XmlElementAttribute]
+ public TFSXmlSummon[] summon;
+ }
+
+ [XmlRoot(ElementName = "summon")]
+ public class TFSXmlSummon
+ {
+ [XmlAttribute]
+ public string name;
+ [XmlAttribute]
+ public int interval = 1000; //interval and speed are the same //defaults to 1000 if missing
+ [XmlAttribute]
+ public int speed = 1000; //interval and speed are the same //defaults to 1000 if missing
+ [XmlAttribute]
+ public double chance = 100; //defaults to 100 if missing
+ [XmlAttribute]
+ public int max;
+ [XmlAttribute]
+ public bool force;
+ }
+}
diff --git a/app/MonsterConverterTfsXml/TfsXmlConverter.cs b/app/MonsterConverterTfsXml/TfsXmlConverter.cs
index 41d4bac7..fc9f4831 100644
--- a/app/MonsterConverterTfsXml/TfsXmlConverter.cs
+++ b/app/MonsterConverterTfsXml/TfsXmlConverter.cs
@@ -4,8 +4,6 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
-using System.Text;
-using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
@@ -20,7 +18,7 @@ public class TfsXmlConverter : MonsterConverter
const uint MAX_LOOTCHANCE = 100000;
const uint ATTACK_INTERVAL_DEFAULT = 2000;
- IDictionary magicEffectNames = new Dictionary
+ private readonly IDictionary magicEffectNames = new Dictionary
{
{"redspark", Effect.DrawBlood},
{"bluebubble", Effect.LoseEnergy},
@@ -105,7 +103,7 @@ public class TfsXmlConverter : MonsterConverter
{"purplesmoke", Effect.PurpleSmoke}
};
- IDictionary shootTypeNames = new Dictionary
+ private readonly IDictionary shootTypeNames = new Dictionary
{
{"spear", Animation.Spear},
{"bolt", Animation.Bolt},
@@ -159,7 +157,7 @@ public class TfsXmlConverter : MonsterConverter
{"simplearrow", Animation.SimpleArrow}
};
- IDictionary CombatDamageNames = new Dictionary
+ private readonly IDictionary combatDamageNames = new Dictionary
{
{"physical", CombatDamage.Physical},
{"energy", CombatDamage.Energy},
@@ -175,6 +173,35 @@ public class TfsXmlConverter : MonsterConverter
//{"undefined", CombatDamage.Undefined}
};
+ private readonly IDictionary conditionDamageNames = new Dictionary
+ {
+ {"physicalcondition", ConditionType.Bleeding},
+ {"bleedcondition", ConditionType.Bleeding},
+ {"energycondition", ConditionType.Energy},
+ {"poisoncondition", ConditionType.Poison},
+ {"earthcondition", ConditionType.Poison},
+ {"firecondition", ConditionType.Fire},
+ {"drowncondition", ConditionType.Drown},
+ {"icecondition", ConditionType.Freezing},
+ {"freezecondition", ConditionType.Freezing},
+ {"holycondition", ConditionType.Dazzled},
+ {"dazzledcondition", ConditionType.Dazzled},
+ {"cursecondition", ConditionType.Cursed},
+ {"deathcondition", ConditionType.Cursed}
+ };
+
+ private readonly IDictionary conditionDefaultTick = new Dictionary
+ {
+ {ConditionType.Bleeding, 4000},
+ {ConditionType.Energy, 10000},
+ {ConditionType.Fire, 9000},
+ {ConditionType.Poison, 4000},
+ {ConditionType.Drown, 5000},
+ {ConditionType.Freezing, 8000},
+ {ConditionType.Dazzled, 10000},
+ {ConditionType.Cursed, 4000},
+ };
+
public override string FileExt { get => "xml"; }
public override bool IsReadSupported { get => true; }
@@ -186,8 +213,8 @@ public override bool ReadMonster(string filename, out Monster monster)
{
XmlSerializer serializer = new XmlSerializer(typeof(TFSXmlMonster));
- serializer.UnknownNode += new XmlNodeEventHandler(serializer_UnknownNode);
- serializer.UnknownAttribute += new XmlAttributeEventHandler(serializer_UnknownAttribute);
+ serializer.UnknownNode += new XmlNodeEventHandler(Serializer_UnknownNode);
+ serializer.UnknownAttribute += new XmlAttributeEventHandler(Serializer_UnknownAttribute);
// A FileStream is needed to read the XML document.
FileStream fs = new FileStream(filename, FileMode.Open);
@@ -226,7 +253,7 @@ private void xmlToGeneric(TFSXmlMonster tfsMonster, out Monster monster)
Health = (uint)tfsMonster.health.max,
Experience = (uint)tfsMonster.experience,
Speed = (uint)tfsMonster.speed,
- Race = tfsToGenericBlood(tfsMonster.race),
+ Race = TfsToGenericBlood(tfsMonster.race),
};
if (!string.IsNullOrEmpty(tfsMonster.nameDescription))
@@ -430,47 +457,47 @@ private void xmlToGeneric(TFSXmlMonster tfsMonster, out Monster monster)
{
if (x.attr[0].Name == "physicalPercent")
{
- monster.Physical = tfstoGenericElementalPercent(value);
+ monster.Physical = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "icePercent")
{
- monster.Ice = tfstoGenericElementalPercent(value);
+ monster.Ice = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "poisonPercent")
{
- monster.Earth = tfstoGenericElementalPercent(value);
+ monster.Earth = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "earthPercent")
{
- monster.Earth = tfstoGenericElementalPercent(value);
+ monster.Earth = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "firePercent")
{
- monster.Fire = tfstoGenericElementalPercent(value);
+ monster.Fire = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "energyPercent")
{
- monster.Energy = tfstoGenericElementalPercent(value);
+ monster.Energy = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "holyPercent")
{
- monster.Holy = tfstoGenericElementalPercent(value);
+ monster.Holy = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "deathPercent")
{
- monster.Death = tfstoGenericElementalPercent(value);
+ monster.Death = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "drownPercent")
{
- monster.Drown = tfstoGenericElementalPercent(value);
+ monster.Drown = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "lifedrainPercent")
{
- monster.LifeDrain = tfstoGenericElementalPercent(value);
+ monster.LifeDrain = TfstoGenericElementalPercent(value);
}
else if (x.attr[0].Name == "manadrainPercent")
{
- monster.ManaDrain = tfstoGenericElementalPercent(value);
+ monster.ManaDrain = TfstoGenericElementalPercent(value);
}
}
}
@@ -645,7 +672,7 @@ private void xmlToGeneric(TFSXmlMonster tfsMonster, out Monster monster)
}
}
- private Blood tfsToGenericBlood(string blood)
+ private Blood TfsToGenericBlood(string blood)
{
Blood race = Blood.blood; //default
@@ -726,7 +753,43 @@ private void XmlSpellsToGeneric(ref Monster monster, Attack[] spells, SpellCateg
spell.Interval = ATTACK_INTERVAL_DEFAULT;
}
- spell.Chance = (uint)attack.chance;
+ spell.Chance = attack.chance / 100.0;
+
+ if (attack.attribute != null)
+ {
+ foreach (var attr in attack.attribute)
+ {
+ if (attr.key.ToLower() == "shootEffect".ToLower())
+ {
+ spell.ShootEffect = shootTypeNames[attr.value.ToLower()];
+ }
+ else if (attr.key.ToLower() == "areaEffect".ToLower())
+ {
+ spell.AreaEffect = magicEffectNames[attr.value.ToLower()];
+ }
+ else
+ {
+ Console.WriteLine($"Unkown attack attribute {attr.key}");
+ }
+ }
+ }
+
+ if (attack.range > 0)
+ {
+ spell.Range = (uint?)attack.range;
+ }
+
+ if (attack.length > 0)
+ {
+ spell.Length = (uint?)attack.length;
+ spell.Spread = (attack.spread == -1) ? 3 : (uint?)attack.spread;
+ }
+
+ if (attack.radius > 0)
+ {
+ spell.Radius = (uint?)attack.radius;
+ spell.OnTarget = (attack.target == 1);
+ }
if (attack.name == "melee")
{
@@ -743,64 +806,81 @@ private void XmlSpellsToGeneric(ref Monster monster, Attack[] spells, SpellCateg
if (attack.fire != 0)
{
- spell.Condition = Condition.Fire;
+ spell.Condition = ConditionType.Fire;
spell.StartDamage = attack.poison;
- spell.Tick = (attack.tick != 0) ? attack.tick : 9000;
}
else if (attack.poison != 0)
{
- spell.Condition = Condition.Poison;
+ spell.Condition = ConditionType.Poison;
spell.StartDamage = attack.poison;
- spell.Tick = (attack.tick != 0) ? attack.tick : 4000;
}
else if (attack.energy != 0)
{
- spell.Condition = Condition.Energy;
+ spell.Condition = ConditionType.Energy;
spell.StartDamage = attack.energy;
- spell.Tick = (attack.tick != 0) ? attack.tick : 10000;
}
else if (attack.drown != 0)
{
- spell.Condition = Condition.Drown;
+ spell.Condition = ConditionType.Drown;
spell.StartDamage = attack.drown;
- spell.Tick = (attack.tick != 0) ? attack.tick : 5000;
}
else if (attack.dazzle != 0)
{
- spell.Condition = Condition.Dazzled;
+ spell.Condition = ConditionType.Dazzled;
spell.StartDamage = attack.dazzle;
- spell.Tick = (attack.tick != 0) ? attack.tick : 10000;
}
else if (attack.curse != 0)
{
- spell.Condition = Condition.Cursed;
+ spell.Condition = ConditionType.Cursed;
spell.StartDamage = attack.curse;
- spell.Tick = (attack.tick != 0) ? attack.tick : 4000;
}
else if (attack.bleed != 0)
{
- spell.Condition = Condition.Bleeding;
+ spell.Condition = ConditionType.Bleeding;
spell.StartDamage = attack.bleed;
- spell.Tick = (attack.tick != 0) ? attack.tick : 4000;
}
else if (attack.physical != 0)
{
- spell.Condition = Condition.Bleeding;
+ spell.Condition = ConditionType.Bleeding;
spell.StartDamage = attack.physical;
- spell.Tick = (attack.tick != 0) ? attack.tick : 4000;
+ }
+ else if (attack.freeze != 0)
+ {
+ spell.Condition = ConditionType.Freezing;
+ spell.StartDamage = attack.freeze;
+ }
+ if (spell.Condition != ConditionType.None)
+ {
+ spell.Tick = (attack.tick != 0) ? attack.tick : conditionDefaultTick[spell.Condition];
}
}
else if (attack.name == "speed")
{
- spell.SpeedChange = attack.speedchange;
- spell.Duration = attack.duration;
- if (attack.duration == 0)
+ if (attack.speedchange != 0)
{
- spell.Duration = 10000; // Default when no duration set
+ spell.MinSpeedChange = attack.speedchange;
+ spell.MaxSpeedChange = attack.speedchange;
}
+ else
+ {
+ spell.MinSpeedChange = attack.minspeedchange;
+ spell.MaxSpeedChange = attack.maxspeedchange;
+ }
+ }
+ else if (attack.name == "drunk")
+ {
+ spell.Drunkenness = attack.drunkenness / 100.0;
}
else
{
+ if (attack.name.Contains("condition"))
+ {
+ spell.Condition = conditionDamageNames[attack.name];
+ spell.Name = "condition";
+ spell.Tick = (attack.tick != 0) ? attack.tick : conditionDefaultTick[spell.Condition];
+ spell.StartDamage = attack.start;
+ }
+
// Always default both if max is included to be explicit
// Some spells don't have damage so don't include either of them
if (attack.max != 0)
@@ -809,44 +889,10 @@ private void XmlSpellsToGeneric(ref Monster monster, Attack[] spells, SpellCateg
spell.MaxDamage = attack.max;
}
- if (attack.attribute != null)
- {
- foreach (var attr in attack.attribute)
- {
- if (attr.key.ToLower() == "shootEffect".ToLower())
- {
- spell.ShootEffect = shootTypeNames[attr.value.ToLower()];
- }
- else if (attr.key.ToLower() == "areaEffect".ToLower())
- {
- spell.AreaEffect = magicEffectNames[attr.value.ToLower()];
- }
- else
- {
- Console.WriteLine($"Unkown attack attribute {attr.key}");
- }
- }
- }
-
- if (attack.range > 0)
- {
- spell.Range = (uint?)attack.range;
- }
-
- if (attack.length > 0)
- {
- spell.Length = (uint?)attack.length;
- spell.Spread = (uint?)attack.spread;
- if ((spell.Length > 3) && (spell.Spread == 0))
- {
- spell.Spread = 3;
- }
- }
- spell.Target = (attack.target == 1);
-
- if (CombatDamageNames.ContainsKey(spell.Name))
+ if (combatDamageNames.ContainsKey(spell.Name))
{
- spell.DamageElement = CombatDamageNames[spell.Name];
+ spell.DamageElement = combatDamageNames[spell.Name];
+ spell.Name = "combat";
}
if (!string.IsNullOrEmpty(attack.monster))
@@ -859,12 +905,17 @@ private void XmlSpellsToGeneric(ref Monster monster, Attack[] spells, SpellCateg
}
}
+ if ((attack.name == "speed") || (attack.name == "outfit") || (attack.name == "invisible") || (attack.name == "drunk"))
+ {
+ spell.Duration = attack.duration;
+ }
+
monster.Attacks.Add(spell);
}
}
}
- private string generictoTfsBlood(Blood race)
+ private string GenerictoTfsBlood(Blood race)
{
string bloodName = "blood";
@@ -894,377 +945,20 @@ private string generictoTfsBlood(Blood race)
return bloodName;
}
- private double tfstoGenericElementalPercent(int percent)
+ private double TfstoGenericElementalPercent(int percent)
{
return (1 - ((double)percent / 100));
}
- private void serializer_UnknownNode(object sender, XmlNodeEventArgs e)
+ private void Serializer_UnknownNode(object sender, XmlNodeEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Unknown Node:" + e.Name + "\t" + e.Text);
}
- private void serializer_UnknownAttribute(object sender, XmlAttributeEventArgs e)
+ private void Serializer_UnknownAttribute(object sender, XmlAttributeEventArgs e)
{
System.Xml.XmlAttribute attr = e.Attr;
System.Diagnostics.Debug.WriteLine("Unknown attribute " + attr.Name + "='" + attr.Value + "'");
}
}
-
- #region XML serializer classes
- public enum namedImmunityXml
- {
- physical,
- energy,
- fire,
- poison,
- earth = namedImmunityXml.poison,
- drown,
- ice,
- holy,
- death,
- lifedrain,
- manadrain,
- paralyze,
- outfit,
- drunk,
- invisible,
- invisibility = namedImmunityXml.invisible,
- bleed,
- NA
- }
-
- enum skullsXml
- {
- none = 0,
- yellow,
- green,
- white,
- red,
- black,
- orange
- }
-
- [Serializable, XmlRoot("monster")]
- public class TFSXmlMonster
- {
- [XmlAttribute]
- public string name;
- [XmlAttribute]
- public string nameDescription;
- [XmlAttribute]
- public string namedescription;
- [XmlAttribute]
- public string race = "blood";
- [XmlAttribute]
- public int experience = 0;
- [XmlAttribute]
- public int speed = 200;
- [XmlAttribute]
- public int manacost = 0;
- [XmlAttribute]
- public string skull = "none"; //uses strings, "none", "yellow", "green", "white", "red", "black", "orange"
- //[XmlAttribute]
- //public int script; //todo: how to handle
-
- public TFSXmlHealth health;
- public Flags flags;
- public Look look;
- public TargetChange targetchange;
- public Attacks attacks;
- public Defenses defenses;
- public Immunities immunities;
- public Voices voices;
- public TfsXmlLoot loot;
- public Elements elements;
- public TFSXmlSummons summons;
- }
-
- [XmlRoot(ElementName = "health")]
- public class TFSXmlHealth
- {
- [XmlAttribute]
- public int now = 100;
- [XmlAttribute]
- public int max = 100;
- }
-
- [XmlRoot(ElementName = "flags")]
- public class Flags
- {
- [XmlElementAttribute]
- public MultiAttr[] flag;
- }
-
- public class MultiAttr
- {
- [XmlAnyAttribute]
- public XmlAttribute[] attr;
- }
-
- public class TargetChange
- {
- [XmlAttribute]
- public int interval = 0; //interval and speed are the same, default is 0
- [XmlAttribute]
- public int speed = 0; //interval and speed are the same, default is 0
- [XmlAttribute]
- public int chance = 0; //default is 0
- }
-
- public class Look
- {
- [XmlAttribute]
- public int type = 0;
- [XmlAttribute]
- public int head = 0; //only can exist if type exists
- [XmlAttribute]
- public int body = 0; //only can exist if type exists
- [XmlAttribute]
- public int legs = 0; //only can exist if type exists
- [XmlAttribute]
- public int feet = 0; //only can exist if type exists
- [XmlAttribute]
- public int addons = 0; //only can exist if type exists
- [XmlAttribute]
- public int typeex = 0;
- [XmlAttribute]
- public int mount = 0;
- [XmlAttribute]
- public int corpse = 0;
- }
-
- public class TfsXmlSpellAttributes
- {
- [XmlAttribute]
- public string key { get; set; }
-
- [XmlAttribute]
- public string value { get; set; }
- }
-
- public class Attacks
- {
- [XmlElementAttribute]
- public Attack[] attack;
- }
-
- public class Attack
- {
- // only script or name not both
- //[XmlAttribute]
- //public string script;
- [XmlAttribute]
- public string name;
-
- // Only one should exist, they represent the same information
- [XmlAttribute]
- public int interval = 0; //defaults to 2000 if missing, default is handled in parsing
- [XmlAttribute]
- public int speed = 0; //defaults to 2000 if missing, default is handled in parsing
-
- [XmlAttribute]
- public int chance = 100; //defaults to 100 if missing
- [XmlAttribute]
- public int range = 0; //defaults to 0 if missing
- [XmlAttribute]
- public int min = 0; //defaults to 0 if missing
- [XmlAttribute]
- public int max = 0; //defaults to 0 if missing
- [XmlAttribute]
- public int length = 0; //if length exists spread defaults to 3
- [XmlAttribute]
- public int spread = 0; //if length exists spread defaults to 3
- [XmlAttribute]
- public int radius = 0;
- [XmlAttribute]
- public int target = 0; // Defaults to 0 if missing
-
- [XmlAttribute]
- public int speedchange = 0;
- [XmlAttribute]
- public int duration = 0;
-
- [XmlElementAttribute(ElementName = "attribute")]
- public TfsXmlSpellAttributes[] attribute { get; set; }
-
- // the following only exist when attack name is melee
- // when melee exists minMax and Max are set to 0
- [XmlAttribute]
- public int skill;
- [XmlAttribute]
- public int attack;
- [XmlAttribute]
- public int fire;
- [XmlAttribute]
- public int poison;
- [XmlAttribute]
- public int energy;
- [XmlAttribute]
- public int drown;
- [XmlAttribute]
- public int freeze;
- [XmlAttribute]
- public int dazzle;
- [XmlAttribute]
- public int curse;
- [XmlAttribute]
- public int bleed; //bleed and physical are the same
- [XmlAttribute]
- public int physical; //bleed and physical are the same
-
- [XmlAttribute]
- public int tick; //only used if a condition is set each type has its own default tick which can be overriden with this attr
- [XmlAttribute]
- public int start; //Start condition damage
-
- [XmlAttribute]
- public string monster;
- [XmlAttribute]
- public int item;
- }
-
- public class Defenses
- {
- [XmlAttribute]
- public uint defense;
- [XmlAttribute]
- public uint armor;
-
- [XmlElementAttribute(ElementName = "defense")]
- public Attack[] defenses;
- }
-
- public class Immunities
- {
- [XmlElementAttribute]
- public Immunity[] immunity;
- }
-
- public class Immunity
- {
- [XmlAttribute]
- public namedImmunityXml name = namedImmunityXml.NA;
- [XmlAttribute]
- public int physical = 0; //Immune to physical and bleeding condition
- [XmlAttribute]
- public int energy = 0;
- [XmlAttribute]
- public int fire = 0;
- [XmlAttribute]
- public int poison = 0; //poison and earth are the same
- [XmlAttribute]
- public int earth = 0; //poison and earth are the same
- [XmlAttribute]
- public int drown = 0;
- [XmlAttribute]
- public int ice = 0;
- [XmlAttribute]
- public int holy = 0;
- [XmlAttribute]
- public int death = 0;
- [XmlAttribute]
- public int lifedrain = 0;
- [XmlAttribute]
- public int manadrain = 0;
- [XmlAttribute]
- public int paralyze = 0;
- [XmlAttribute]
- public int outfit = 0; // TODO should be true by default?
- [XmlAttribute]
- public int bleed = 0; // immue to only bleed condition
- [XmlAttribute]
- public int drunk = 0;
- [XmlAttribute]
- public int invisible = 0; //invisible and invisibility are the same
- [XmlAttribute]
- public int invisibility = 0; //invisible and invisibility are the same
- }
-
- public class Voices
- {
- [XmlAttribute]
- public int interval; //interval and speed are the same
- [XmlAttribute]
- public int speed; //interval and speed are the same
- [XmlAttribute]
- public int chance;
- [XmlElementAttribute]
- public VoiceXml[] voice;
- }
-
- public class VoiceXml
- {
- [XmlAttribute]
- public string sentence;
- ///
- /// Can be 1 or true
- /// if it doesnt exist the value is false
- ///
- [XmlAttribute]
- public string yell;
- }
-
- [Serializable, XmlRoot("Loot")]
- public class TfsXmlLoot
- {
- [XmlElementAttribute]
- public Item[] item;
- }
-
- public class Item
- {
- // Only name or ID will be used not both
- [XmlAttribute]
- public string name;
- [XmlAttribute]
- public int id;
- [XmlAttribute]
- public int countmax = 1; //default value is 1
- [XmlAttribute]
- public int chance; //chance and chance1 are the same
- [XmlAttribute]
- public int chance1; //chance and chance1 are the same
-
- //optional
- //[XmlAttribute]
- //public int subtype; //used for charges?
- //[XmlAttribute]
- //public int actionId;
- //[XmlAttribute]
- //public string test; //used for? //Id guess to override the fault item name string?
- }
-
- public class Elements
- {
- [XmlElementAttribute]
- public MultiAttr[] element;
- }
-
- [XmlRoot(ElementName = "summons")]
- public class TFSXmlSummons
- {
- [XmlAttribute]
- public int maxSummons;
- [XmlElementAttribute]
- public TFSXmlSummon[] summon;
- }
-
- [XmlRoot(ElementName = "summon")]
- public class TFSXmlSummon
- {
- [XmlAttribute]
- public string name;
- [XmlAttribute]
- public int interval = 1000; //interval and speed are the same //defaults to 1000 if missing
- [XmlAttribute]
- public int speed = 1000; //interval and speed are the same //defaults to 1000 if missing
- [XmlAttribute]
- public double chance = 100; //defaults to 100 if missing
- [XmlAttribute]
- public int max;
- [XmlAttribute]
- public bool force;
- }
- #endregion
}
diff --git a/app/MonsterConverterTibiaWiki/TibiaWikiConverter.cs b/app/MonsterConverterTibiaWiki/TibiaWikiConverter.cs
index 74878b89..1f081302 100644
--- a/app/MonsterConverterTibiaWiki/TibiaWikiConverter.cs
+++ b/app/MonsterConverterTibiaWiki/TibiaWikiConverter.cs
@@ -166,14 +166,14 @@ private static bool ParseAbilities(Monster mon, MatchCollection mc)
case var _ when new Regex(@"\[\[haste\]\]").IsMatch(cleanability):
{
- var spell = new Spell() { Name = "speed", SpellCategory = SpellCategory.Defensive, Interval = 2000, Chance = 15, SpeedChange = 300, AreaEffect = Effect.MagicRed, Duration = 7000 };
+ var spell = new Spell() { Name = "speed", SpellCategory = SpellCategory.Defensive, Interval = 2000, Chance = 15, MinSpeedChange = 300, MaxSpeedChange = 300, AreaEffect = Effect.MagicRed, Duration = 7000 };
mon.Attacks.Add(spell);
break;
}
case var _ when new Regex(@"\[\[strong haste\]\]").IsMatch(cleanability):
{
- var spell = new Spell() { Name = "speed", SpellCategory = SpellCategory.Defensive, Interval = 2000, Chance = 15, SpeedChange = 450, AreaEffect = Effect.MagicRed, Duration = 4000 };
+ var spell = new Spell() { Name = "speed", SpellCategory = SpellCategory.Defensive, Interval = 2000, Chance = 15, MinSpeedChange = 450, MaxSpeedChange = 450, AreaEffect = Effect.MagicRed, Duration = 4000 };
mon.Attacks.Add(spell);
break;
}
@@ -201,7 +201,7 @@ private static bool ParseAbilities(Monster mon, MatchCollection mc)
return true;
}
- private static Animation? TibiaWikiToAnimation(string effect)
+ private static Animation TibiaWikiToAnimation(string effect)
{
if ((effect == "spear") || (effect == "spears"))
{
@@ -227,7 +227,10 @@ private static bool ParseAbilities(Monster mon, MatchCollection mc)
{
return Animation.SmallStone;
}
- return null;
+ else
+ {
+ return Animation.None;
+ }
}
///