diff --git a/H3Calc/Data/heroes.json b/H3Calc/Data/heroes.json index fad2db9..2874174 100644 --- a/H3Calc/Data/heroes.json +++ b/H3Calc/Data/heroes.json @@ -7,12 +7,12 @@ { "Id": 1, "Name": "Валеска", - "SpecializedUnitId": 2 + "SpecializedUnitIds": [ 2, 3 ] }, { "Id": 2, "Name": "Эдрик", - "SpecializedUnitId": 4 + "SpecializedUnitIds": [ 4, 5 ] }, { "Id": 3, @@ -25,16 +25,17 @@ { "Id": 5, "Name": "Сорша", - "SpecializedUnitId": 6 + "SpecializedUnitIds": [ 6, 7 ] }, { "Id": 6, - "Name": "Кристиан" + "Name": "Кристиан", + "SpecializedUnitIds": [ 158 ] }, { "Id": 7, "Name": "Турис", - "SpecializedUnitId": 10 + "SpecializedUnitIds": [ 10, 11 ] }, { "Id": 8, @@ -58,7 +59,7 @@ { "Id": 12, "Name": "Инхам", - "SpecializedUnitId": 8 + "SpecializedUnitIds": [ 8, 9 ] }, { "Id": 13, @@ -81,7 +82,7 @@ { "Id": 17, "Name": "Уфретин", - "SpecializedUnitId": 16 + "SpecializedUnitIds": [ 16, 17 ] }, { "Id": 18, @@ -90,7 +91,7 @@ { "Id": 19, "Name": "Риланд", - "SpecializedUnitId": 22 + "SpecializedUnitIds": [ 22, 23 ] }, { "Id": 20, @@ -99,12 +100,12 @@ { "Id": 21, "Name": "Ивор", - "SpecializedUnitId": 18 + "SpecializedUnitIds": [ 18, 19 ] }, { "Id": 22, "Name": "Кланси", - "SpecializedUnitId": 24 + "SpecializedUnitIds": [ 24, 25 ] }, { "Id": 23, @@ -143,22 +144,22 @@ { "Id": 31, "Name": "Аерис", - "SpecializedUnitId": 20 + "SpecializedUnitIds": [ 20, 21 ] }, { "Id": 32, "Name": "Пигедрам", - "SpecializedUnitId": 30 + "SpecializedUnitIds": [ 30, 31 ] }, { "Id": 33, "Name": "Тан", - "SpecializedUnitId": 36 + "SpecializedUnitIds": [ 36, 37 ] }, { "Id": 34, "Name": "Жосефина", - "SpecializedUnitId": 32 + "SpecializedUnitIds": [ 32, 33 ] }, { "SpecializedSecondarySkill": "Armorer", @@ -167,12 +168,13 @@ }, { "Id": 36, - "Name": "Торосар" + "Name": "Торосар", + "SpecializedUnitIds": [ 158 ] }, { "Id": 37, "Name": "Фафнер", - "SpecializedUnitId": 38 + "SpecializedUnitIds": [ 38, 39 ] }, { "Id": 38, @@ -181,7 +183,7 @@ { "Id": 39, "Name": "Иона", - "SpecializedUnitId": 36 + "SpecializedUnitIds": [ 36, 37 ] }, { "Id": 40, @@ -202,7 +204,7 @@ { "Id": 44, "Name": "Теодор", - "SpecializedUnitId": 34 + "SpecializedUnitIds": [ 34, 35 ] }, { "Id": 45, @@ -220,22 +222,22 @@ { "Id": 48, "Name": "Фиона", - "SpecializedUnitId": 74 + "SpecializedUnitIds": [ 74, 75 ] }, { "Id": 49, "Name": "Рашка", - "SpecializedUnitId": 80 + "SpecializedUnitIds": [ 80, 81 ] }, { "Id": 50, "Name": "Мариус", - "SpecializedUnitId": 76 + "SpecializedUnitIds": [ 76, 77 ] }, { "Id": 51, "Name": "Игнат", - "SpecializedUnitId": 70 + "SpecializedUnitIds": [ 70, 71 ] }, { "Id": 52, @@ -244,16 +246,17 @@ { "Id": 53, "Name": "Калх", - "SpecializedUnitId": 72 + "SpecializedUnitIds": [ 72, 73 ] }, { "Id": 54, - "Name": "Пир" + "Name": "Пир", + "SpecializedUnitIds": [ 158 ] }, { "Id": 55, "Name": "Нимус", - "SpecializedUnitId": 78 + "SpecializedUnitIds": [ 78, 79 ] }, { "Id": 56, @@ -295,27 +298,27 @@ { "Id": 64, "Name": "Стракер", - "SpecializedUnitId": 44 + "SpecializedUnitIds": [ 44, 45 ] }, { "Id": 65, "Name": "Вокиал", - "SpecializedUnitId": 48 + "SpecializedUnitIds": [ 48, 49 ] }, { "Id": 66, "Name": "Моандор", - "SpecializedUnitId": 50 + "SpecializedUnitIds": [ 50, 51 ] }, { "Id": 67, "Name": "Чарна", - "SpecializedUnitId": 46 + "SpecializedUnitIds": [ 46, 47 ] }, { "Id": 68, "Name": "Тамика", - "SpecializedUnitId": 52 + "SpecializedUnitIds": [ 52, 53 ] }, { "Id": 69, @@ -328,7 +331,7 @@ { "Id": 71, "Name": "Галтран", - "SpecializedUnitId": 42 + "SpecializedUnitIds": [ 42, 43 ] }, { "Id": 72, @@ -369,21 +372,22 @@ { "Id": 80, "Name": "Лорелей", - "SpecializedUnitId": 58 + "SpecializedUnitIds": [ 58, 59 ] }, { "Id": 81, - "Name": "Арлаш" + "Name": "Арлаш", + "SpecializedUnitIds": [ 158 ] }, { "Id": 82, "Name": "Дас", - "SpecializedUnitId": 64 + "SpecializedUnitIds": [ 64, 65 ] }, { "Id": 83, "Name": "Аджит", - "SpecializedUnitId": 60 + "SpecializedUnitIds": [ 60, 61 ] }, { "Id": 84, @@ -396,12 +400,12 @@ { "Id": 86, "Name": "Синка", - "SpecializedUnitId": 66 + "SpecializedUnitIds": [ 66, 67 ] }, { "Id": 87, "Name": "Шакти", - "SpecializedUnitId": 56 + "SpecializedUnitIds": [ 56, 57 ] }, { "Id": 88, @@ -437,35 +441,37 @@ "SpecializedSpell": "StoneSkin", "Id": 95, "Name": "Дарксторн" - }, + } + , { "Id": 96, "Name": "Йог", - "SpecializedUnitId": 108 + "SpecializedUnitIds": [ 108, 109 ] }, { "Id": 97, - "Name": "Гурниссон" + "Name": "Гурниссон", + "SpecializedUnitIds": [ 158 ] }, { "Id": 98, "Name": "Жабаркас", - "SpecializedUnitId": 102 + "SpecializedUnitIds": [ 102, 103 ] }, { "Id": 99, "Name": "Шива", - "SpecializedUnitId": 106 + "SpecializedUnitIds": [ 106, 107 ] }, { "Id": 100, "Name": "Гретчин", - "SpecializedUnitId": 98 + "SpecializedUnitIds": [ 98, 99 ] }, { "Id": 101, "Name": "Креллион", - "SpecializedUnitId": 104 + "SpecializedUnitIds": [ 104, 105 ] }, { "SpecializedSecondarySkill": "Offense", @@ -475,7 +481,7 @@ { "Id": 103, "Name": "Тираксор", - "SpecializedUnitId": 100 + "SpecializedUnitIds": [ 100, 101 ] }, { "Id": 104, @@ -485,7 +491,7 @@ { "Id": 105, "Name": "Вей", - "SpecializedUnitId": 104 + "SpecializedUnitIds": [ 104, 105 ] }, { "Id": 106, @@ -516,17 +522,17 @@ { "Id": 112, "Name": "Брон", - "SpecializedUnitId": 90 + "SpecializedUnitIds": [ 90, 91 ] }, { "Id": 113, "Name": "Дракон", - "SpecializedUnitId": 84 + "SpecializedUnitIds": [ 84, 85 ] }, { "Id": 114, "Name": "Вистан", - "SpecializedUnitId": 86 + "SpecializedUnitIds": [ 86, 87 ] }, { "SpecializedSecondarySkill": "Armorer", @@ -536,21 +542,22 @@ { "Id": 116, "Name": "Алкин", - "SpecializedUnitId": 92 + "SpecializedUnitIds": [ 92, 93 ] }, { "Id": 117, "Name": "Корбак", - "SpecializedUnitId": 88 + "SpecializedUnitIds": [ 88, 89 ] }, { "Id": 118, - "Name": "Гервульф" + "Name": "Гервульф", + "SpecializedUnitIds": [ 158 ] }, { "Id": 119, "Name": "Брохильд", - "SpecializedUnitId": 94 + "SpecializedUnitIds": [ 94, 95 ] }, { "SpecializedSpell": "Weakness", @@ -590,42 +597,42 @@ { "Id": 128, "Name": "Пасис", - "SpecializedUnitId": 122 + "SpecializedUnitIds": [ 122, 123 ] }, { "Id": 129, "Name": "Тунар", - "SpecializedUnitId": 120 + "SpecializedUnitIds": [ 120, 121 ] }, { "Id": 130, "Name": "Игнисса", - "SpecializedUnitId": 118 + "SpecializedUnitIds": [ 118, 119 ] }, { "Id": 131, "Name": "Лакус", - "SpecializedUnitId": 116 + "SpecializedUnitIds": [ 116, 117 ] }, { "Id": 132, "Name": "Монер", - "SpecializedUnitId": 122 + "SpecializedUnitIds": [ 122, 123 ] }, { "Id": 133, "Name": "Эрдамон", - "SpecializedUnitId": 120 + "SpecializedUnitIds": [ 120, 121 ] }, { "Id": 134, "Name": "Фьюр", - "SpecializedUnitId": 118 + "SpecializedUnitIds": [ 118, 119 ] }, { "Id": 135, "Name": "Кальт", - "SpecializedUnitId": 116 + "SpecializedUnitIds": [ 116, 117 ] }, { "Id": 136, @@ -663,5 +670,78 @@ { "Id": 143, "Name": "Гриндан" + }, + { + "Id": 143, + "Name": "Cassiopeia", + "SpecializedUnitIds": [ 141, 141 ] + }, + { + "Id": 144, + "Name": "Corkes", + "SpecializedSecondarySkill": "Offense" + }, + { + "Id": 145, + "Name": "Jeremy", + "SpecializedUnitIds": [ 159 ] + }, + { + "Id": 146, + "Name": "Illor", + "SpecializedUnitIds": [ 148, 149 ] + }, + { + "Id": 147, + "Name": "Derek", + "SpecializedUnitIds": [ 143, 144 ] + }, + { + "Id": 148, + "Name": "Leena" + }, + { + "Id": 149, + "Name": "Anabel", + "SpecializedUnitIds": [ 145, 146, 147 ] + }, + { + "Id": 150, + "Name": "Miriam" + }, + { + "Id": 151, + "Name": "Casmetra", + "SpecializedUnitIds": [ 150, 151 ] + }, + { + "Id": 152, + "Name": "Eovacius" + }, + { + "Id": 153, + "Name": "Spint", + "SpecializedSecondarySkill": "Sorcery" + }, + { + "Id": 154, + "Name": "Andal" + }, + { + "Id": 155, + "Name": "Manfred", + "SpecializedSpell": "Fireball" + }, + { + "Id": 156, + "Name": "Zilare" + }, + { + "Id": 157, + "Name": "Astra" + }, + { + "Id": 157, + "Name": "Dargem" } ] \ No newline at end of file diff --git a/H3Calc/Data/terrains.json b/H3Calc/Data/terrains.json index 8c7405b..7f6a119 100644 --- a/H3Calc/Data/terrains.json +++ b/H3Calc/Data/terrains.json @@ -30,5 +30,9 @@ { "Id": 7, "Name": "Lava" + }, + { + "Id": 8, + "Name": "Highlands" } ] \ No newline at end of file diff --git a/H3Calc/Data/unit-portraits.png b/H3Calc/Data/unit-portraits.png index cfee423..22a95bb 100644 Binary files a/H3Calc/Data/unit-portraits.png and b/H3Calc/Data/unit-portraits.png differ diff --git a/H3Calc/Data/unit-portraits_big.png b/H3Calc/Data/unit-portraits_big.png new file mode 100644 index 0000000..fc27f5c Binary files /dev/null and b/H3Calc/Data/unit-portraits_big.png differ diff --git a/H3Calc/Data/unit-portraits_sod.png b/H3Calc/Data/unit-portraits_sod.png new file mode 100644 index 0000000..cfee423 Binary files /dev/null and b/H3Calc/Data/unit-portraits_sod.png differ diff --git a/H3Calc/Data/units.json b/H3Calc/Data/units.json index 702d0d3..f7e04b3 100644 --- a/H3Calc/Data/units.json +++ b/H3Calc/Data/units.json @@ -1514,7 +1514,7 @@ "MaxDamage": 2, "Health": 3 }, - "NativeTerrainId": 2 + "NativeTerrainId": 8 }, { "Id": 113, @@ -1527,7 +1527,7 @@ "MaxDamage": 3, "Health": 3 }, - "NativeTerrainId": 2 + "NativeTerrainId": 8 }, { "Id": 114, @@ -1540,7 +1540,7 @@ "MaxDamage": 8, "Health": 25 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "VulnerableSpells": [ "LightningBolt", "ChainLightning", @@ -1561,7 +1561,7 @@ "MaxDamage": 8, "Health": 25 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "IsRanged": true, "VulnerableSpells": [ "LightningBolt", @@ -1583,7 +1583,7 @@ "MaxDamage": 7, "Health": 30 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "VulnerableSpells": [ "Inferno", "Fireball", @@ -1605,7 +1605,7 @@ "MaxDamage": 7, "Health": 30 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "IsRanged": true, "VulnerableSpells": [ "Inferno", @@ -1628,12 +1628,12 @@ "MaxDamage": 6, "Health": 35 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "VulnerableSpells": [ "IceBolt", "FrostRing" ], - "ImmuneMagic" : "FireMagic" + "ImmuneMagic": "FireMagic" }, { "Id": 119, @@ -1646,12 +1646,12 @@ "MaxDamage": 6, "Health": 35 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "VulnerableSpells": [ "IceBolt", "FrostRing" ], - "ImmuneMagic" : "FireMagic" + "ImmuneMagic": "FireMagic" }, { "Id": 120, @@ -1664,9 +1664,9 @@ "MaxDamage": 8, "Health": 40 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "VulnerableSpells": [ - "MeteorShower" + "MeteorShower" ], "ImmuneSpells": [ "LightningBolt", @@ -1685,9 +1685,9 @@ "MaxDamage": 10, "Health": 40 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "VulnerableSpells": [ - "MeteorShower" + "MeteorShower" ], "ImmuneSpells": [ "LightningBolt", @@ -1706,7 +1706,7 @@ "MaxDamage": 20, "Health": 75 }, - "NativeTerrainId": 2 + "NativeTerrainId": 8 }, { "Id": 123, @@ -1719,7 +1719,7 @@ "MaxDamage": 25, "Health": 80 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "ImmuneSpellLevel": 5 }, { @@ -1733,7 +1733,7 @@ "MaxDamage": 40, "Health": 150 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "ImmuneMagic": "FireMagic" }, { @@ -1747,7 +1747,7 @@ "MaxDamage": 40, "Health": 200 }, - "NativeTerrainId": 2, + "NativeTerrainId": 8, "ImmuneMagic": "FireMagic" }, { @@ -1933,5 +1933,269 @@ "MaxDamage": 80, "Health": 1000 } + }, + { + "Id": 141, + "Name": "Nymphs", + "Level": 1, + "InitialStats": { + "Attack": 5, + "Defense": 2, + "MinDamage": 1, + "MaxDamage": 2, + "Health": 4 + }, + "NativeTerrainId": 4 + }, + { + "Id": 142, + "Name": "Oceanids", + "Level": 1, + "InitialStats": { + "Attack": 6, + "Defense": 2, + "MinDamage": 1, + "MaxDamage": 3, + "Health": 4 + }, + "NativeTerrainId": 4 + }, + { + "Id": 143, + "Name": "Crew Mates", + "Level": 2, + "InitialStats": { + "Attack": 7, + "Defense": 4, + "MinDamage": 2, + "MaxDamage": 4, + "Health": 15 + }, + "NativeTerrainId": 4 + }, + { + "Id": 144, + "Name": "Seamen", + "Level": 2, + "InitialStats": { + "Attack": 8, + "Defense": 6, + "MinDamage": 3, + "MaxDamage": 4, + "Health": 15 + }, + "NativeTerrainId": 4 + }, + { + "Id": 145, + "Name": "Pirates", + "Level": 3, + "InitialStats": { + "Attack": 8, + "Defense": 6, + "MinDamage": 3, + "MaxDamage": 7, + "Health": 15 + }, + "IsRanged": true, + "NativeTerrainId": 4 + }, + { + "Id": 146, + "Name": "Corsairs", + "Level": 3, + "InitialStats": { + "Attack": 10, + "Defense": 8, + "MinDamage": 3, + "MaxDamage": 7, + "Health": 15 + }, + "IsRanged": true, + "NativeTerrainId": 4 + }, + { + "Id": 147, + "Name": "Sea Dogs", + "Level": 3, + "InitialStats": { + "Attack": 12, + "Defense": 11, + "MinDamage": 3, + "MaxDamage": 7, + "Health": 15 + }, + "IsRanged": true, + "NativeTerrainId": 4 + }, + { + "Id": 148, + "Name": "Stormbirds", + "Level": 4, + "InitialStats": { + "Attack": 10, + "Defense": 8, + "MinDamage": 6, + "MaxDamage": 9, + "Health": 30 + }, + "NativeTerrainId": 4 + }, + { + "Id": 149, + "Name": "Ayssids", + "Level": 4, + "InitialStats": { + "Attack": 11, + "Defense": 8, + "MinDamage": 6, + "MaxDamage": 10, + "Health": 30 + }, + "NativeTerrainId": 4 + }, + { + "Id": 150, + "Name": "Sea Witches", + "Level": 5, + "InitialStats": { + "Attack": 12, + "Defense": 7, + "MinDamage": 10, + "MaxDamage": 14, + "Health": 35 + }, + "IsRanged": true, + "NativeTerrainId": 4 + }, + { + "Id": 151, + "Name": "Sorceresses", + "Level": 5, + "InitialStats": { + "Attack": 12, + "Defense": 9, + "MinDamage": 10, + "MaxDamage": 16, + "Health": 35 + }, + "IsRanged": true, + "NativeTerrainId": 4 + }, + { + "Id": 152, + "Name": "Nix", + "Level": 6, + "InitialStats": { + "Attack": 13, + "Defense": 16, + "MinDamage": 18, + "MaxDamage": 22, + "Health": 80 + }, + "NativeTerrainId": 4 + }, + { + "Id": 153, + "Name": "Nix Warriors", + "Level": 6, + "InitialStats": { + "Attack": 14, + "Defense": 17, + "MinDamage": 18, + "MaxDamage": 22, + "Health": 90 + }, + "NativeTerrainId": 4 + }, + { + "Id": 154, + "Name": "Sea Serpents", + "Level": 7, + "InitialStats": { + "Attack": 22, + "Defense": 16, + "MinDamage": 30, + "MaxDamage": 55, + "Health": 180 + }, + "NativeTerrainId": 4 + }, + { + "Id": 155, + "Name": "Haspids", + "Level": 7, + "InitialStats": { + "Attack": 29, + "Defense": 20, + "MinDamage": 30, + "MaxDamage": 55, + "Health": 300 + }, + "NativeTerrainId": 4 + }, + + { + "Id": 156, + "Name": "Satyr", + "Level": 4, + "InitialStats": { + "Attack": 10, + "Defense": 11, + "MinDamage": 6, + "MaxDamage": 10, + "Health": 35 + } + }, + + { + "Id": 157, + "Name": "Fangarm", + "Level": 5, + "InitialStats": { + "Attack": 12, + "Defense": 12, + "MinDamage": 8, + "MaxDamage": 12, + "Health": 50 + } + }, + { + "Id": 158, + "Name": "Ballista", + "Level": 5, + "InitialStats": { + "Attack": 10, + "Defense": 10, + "MinDamage": 2, + "MaxDamage": 3, + "Health": 250 + }, + "IsRanged": true + }, + { + "Id": 159, + "Name": "Cannon", + "Level": 6, + "InitialStats": { + "Attack": 20, + "Defense": 10, + "MinDamage": 4, + "MaxDamage": 7, + "Health": 250 + }, + "IsRanged": true + }, + { + "Id": 160, + "Name": "Лепрекон", + "Level": 2, + "InitialStats": { + "Attack": 8, + "Defense": 5, + "MinDamage": 3, + "MaxDamage": 5, + "Health": 15 + } } ] \ No newline at end of file diff --git a/H3Calc/Engine/CombatDamageCalculator.cs b/H3Calc/Engine/CombatDamageCalculator.cs index d9bcbd6..10e1787 100644 --- a/H3Calc/Engine/CombatDamageCalculator.cs +++ b/H3Calc/Engine/CombatDamageCalculator.cs @@ -126,6 +126,11 @@ private int PerformCalculation(int baseDamage, CombatDamageModifier damageModifi result *= totalBonus; result = Math.Floor(result); + if (damageModifier.BaseDamageMultiplier != 0) + { + result += baseDamage * (damageModifier.BaseDamageMultiplier - 1); + } + foreach (double damageReduction in damageModifier.DamageReductions) { result *= (1 - damageReduction); @@ -204,6 +209,7 @@ public class CombatDamageModifier { public List DamageBonuses { get; set; } public List DamageReductions { get; set; } + public int BaseDamageMultiplier { get; set; } //for war machines and maybe luck public CombatDamageModifier() { diff --git a/H3Calc/Engine/Hero.cs b/H3Calc/Engine/Hero.cs index 735f49b..2d80a3e 100644 --- a/H3Calc/Engine/Hero.cs +++ b/H3Calc/Engine/Hero.cs @@ -46,15 +46,14 @@ public Type SpecializedSpell SpecializedSpellString = Utils.StringFromType(value); } } - - [DefaultValue(-1)] - public int SpecializedUnitId { get; set; } + [DefaultValue(null)] + public int[] SpecializedUnitIds { get; set; } public Hero() { SpecializedSecondarySkill = null; SpecializedSpell = null; - SpecializedUnitId = -1; + SpecializedUnitIds = null; } } @@ -129,7 +128,7 @@ public SpellCasterStats SpellCasterStatsForSpell(Spell spell) } } - if (spell.GetType() == Hero.SpecializedSpell) + if (Hero != null && spell.GetType() == Hero.SpecializedSpell) { stats.IsSpecialized = true; stats.SpecializationLevel = Level; @@ -140,10 +139,17 @@ public SpellCasterStats SpellCasterStatsForSpell(Spell spell) public void ApplyPermanently(Unit unit, UnitStats modifiedStats) { + if (unit.Id == 158 || unit.Id == 159) // 158 - Ballista, 159 - Cannon + { + modifiedStats.MinDamage *= Attack + 1; + modifiedStats.MaxDamage *= Attack + 1; + + } + modifiedStats.Attack += Attack; modifiedStats.Defense += Defense; - if (Hero != null && Hero.SpecializedUnitId >= 0) + if (Hero != null && Hero.SpecializedUnitIds != null && Hero.SpecializedUnitIds.Length > 0) { ApplyUnitSpecialization(unit, modifiedStats); } @@ -151,14 +157,12 @@ public void ApplyPermanently(Unit unit, UnitStats modifiedStats) private void ApplyUnitSpecialization(Unit unit, UnitStats modifiedStats) { - // TODO: this is dirty, should handle upgraded units in a better way. - if ((unit.Id != Hero.SpecializedUnitId) && (unit.Id != Hero.SpecializedUnitId + 1)) + if (!Hero.SpecializedUnitIds.Contains(unit.Id)) { return; } // Special formulae first. - // Water/Ice Elementals if ((unit.Id == 116) || (unit.Id == 117)) { @@ -167,7 +171,7 @@ private void ApplyUnitSpecialization(Unit unit, UnitStats modifiedStats) } // Fire/Energy Elementals - if ((unit.Id == 116) || (unit.Id == 117)) + if ((unit.Id == 118) || (unit.Id == 119)) { modifiedStats.Attack += 1; modifiedStats.Defense += 2; diff --git a/H3Calc/Engine/ModifierSpells.cs b/H3Calc/Engine/ModifierSpells.cs index 0ce7967..98e55d8 100644 --- a/H3Calc/Engine/ModifierSpells.cs +++ b/H3Calc/Engine/ModifierSpells.cs @@ -61,12 +61,13 @@ public override void ApplyPermanently(Unit unit, UnitStats modifiedStats) { if (CasterStats.SkillLevel <= SecondarySkillLevel.Basic) { - modifiedStats.MinDamage = modifiedStats.MaxDamage = unit.InitialStats.MinDamage; + modifiedStats.MaxDamage = modifiedStats.MinDamage; } else { - modifiedStats.MinDamage = modifiedStats.MaxDamage = unit.InitialStats.MinDamage - 1; - } + + modifiedStats.MinDamage = modifiedStats.MaxDamage = (modifiedStats.MinDamage - 1 < 1) ? 1 : modifiedStats.MinDamage - 1; // min damage can't be less than 1 + } } } @@ -168,11 +169,11 @@ public override void ApplyPermanently(Unit unit, UnitStats modifiedStats) { if (CasterStats.SkillLevel <= SecondarySkillLevel.Basic) { - modifiedStats.MinDamage = modifiedStats.MaxDamage = unit.InitialStats.MaxDamage; + modifiedStats.MinDamage = modifiedStats.MaxDamage; } else { - modifiedStats.MinDamage = modifiedStats.MaxDamage = unit.InitialStats.MaxDamage + 1; + modifiedStats.MinDamage = modifiedStats.MaxDamage = modifiedStats.MaxDamage + 1; } } diff --git a/H3Calc/Engine/SecondarySkills.cs b/H3Calc/Engine/SecondarySkills.cs index 77d86b7..6529932 100644 --- a/H3Calc/Engine/SecondarySkills.cs +++ b/H3Calc/Engine/SecondarySkills.cs @@ -176,4 +176,34 @@ public override void ApplySpell(SpellDamageCalculatorData data, SpellDamageModif damageModifier.DamageMultipliers.Add(multiplier); } } + + public class Artillery : SecondarySkill + { + public override void ApplyOnAttack(AttackData attackData, CombatDamageModifier damageModifier) + { + if (SkillLevel == SecondarySkillLevel.None) + { + return; + } + + if (attackData.Attacker.Id == 158) //158 - Ballista + { + if (SkillLevel == SecondarySkillLevel.Expert) + { + damageModifier.BaseDamageMultiplier = 2; + } + } + else if (attackData.Attacker.Id == 159) //159 - Cannon + { + if (SkillLevel == SecondarySkillLevel.Advanced) + { + damageModifier.BaseDamageMultiplier = 2; + } + else if (SkillLevel == SecondarySkillLevel.Expert) + { + damageModifier.BaseDamageMultiplier = 3; + } + } + } + } } diff --git a/H3Calc/Engine/Unit.cs b/H3Calc/Engine/Unit.cs index 1111148..b80478b 100644 --- a/H3Calc/Engine/Unit.cs +++ b/H3Calc/Engine/Unit.cs @@ -134,6 +134,15 @@ public void ApplyPermanently(Unit unit, UnitStats modifiedStats) public void ApplyOnAttack(AttackData attackData, UnitStats modifiedStats) { + if (attackData.Defender.Id == 152) // Nix + { + modifiedStats.Attack = (int)Math.Round(modifiedStats.Attack * 0.7); + } + + if (attackData.Defender.Id == 153) // Nix Warrior + { + modifiedStats.Attack = (int)Math.Round(modifiedStats.Attack * 0.4); + } } public void ApplyOnDefense(AttackData attackData, UnitStats modifiedStats) diff --git a/H3Calc/H3Calc.csproj b/H3Calc/H3Calc.csproj index 424d294..b0e345d 100644 --- a/H3Calc/H3Calc.csproj +++ b/H3Calc/H3Calc.csproj @@ -161,6 +161,7 @@ + diff --git a/H3Calc/PickPanel.Designer.cs b/H3Calc/PickPanel.Designer.cs index a1a343c..b9faeb0 100644 --- a/H3Calc/PickPanel.Designer.cs +++ b/H3Calc/PickPanel.Designer.cs @@ -65,6 +65,8 @@ private void InitializeComponent() this.HeroDefenseUpDn = new H3Calc.ImmediateNumericUpDown(); this.HeroLevelUpDn = new H3Calc.ImmediateNumericUpDown(); this.HeroAttackUpDn = new H3Calc.ImmediateNumericUpDown(); + this.HeroArtilleryComboBox = new System.Windows.Forms.ComboBox(); + this.HeroArtilleryLbl = new System.Windows.Forms.Label(); this.StandardModePanel.SuspendLayout(); this.ScientificModePanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.HeroDefenseUpDn)).BeginInit(); @@ -379,6 +381,8 @@ private void InitializeComponent() // // StandardModePanel // + this.StandardModePanel.Controls.Add(this.HeroArtilleryLbl); + this.StandardModePanel.Controls.Add(this.HeroArtilleryComboBox); this.StandardModePanel.Controls.Add(this.HeroEarthComboBox); this.StandardModePanel.Controls.Add(this.HeroWaterLbl); this.StandardModePanel.Controls.Add(this.HeroFireComboBox); @@ -453,6 +457,24 @@ private void InitializeComponent() this.HeroAttackUpDn.Size = new System.Drawing.Size(42, 20); this.HeroAttackUpDn.TabIndex = 54; // + // HeroArtilleryComboBox + // + this.HeroArtilleryComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.HeroArtilleryComboBox.FormattingEnabled = true; + this.HeroArtilleryComboBox.Location = new System.Drawing.Point(52, 67); + this.HeroArtilleryComboBox.Name = "HeroArtilleryComboBox"; + this.HeroArtilleryComboBox.Size = new System.Drawing.Size(77, 21); + this.HeroArtilleryComboBox.TabIndex = 87; + // + // HeroArtilleryLbl + // + this.HeroArtilleryLbl.AutoSize = true; + this.HeroArtilleryLbl.Location = new System.Drawing.Point(3, 70); + this.HeroArtilleryLbl.Name = "HeroArtilleryLbl"; + this.HeroArtilleryLbl.Size = new System.Drawing.Size(40, 13); + this.HeroArtilleryLbl.TabIndex = 88; + this.HeroArtilleryLbl.Text = "Artillery"; + // // PickPanel // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -522,5 +544,7 @@ private void InitializeComponent() public System.Windows.Forms.Label HeroDefenseLbl; private System.Windows.Forms.Panel StandardModePanel; private System.Windows.Forms.Panel ScientificModePanel; + public System.Windows.Forms.Label HeroArtilleryLbl; + public System.Windows.Forms.ComboBox HeroArtilleryComboBox; } } diff --git a/H3Calc/PickPanel.cs b/H3Calc/PickPanel.cs index eb66a1a..46cc8a3 100644 --- a/H3Calc/PickPanel.cs +++ b/H3Calc/PickPanel.cs @@ -120,6 +120,8 @@ public PickPanel() HeroOffenseComboBox, HeroArcheryLbl, HeroArcheryComboBox, + HeroArtilleryLbl, + HeroArtilleryComboBox, HeroArmorerLbl, HeroArmorerComboBox }; @@ -141,6 +143,7 @@ public PickPanel() { HeroOffenseComboBox, HeroArcheryComboBox, + HeroArtilleryComboBox, HeroArmorerComboBox, HeroAirComboBox, @@ -182,6 +185,7 @@ public PickPanel() HeroDefenseUpDn.ValueChanged += ControlValueChanged; HeroOffenseComboBox.SelectedValueChanged += ControlValueChanged; HeroArcheryComboBox.SelectedValueChanged += ControlValueChanged; + HeroArtilleryComboBox.SelectedValueChanged += ControlValueChanged; HeroArmorerComboBox.SelectedValueChanged += ControlValueChanged; HeroAirComboBox.SelectedValueChanged += ControlValueChanged; HeroFireComboBox.SelectedValueChanged += ControlValueChanged; @@ -279,6 +283,7 @@ void UpdateDataFromControls() stats.SetLevelForSecondarySkillType(typeof(Offense), (SecondarySkillLevel)HeroOffenseComboBox.SelectedValue); stats.SetLevelForSecondarySkillType(typeof(Archery), (SecondarySkillLevel)HeroArcheryComboBox.SelectedValue); + stats.SetLevelForSecondarySkillType(typeof(Artillery), (SecondarySkillLevel)HeroArtilleryComboBox.SelectedValue); stats.SetLevelForSecondarySkillType(typeof(Armorer), (SecondarySkillLevel)HeroArmorerComboBox.SelectedValue); if (Mode == ApplicationMode.Scientific) @@ -420,8 +425,10 @@ private ComboBox ComboBoxForSecondarySkill(SecondarySkill skill) if (skillType == typeof(Offense)) { return HeroOffenseComboBox; } if (skillType == typeof(Archery)) { return HeroArcheryComboBox; } + if (skillType == typeof(Artillery)) { return HeroArtilleryComboBox; } if (skillType == typeof(Armorer)) { return HeroArmorerComboBox; } + if (skillType == typeof(AirMagic)) { return HeroAirComboBox; } if (skillType == typeof(FireMagic)) { return HeroFireComboBox; } if (skillType == typeof(EarthMagic)) { return HeroEarthComboBox; } diff --git a/H3Calc/UnitPicker.Designer.cs b/H3Calc/UnitPicker.Designer.cs index 8fc8231..3242022 100644 --- a/H3Calc/UnitPicker.Designer.cs +++ b/H3Calc/UnitPicker.Designer.cs @@ -37,7 +37,7 @@ private void InitializeComponent() this.pictureBox1.Image = global::H3Calc.Properties.Resources.unit_portraits; this.pictureBox1.Location = new System.Drawing.Point(0, 0); this.pictureBox1.Name = "pictureBox1"; - this.pictureBox1.Size = new System.Drawing.Size(950, 695); + this.pictureBox1.Size = new System.Drawing.Size(860, 768); this.pictureBox1.TabIndex = 0; this.pictureBox1.TabStop = false; this.pictureBox1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseClick); @@ -47,10 +47,12 @@ private void InitializeComponent() // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(950, 695); + this.AutoScroll = true; + this.AutoScrollMinSize = new System.Drawing.Size(0, 760); + this.ClientSize = new System.Drawing.Size(827, 712); this.Controls.Add(this.pictureBox1); this.Cursor = System.Windows.Forms.Cursors.Default; - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "UnitPicker"; diff --git a/H3Calc/UnitPicker.cs b/H3Calc/UnitPicker.cs index 6cc55c7..ebb4129 100644 --- a/H3Calc/UnitPicker.cs +++ b/H3Calc/UnitPicker.cs @@ -15,23 +15,61 @@ namespace H3Calc { public partial class UnitPicker : Form { - public List Units { get; set; } + public List Units { get; set; } + + public Dictionary RowColumnsCount = new Dictionary() + { + {0, 13},{1, 13 },{2, 13 },{3, 13 },{4, 13 },{5, 13 },{6, 13 },{7, 13 },{8, 13 }, + {9, 14 }, + {10, 14 }, + {11, 4 } + }; + + public bool UnitExists(int clickedRow, int clickedColumn) + { + if (!RowColumnsCount.ContainsKey(clickedRow) || (clickedColumn > RowColumnsCount[clickedRow] ) ) + { + return false; + } + + return true; + } + + public int CalculateUnitId(int clickedRow, int clickedColumn) + { + var unitId = 0; + + for (var i = 0; i <= clickedRow; i++) + { + if (i < clickedRow) + { + unitId += (i == 0) ? RowColumnsCount[i] : RowColumnsCount[i] + 1; + } + else + { + unitId += (i == 0) ? clickedColumn : clickedColumn + 1; + } + } + + return unitId; + } + public event EventHandler UnitPicked; public UnitPicker(List units) { InitializeComponent(); - this.ClientSize = new Size(950, 695); + this.ClientSize = new Size(860, 768); Units = units; } private Unit UnitFromPicker(int x, int y) { - const int kPortraitWidth = 58; - const int kPortraitHeight = 64; - const int kPadding = 5; + const int kPortraitWidth = 50; + const int kPortraitHeight = 55; + const int kPadding = 4; if ((x % (kPortraitWidth + kPadding) < kPadding) || (y % (kPortraitHeight + kPadding) < kPadding)) { @@ -41,12 +79,12 @@ private Unit UnitFromPicker(int x, int y) int row = y / (kPortraitHeight + kPadding); int column = x / (kPortraitWidth + kPadding); - if (row != 9 && column == 14) + if (!UnitExists(row, column)) { return null; } - int unitId = row * 14 + column; + int unitId = CalculateUnitId(row, column); return Units.FirstOrDefault(u => u.Id == unitId); } @@ -57,7 +95,7 @@ private void pictureBox1_MouseMove(object sender, MouseEventArgs e) } private void pictureBox1_MouseClick(object sender, MouseEventArgs e) - { + { Unit unit = UnitFromPicker(e.X, e.Y); if (unit != null && UnitPicked != null)