diff --git a/includes/defines.php b/includes/defines.php
index 3c5ab8203..6db2a4998 100644
--- a/includes/defines.php
+++ b/includes/defines.php
@@ -422,6 +422,52 @@
define('STAT_INTELLECT', 3);
define('STAT_SPIRIT', 4);
+// ItemMods
+define('ITEM_MOD_MANA', 0);
+define('ITEM_MOD_HEALTH', 1);
+define('ITEM_MOD_AGILITY', 3);
+define('ITEM_MOD_STRENGTH', 4);
+define('ITEM_MOD_INTELLECT', 5);
+define('ITEM_MOD_SPIRIT', 6);
+define('ITEM_MOD_STAMINA', 7);
+define('ITEM_MOD_DEFENSE_SKILL_RATING', 12);
+define('ITEM_MOD_DODGE_RATING', 13);
+define('ITEM_MOD_PARRY_RATING', 14);
+define('ITEM_MOD_BLOCK_RATING', 15);
+define('ITEM_MOD_HIT_MELEE_RATING', 16);
+define('ITEM_MOD_HIT_RANGED_RATING', 17);
+define('ITEM_MOD_HIT_SPELL_RATING', 18);
+define('ITEM_MOD_CRIT_MELEE_RATING', 19);
+define('ITEM_MOD_CRIT_RANGED_RATING', 20);
+define('ITEM_MOD_CRIT_SPELL_RATING', 21);
+define('ITEM_MOD_HIT_TAKEN_MELEE_RATING', 22);
+define('ITEM_MOD_HIT_TAKEN_RANGED_RATING', 23);
+define('ITEM_MOD_HIT_TAKEN_SPELL_RATING', 24);
+define('ITEM_MOD_CRIT_TAKEN_MELEE_RATING', 25);
+define('ITEM_MOD_CRIT_TAKEN_RANGED_RATING', 26);
+define('ITEM_MOD_CRIT_TAKEN_SPELL_RATING', 27);
+define('ITEM_MOD_HASTE_MELEE_RATING', 28);
+define('ITEM_MOD_HASTE_RANGED_RATING', 29);
+define('ITEM_MOD_HASTE_SPELL_RATING', 30);
+define('ITEM_MOD_HIT_RATING', 31);
+define('ITEM_MOD_CRIT_RATING', 32);
+define('ITEM_MOD_HIT_TAKEN_RATING', 33);
+define('ITEM_MOD_CRIT_TAKEN_RATING', 34);
+define('ITEM_MOD_RESILIENCE_RATING', 35);
+define('ITEM_MOD_HASTE_RATING', 36);
+define('ITEM_MOD_EXPERTISE_RATING', 37);
+define('ITEM_MOD_ATTACK_POWER', 38);
+define('ITEM_MOD_RANGED_ATTACK_POWER', 39);
+define('ITEM_MOD_FERAL_ATTACK_POWER', 40);
+define('ITEM_MOD_SPELL_HEALING_DONE', 41);
+define('ITEM_MOD_SPELL_DAMAGE_DONE', 42);
+define('ITEM_MOD_MANA_REGENERATION', 43);
+define('ITEM_MOD_ARMOR_PENETRATION_RATING', 44);
+define('ITEM_MOD_SPELL_POWER', 45);
+define('ITEM_MOD_HEALTH_REGEN', 46);
+define('ITEM_MOD_SPELL_PENETRATION', 47);
+define('ITEM_MOD_BLOCK_VALUE', 48);
+
// Powers
define('POWER_MANA', 0);
define('POWER_RAGE', 1);
@@ -777,70 +823,16 @@
define('ITEM_FLAG_ACCOUNTBOUND', 0x08000000);
define('ITEM_FLAG_MILLABLE', 0x20000000);
-// ItemMod (differ slightly from client, see g_statToJson)
-define('ITEM_MOD_WEAPON_DMG', 0); // < custom
-define('ITEM_MOD_MANA', 1);
-define('ITEM_MOD_HEALTH', 2);
-define('ITEM_MOD_AGILITY', 3); // stats v
-define('ITEM_MOD_STRENGTH', 4);
-define('ITEM_MOD_INTELLECT', 5);
-define('ITEM_MOD_SPIRIT', 6);
-define('ITEM_MOD_STAMINA', 7);
-define('ITEM_MOD_ENERGY', 8); // powers v
-define('ITEM_MOD_RAGE', 9);
-define('ITEM_MOD_FOCUS', 10);
-define('ITEM_MOD_RUNIC_POWER', 11);
-define('ITEM_MOD_DEFENSE_SKILL_RATING', 12); // ratings v
-define('ITEM_MOD_DODGE_RATING', 13);
-define('ITEM_MOD_PARRY_RATING', 14);
-define('ITEM_MOD_BLOCK_RATING', 15);
-define('ITEM_MOD_HIT_MELEE_RATING', 16);
-define('ITEM_MOD_HIT_RANGED_RATING', 17);
-define('ITEM_MOD_HIT_SPELL_RATING', 18);
-define('ITEM_MOD_CRIT_MELEE_RATING', 19);
-define('ITEM_MOD_CRIT_RANGED_RATING', 20);
-define('ITEM_MOD_CRIT_SPELL_RATING', 21);
-define('ITEM_MOD_HIT_TAKEN_MELEE_RATING', 22);
-define('ITEM_MOD_HIT_TAKEN_RANGED_RATING', 23);
-define('ITEM_MOD_HIT_TAKEN_SPELL_RATING', 24);
-define('ITEM_MOD_CRIT_TAKEN_MELEE_RATING', 25);
-define('ITEM_MOD_CRIT_TAKEN_RANGED_RATING', 26);
-define('ITEM_MOD_CRIT_TAKEN_SPELL_RATING', 27);
-define('ITEM_MOD_HASTE_MELEE_RATING', 28);
-define('ITEM_MOD_HASTE_RANGED_RATING', 29);
-define('ITEM_MOD_HASTE_SPELL_RATING', 30);
-define('ITEM_MOD_HIT_RATING', 31);
-define('ITEM_MOD_CRIT_RATING', 32);
-define('ITEM_MOD_HIT_TAKEN_RATING', 33);
-define('ITEM_MOD_CRIT_TAKEN_RATING', 34);
-define('ITEM_MOD_RESILIENCE_RATING', 35);
-define('ITEM_MOD_HASTE_RATING', 36);
-define('ITEM_MOD_EXPERTISE_RATING', 37);
-define('ITEM_MOD_ATTACK_POWER', 38);
-define('ITEM_MOD_RANGED_ATTACK_POWER', 39);
-define('ITEM_MOD_FERAL_ATTACK_POWER', 40);
-define('ITEM_MOD_SPELL_HEALING_DONE', 41); // deprecated
-define('ITEM_MOD_SPELL_DAMAGE_DONE', 42); // deprecated
-define('ITEM_MOD_MANA_REGENERATION', 43);
-define('ITEM_MOD_ARMOR_PENETRATION_RATING', 44);
-define('ITEM_MOD_SPELL_POWER', 45);
-define('ITEM_MOD_HEALTH_REGEN', 46);
-define('ITEM_MOD_SPELL_PENETRATION', 47);
-define('ITEM_MOD_BLOCK_VALUE', 48);
-// define('ITEM_MOD_MASTERY_RATING', 49);
-define('ITEM_MOD_ARMOR', 50); // resistances v
-define('ITEM_MOD_FIRE_RESISTANCE', 51);
-define('ITEM_MOD_FROST_RESISTANCE', 52);
-define('ITEM_MOD_HOLY_RESISTANCE', 53);
-define('ITEM_MOD_SHADOW_RESISTANCE', 54);
-define('ITEM_MOD_NATURE_RESISTANCE', 55);
-define('ITEM_MOD_ARCANE_RESISTANCE', 56); // custom v
-define('ITEM_MOD_FIRE_POWER', 57);
-define('ITEM_MOD_FROST_POWER', 58);
-define('ITEM_MOD_HOLY_POWER', 59);
-define('ITEM_MOD_SHADOW_POWER', 60);
-define('ITEM_MOD_NATURE_POWER', 61);
-define('ITEM_MOD_ARCANE_POWER', 62);
+// ItemEnchantment types
+define('ENCHANTMENT_TYPE_NONE', 0);
+define('ENCHANTMENT_TYPE_COMBAT_SPELL', 1);
+define('ENCHANTMENT_TYPE_DAMAGE', 2);
+define('ENCHANTMENT_TYPE_EQUIP_SPELL', 3);
+define('ENCHANTMENT_TYPE_RESISTANCE', 4);
+define('ENCHANTMENT_TYPE_STAT', 5);
+define('ENCHANTMENT_TYPE_TOTEM', 6);
+define('ENCHANTMENT_TYPE_USE_SPELL', 7);
+define('ENCHANTMENT_TYPE_PRISMATIC_SOCKET', 8);
// Spell Effects and Auras
define('SPELL_EFFECT_NONE', 0);
diff --git a/includes/game.php b/includes/game.php
index 44e4f158d..532c065b6 100644
--- a/includes/game.php
+++ b/includes/game.php
@@ -33,17 +33,6 @@ class Game
null, 'warrior', 'paladin', 'hunter', 'rogue', 'priest', 'deathknight', 'shaman', 'mage', 'warlock', null, 'druid'
);
- private static $combatRatingToItemMod = array( // zero-indexed idx:CR; val:Mod
- null, 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 25, 26, 27, 28,
- 29, 30, null, null, null, 37, 44
- );
-
- public static $lvlIndepRating = array( // rating doesn't scale with level
- ITEM_MOD_MANA, ITEM_MOD_HEALTH, ITEM_MOD_ATTACK_POWER, ITEM_MOD_MANA_REGENERATION, ITEM_MOD_SPELL_POWER,
- ITEM_MOD_HEALTH_REGEN, ITEM_MOD_SPELL_PENETRATION, ITEM_MOD_BLOCK_VALUE
- );
-
public static $questClasses = array(
-2 => [ 0],
0 => [ 1, 3, 4, 8, 9, 10, 11, 12, 25, 28, 33, 36, 38, 40, 41, 44, 45, 46, 47, 51, 85, 130, 132, 139, 154, 267, 1497, 1519, 1537, 2257, 3430, 3431, 3433, 3487, 4080, 4298],
@@ -146,19 +135,6 @@ class Game
'meta', 'red', 'yellow', 'blue'
);
- // 'replicates' $WH.g_statToJson
- public static $itemMods = array( // zero-indexed; "mastrtng": unused mastery; _[a-z] => taken mods..
- 'dmg', 'mana', 'health', 'agi', 'str', 'int', 'spi',
- 'sta', 'energy', 'rage', 'focus', 'runicpwr', 'defrtng', 'dodgertng',
- 'parryrtng', 'blockrtng', 'mlehitrtng', 'rgdhitrtng', 'splhitrtng', 'mlecritstrkrtng', 'rgdcritstrkrtng',
- 'splcritstrkrtng', '_mlehitrtng', '_rgdhitrtng', '_splhitrtng', '_mlecritstrkrtng', '_rgdcritstrkrtng', '_splcritstrkrtng',
- 'mlehastertng', 'rgdhastertng', 'splhastertng', 'hitrtng', 'critstrkrtng', '_hitrtng', '_critstrkrtng',
- 'resirtng', 'hastertng', 'exprtng', 'atkpwr', 'rgdatkpwr', 'feratkpwr', 'splheal',
- 'spldmg', 'manargn', 'armorpenrtng', 'splpwr', 'healthrgn', 'splpen', 'block', // ITEM_MOD_BLOCK_VALUE
- 'mastrtng', 'armor', 'firres', 'frores', 'holres', 'shares', 'natres',
- 'arcres', 'firsplpwr', 'frosplpwr', 'holsplpwr', 'shasplpwr', 'natsplpwr', 'arcsplpwr'
- );
-
public static $class2SpellFamily = array(
// null Warrior Paladin Hunter Rogue Priest DK Shaman Mage Warlock null Druid
null, 4, 10, 9, 8, 6, 15, 11, 3, 5, null, 7
@@ -171,31 +147,6 @@ class Game
4273 => 6, 4277 => 3, 4395 => 2, 4494 => 2, 4722 => 2, 4812 => 8
);
- public static function itemModByRatingMask($mask)
- {
- if (($mask & 0x1C000) == 0x1C000) // special case resilience
- return ITEM_MOD_RESILIENCE_RATING;
-
- if (($mask & 0x00E0) == 0x00E0) // hit rating - all subcats (mle, rgd, spl)
- return ITEM_MOD_HIT_RATING;
-
- if (($mask & 0x0700) == 0x0700) // crit rating - all subcats (mle, rgd, spl)
- return ITEM_MOD_CRIT_RATING;
-
- for ($j = 0; $j < count(self::$combatRatingToItemMod); $j++)
- {
- if (!self::$combatRatingToItemMod[$j])
- continue;
-
- if (!($mask & (1 << $j)))
- continue;
-
- return self::$combatRatingToItemMod[$j];
- }
-
- return 0;
- }
-
public static function sideByRaceMask($race)
{
// Any
diff --git a/includes/kernel.php b/includes/kernel.php
index be8a6ae54..7a35b476e 100644
--- a/includes/kernel.php
+++ b/includes/kernel.php
@@ -23,6 +23,7 @@
require_once 'includes/defines.php';
+require_once 'includes/stats.class.php'; // Game entity statistics conversion
require_once 'includes/libs/DbSimple/Generic.php'; // Libraray: http://en.dklab.ru/lib/DbSimple (using variant: https://github.com/ivan1986/DbSimple/tree/master)
require_once 'includes/utilities.php'; // helper functions
require_once 'includes/config.class.php'; // Config holder
diff --git a/includes/stats.class.php b/includes/stats.class.php
new file mode 100644
index 000000000..615d11b2e
--- /dev/null
+++ b/includes/stats.class.php
@@ -0,0 +1,668 @@
+ ['health', ITEM_MOD_HEALTH, null, 115, self::FLAG_ITEM],
+ self::MANA => ['mana', ITEM_MOD_MANA, null, 116, self::FLAG_ITEM],
+ self::AGILITY => ['agi', ITEM_MOD_AGILITY, null, 21, self::FLAG_ITEM],
+ self::STRENGTH => ['str', ITEM_MOD_STRENGTH, null, 20, self::FLAG_ITEM],
+ self::INTELLECT => ['int', ITEM_MOD_INTELLECT, null, 23, self::FLAG_ITEM],
+ self::SPIRIT => ['spi', ITEM_MOD_SPIRIT, null, 24, self::FLAG_ITEM],
+ self::STAMINA => ['sta', ITEM_MOD_STAMINA, null, 22, self::FLAG_ITEM],
+ self::ENERGY => ['energy', null, null, null, self::FLAG_ITEM],
+ self::RAGE => ['rage', null, null, null, self::FLAG_ITEM],
+ self::FOCUS => ['focus', null, null, null, self::FLAG_ITEM],
+ self::RUNIC_POWER => ['runic', null, null, null, self::FLAG_ITEM | self::FLAG_SERVERSIDE],
+ self::DEFENSE_RTG => ['defrtng', ITEM_MOD_DEFENSE_SKILL_RATING, 1, 42, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::DODGE_RTG => ['dodgertng', ITEM_MOD_DODGE_RATING, 2, 45, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::PARRY_RTG => ['parryrtng', ITEM_MOD_PARRY_RATING, 3, 46, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::BLOCK_RTG => ['blockrtng', ITEM_MOD_BLOCK_RATING, 4, 44, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::MELEE_HIT_RTG => ['mlehitrtng', ITEM_MOD_HIT_MELEE_RATING, 5, 95, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::RANGED_HIT_RTG => ['rgdhitrtng', ITEM_MOD_HIT_RANGED_RATING, 6, 39, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::SPELL_HIT_RTG => ['splhitrtng', ITEM_MOD_HIT_SPELL_RATING, 7, 48, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::MELEE_CRIT_RTG => ['mlecritstrkrtng', ITEM_MOD_CRIT_MELEE_RATING, 8, 84, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::RANGED_CRIT_RTG => ['rgdcritstrkrtng', ITEM_MOD_CRIT_RANGED_RATING, 9, 40, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::SPELL_CRIT_RTG => ['splcritstrkrtng', ITEM_MOD_CRIT_SPELL_RATING, 10, 49, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::MELEE_HIT_TAKEN_RTG => ['_mlehitrtng', ITEM_MOD_HIT_TAKEN_MELEE_RATING, 11, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::RANGED_HIT_TAKEN_RTG => ['_rgdhitrtng', ITEM_MOD_HIT_TAKEN_RANGED_RATING, 12, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::SPELL_HIT_TAKEN_RTG => ['_splhitrtng', ITEM_MOD_HIT_TAKEN_SPELL_RATING, 13, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::MELEE_CRIT_TAKEN_RTG => ['_mlecritstrkrtng', ITEM_MOD_CRIT_TAKEN_MELEE_RATING, 14, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::RANGED_CRIT_TAKEN_RTG => ['_rgdcritstrkrtng', ITEM_MOD_CRIT_TAKEN_RANGED_RATING, 15, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::SPELL_CRIT_TAKEN_RTG => ['_splcritstrkrtng', ITEM_MOD_CRIT_TAKEN_SPELL_RATING, 16, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::MELEE_HASTE_RTG => ['mlehastertng', ITEM_MOD_HASTE_MELEE_RATING, 17, 78, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::RANGED_HASTE_RTG => ['rgdhastertng', ITEM_MOD_HASTE_RANGED_RATING, 18, 101, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::SPELL_HASTE_RTG => ['splhastertng', ITEM_MOD_HASTE_SPELL_RATING, 19, 102, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::HIT_RTG => ['hitrtng', ITEM_MOD_HIT_RATING, null, 119, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::CRIT_RTG => ['critstrkrtng', ITEM_MOD_CRIT_RATING, null, 96, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::HIT_TAKEN_RTG => ['_hitrtng', ITEM_MOD_HIT_TAKEN_RATING, null, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::CRIT_TAKEN_RTG => ['_critstrkrtng', ITEM_MOD_CRIT_TAKEN_RATING, null, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::RESILIENCE_RTG => ['resirtng', ITEM_MOD_RESILIENCE_RATING, null, 79, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::HASTE_RTG => ['hastertng', ITEM_MOD_HASTE_RATING, null, 103, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::EXPERTISE_RTG => ['exprtng', ITEM_MOD_EXPERTISE_RATING, 23, 117, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::ATTACK_POWER => ['atkpwr', ITEM_MOD_ATTACK_POWER, null, 77, self::FLAG_ITEM],
+ self::RANGED_ATTACK_POWER => ['rgdatkpwr', ITEM_MOD_RANGED_ATTACK_POWER, null, 38, self::FLAG_ITEM],
+ self::FERAL_ATTACK_POWER => ['feratkpwr', ITEM_MOD_FERAL_ATTACK_POWER, null, 97, self::FLAG_ITEM],
+ self::HEALING_SPELL_POWER => ['splheal', ITEM_MOD_SPELL_HEALING_DONE, null, 50, self::FLAG_ITEM],
+ self::DAMAGE_SPELL_POWER => ['spldmg', ITEM_MOD_SPELL_DAMAGE_DONE, null, 51, self::FLAG_ITEM],
+ self::MANA_REGENERATION => ['manargn', ITEM_MOD_MANA_REGENERATION, null, 61, self::FLAG_ITEM],
+ self::ARMOR_PENETRATION_RTG => ['armorpenrtng', ITEM_MOD_ARMOR_PENETRATION_RATING, 24, 114, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
+ self::SPELL_POWER => ['splpwr', ITEM_MOD_SPELL_POWER, null, 123, self::FLAG_ITEM],
+ self::HEALTH_REGENERATION => ['healthrgn', ITEM_MOD_HEALTH_REGEN, null, 60, self::FLAG_ITEM],
+ self::SPELL_PENETRATION => ['splpen', ITEM_MOD_SPELL_PENETRATION, null, 94, self::FLAG_ITEM],
+ self::BLOCK => ['block', ITEM_MOD_BLOCK_VALUE, null, 43, self::FLAG_ITEM],
+ // self::MASTERY_RTG => ['mastrtng', null, null, null, self::FLAG_NONE],
+ self::ARMOR => ['armor', null, null, 41, self::FLAG_ITEM],
+ self::FIRE_RESISTANCE => ['firres', null, null, 26, self::FLAG_ITEM],
+ self::FROST_RESISTANCE => ['frores', null, null, 28, self::FLAG_ITEM],
+ self::HOLY_RESISTANCE => ['holres', null, null, 30, self::FLAG_ITEM],
+ self::SHADOW_RESISTANCE => ['shares', null, null, 29, self::FLAG_ITEM],
+ self::NATURE_RESISTANCE => ['natres', null, null, 27, self::FLAG_ITEM],
+ self::ARCANE_RESISTANCE => ['arcres', null, null, 25, self::FLAG_ITEM],
+ self::FIRE_SPELL_POWER => ['firsplpwr', null, null, 53, self::FLAG_ITEM],
+ self::FROST_SPELL_POWER => ['frosplpwr', null, null, 54, self::FLAG_ITEM],
+ self::HOLY_SPELL_POWER => ['holsplpwr', null, null, 55, self::FLAG_ITEM],
+ self::SHADOW_SPELL_POWER => ['shasplpwr', null, null, 57, self::FLAG_ITEM],
+ self::NATURE_SPELL_POWER => ['natsplpwr', null, null, 56, self::FLAG_ITEM],
+ self::ARCANE_SPELL_POWER => ['arcsplpwr', null, null, 52, self::FLAG_ITEM],
+ // v not part of g_statToJson v
+ self::WEAPON_DAMAGE => ['dmg', null, null, null, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
+ self::WEAPON_DAMAGE_TYPE => ['damagetype', null, null, 35, self::FLAG_SERVERSIDE],
+ self::WEAPON_DAMAGE_MIN => ['dmgmin1', null, null, 33, self::FLAG_SERVERSIDE],
+ self::WEAPON_DAMAGE_MAX => ['dmgmax1', null, null, 34, self::FLAG_SERVERSIDE],
+ self::WEAPON_SPEED => ['speed', null, null, 36, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
+ self::WEAPON_DPS => ['dps', null, null, 32, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
+ self::MELEE_DAMAGE_MIN => ['mledmgmin', null, null, 135, self::FLAG_SERVERSIDE],
+ self::MELEE_DAMAGE_MAX => ['mledmgmax', null, null, 136, self::FLAG_SERVERSIDE],
+ self::MELEE_SPEED => ['mlespeed', null, null, 137, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
+ self::MELEE_DPS => ['mledps', null, null, 134, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE | self::FLAG_PROFILER],
+ self::RANGED_DAMAGE_MIN => ['rgddmgmin', null, null, 139, self::FLAG_SERVERSIDE],
+ self::RANGED_DAMAGE_MAX => ['rgddmgmax', null, null, 140, self::FLAG_SERVERSIDE],
+ self::RANGED_SPEED => ['rgdspeed', null, null, 141, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
+ self::RANGED_DPS => ['rgddps', null, null, 138, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE | self::FLAG_PROFILER],
+ self::EXTRA_SOCKETS => ['nsockets', null, null, 100, self::FLAG_SERVERSIDE],
+ self::ARMOR_BONUS => ['armorbonus', null, null, 109, self::FLAG_SERVERSIDE],
+ self::MELEE_ATTACK_POWER => ['mleatkpwr', null, null, 37, self::FLAG_SERVERSIDE | self::FLAG_PROFILER],
+ // v Profiler only v
+ self::EXPERTISE => ['exp', null, null, null, self::FLAG_PROFILER],
+ self::ARMOR_PENETRATION_PCT => ['armorpenpct', null, null, null, self::FLAG_PROFILER],
+ self::MELEE_HIT_PCT => ['mlehitpct', null, null, null, self::FLAG_PROFILER],
+ self::MELEE_CRIT_PCT => ['mlecritstrkpct', null, null, null, self::FLAG_PROFILER],
+ self::MELEE_HASTE_PCT => ['mlehastepct', null, null, null, self::FLAG_PROFILER],
+ self::RANGED_HIT_PCT => ['rgdhitpct', null, null, null, self::FLAG_PROFILER],
+ self::RANGED_CRIT_PCT => ['rgdcritstrkpct', null, null, null, self::FLAG_PROFILER],
+ self::RANGED_HASTE_PCT => ['rgdhastepct', null, null, null, self::FLAG_PROFILER],
+ self::SPELL_HIT_PCT => ['splhitpct', null, null, null, self::FLAG_PROFILER],
+ self::SPELL_CRIT_PCT => ['splcritstrkpct', null, null, null, self::FLAG_PROFILER],
+ self::SPELL_HASTE_PCT => ['splhastepct', null, null, null, self::FLAG_PROFILER],
+ self::MANA_REGENERATION_SPI => ['spimanargn', null, null, null, self::FLAG_PROFILER],
+ self::MANA_REGENERATION_OC => ['oocmanargn', null, null, null, self::FLAG_PROFILER],
+ self::MANA_REGENERATION_IC => ['icmanargn', null, null, null, self::FLAG_PROFILER],
+ self::ARMOR_TOTAL => ['fullarmor', null, null, null, self::FLAG_PROFILER],
+ self::DEFENSE => ['def', null, null, null, self::FLAG_PROFILER],
+ self::DODGE_PCT => ['dodgepct', null, null, null, self::FLAG_PROFILER],
+ self::PARRY_PCT => ['parrypct', null, null, null, self::FLAG_PROFILER],
+ self::BLOCK_PCT => ['blockpct', null, null, null, self::FLAG_PROFILER],
+ self::RESILIENCE_PCT => ['resipct', null, null, null, self::FLAG_PROFILER]
+ );
+
+ public static function isLevelIndependent(int $stat) : bool
+ {
+ if (!isset(self::$data[$stat]))
+ return false;
+
+ return !(self::$data[$stat][self::IDX_FLAGS] & self::FLAG_LVL_SCALING);
+ }
+
+ public static function getJsonString(int $stat) : string
+ {
+ if (!isset(self::$data[$stat]))
+ return '';
+
+ return self::$data[$stat][self::IDX_JSON_STR];
+ }
+
+ public static function getFilterCriteriumId(int $stat) : ?int
+ {
+ if (!isset(self::$data[$stat]))
+ return null;
+
+ return self::$data[$stat][self::IDX_FILTER_CR_ID];
+ }
+
+ public static function getFlags(int $stat) : int
+ {
+ if (!isset(self::$data[$stat]))
+ return 0;
+
+ return self::$data[$stat][self::IDX_FLAGS];
+ }
+
+ public static function getJsonStringsFor(int $flags = Stat::FLAG_NONE) : array
+ {
+ $x = [];
+ foreach (self::$data as $k => [$s, , , , $f])
+ if ($s && (!$flags || $flags & $f))
+ $x[$k] = $s;
+
+ return $x;
+ }
+
+ public static function getCombatRatingsFor(int $flags = Stat::FLAG_NONE) : array
+ {
+ $x = [];
+ foreach (self::$data as $k => [, , $c, , $f])
+ if ($c && (!$flags || $flags & $f))
+ $x[$k] = $c;
+
+ return $x;
+ }
+
+ public static function getFilterCriteriumIdFor(int $flags = Stat::FLAG_NONE) : array
+ {
+ $x = [];
+ foreach (self::$data as $k => [, , , $cr, $f])
+ if ($cr && (!$flags || $flags & $f))
+ $x[$k] = $cr;
+
+ return $x;
+ }
+
+ public static function getIndexFrom(int $idx, string $match) : int
+ {
+ $i = array_search($match, array_column(self::$data, $idx));
+ if ($i === false)
+ return 0;
+
+ return array_keys(self::$data)[$i];
+ }
+}
+
+class StatsContainer
+{
+ private $store = [];
+
+ private $relSpells = [];
+ private $relEnchantments = [];
+
+ public function __construct(array $relSpells = [], array $relEnchantments = [])
+ {
+ if ($relSpells)
+ $this->relSpells = $relSpells;
+
+ if ($relEnchantments)
+ $this->relEnchantments = $relEnchantments;
+ }
+
+ /**********/
+ /* Source */
+ /**********/
+
+ public function fromItem(array $item) : self
+ {
+ if (!$item)
+ return $this;
+
+ // convert itemMods to stats
+ for ($i = 1; $i <= 10; $i++)
+ {
+ $mod = $item['statType'.$i];
+ $val = $item['statValue'.$i];
+ if (!$mod || !$val)
+ continue;
+
+ if ($idx = Stat::getIndexFrom(Stat::IDX_ITEM_MOD, $mod))
+ Util::arraySumByKey($this->store, [$idx => $val]);
+ }
+
+ // also occurs as seperate field (gets summed in calculation but not in tooltip)
+ if ($item['tplBlock'])
+ Util::arraySumByKey($this->store, [Stat::BLOCK => $item['tplBlock']]);
+
+ // convert spells to stats
+ for ($i = 1; $i <= 5; $i++)
+ if (in_array($item['spellTrigger'.$i], [SPELL_TRIGGER_EQUIP, SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]))
+ if ($relS = $this->relS($item['spellId'.$i]))
+ $this->fromSpell($relS);
+
+ // for ITEM_CLASS_GEM get stats from enchantment
+ if ($relE = $this->relE($item['gemEnchantmentId']))
+ $this->fromEnchantment($relE);
+
+ return $this;
+ }
+
+ public function fromEnchantment(array $enchantment) : self
+ {
+ if (!$enchantment)
+ return $this;
+
+ for ($i = 1; $i <= 3; $i++)
+ {
+ $type = $enchantment['type'.$i];
+ $object = $enchantment['object'.$i];
+ $amount = $enchantment['amount'.$i]; // !CAUTION! scaling enchantments are initialized with "0" as amount. 0 is a valid amount!
+
+ if ($type == ENCHANTMENT_TYPE_EQUIP_SPELL && ($relS = $this->relS($object)))
+ $this->fromSpell($relS);
+ else
+ foreach ($this->convertEnchantment($type, $object) as $idx)
+ Util::arraySumByKey($this->store, [$idx => $amount]);
+ }
+
+ return $this;
+ }
+
+ public function fromSpell(array $spell) : self
+ {
+ if (!$spell)
+ return $this;
+
+ for ($i = 1; $i <= 3; $i++)
+ {
+ $eff = $spell['effect'.$i.'Id'];
+ $aura = $spell['effect'.$i.'AuraId'];
+ $mVal = $spell['effect'.$i.'MiscValue'];
+ $amt = $spell['effect'.$i.'BasePoints'] + $spell['effect'.$i.'DieSides'];
+
+ if (in_array($eff, SpellList::EFFECTS_ENCHANTMENT) && ($relE = $this->relE($mVal)))
+ $this->fromEnchantment($relE);
+ else
+ foreach ($this->convertSpellEffect($aura, $mVal, $amt) as $idx)
+ Util::arraySumByKey($this->store, [$idx => $amt]);
+ }
+
+ return $this;
+ }
+
+ public function fromJson(array &$json, bool $pruneFromSrc = false) : self
+ {
+ if (!$json)
+ return $this;
+
+ foreach (Stat::getJsonStringsFor() as $idx => $key)
+ {
+ if (isset($json[$key])) // 0 is a valid amount!
+ {
+ $float = Stat::getFlags($idx) & Stat::FLAG_FLOAT_VALUE;
+
+ if (Util::checkNumeric($json[$key], $float ? NUM_CAST_FLOAT : NUM_CAST_INT))
+ Util::arraySumByKey($this->store, [$idx => $json[$key]]);
+ }
+
+ if ($pruneFromSrc)
+ unset($json[$key]);
+ }
+
+ return $this;
+ }
+
+ public function fromDB(int $type, int $typeId, int $fieldFlags = 0x0) : self
+ {
+ foreach (DB::Aowow()->selectRow('SELECT (?#) FROM ?_item_stats WHERE `type` = ?d AND `typeId` = ?d', Stat::getJsonStringsFor($fieldFlags ?: (Stat::FLAG_ITEM | Stat::FLAG_SERVERSIDE)), $type, $typeId) as $key => $amt)
+ {
+ if ($amt === null)
+ continue;
+
+ $idx = Stat::getIndexFrom(Stat::IDX_JSON_STR, $key);
+ $float = Stat::getFlags($idx) & Stat::FLAG_FLOAT_VALUE;
+
+ if (Util::checkNumeric($amt, $float ? NUM_CAST_FLOAT : NUM_CAST_INT))
+ Util::arraySumByKey($this->store, [$idx => $amt]);
+ }
+
+ return $this;
+ }
+
+ public function fromContainer(StatsContainer ...$container) : self
+ {
+ foreach ($container as $c)
+ Util::arraySumByKey($this->store, $c->toRaw());
+
+ return $this;
+ }
+
+
+ /**********/
+ /* Output */
+ /**********/
+
+ public function toJson(int $outFlags = 0x0) : array
+ {
+ $out = [];
+ foreach ($this->store as $stat => $amt)
+ if (!$outFlags || (Stat::getFlags($stat) & $outFlags))
+ $out[Stat::getJsonString($stat)] = $amt;
+
+ return $out;
+ }
+
+ public function toRaw() : array
+ {
+ return $this->store;
+ }
+
+
+ /****************/
+ /* internal use */
+ /****************/
+
+ private function relE(int $enchantmentId) : array
+ {
+ if ($enchantmentId <= 0 || !isset($this->relEnchantments[$enchantmentId]))
+ return [];
+
+ return $this->relEnchantments[$enchantmentId];
+ }
+
+ private function relS(int $spellId) : array
+ {
+ if ($spellId <= 0 || !isset($this->relSpells[$spellId]))
+ return [];
+
+ return $this->relSpells[$spellId];
+ }
+
+ private static function convertEnchantment(int $type, int $object) : array
+ {
+ switch ($type)
+ {
+ case ENCHANTMENT_TYPE_PRISMATIC_SOCKET:
+ return [Stat::EXTRA_SOCKETS];
+ case ENCHANTMENT_TYPE_DAMAGE:
+ return [Stat::WEAPON_DAMAGE];
+ case ENCHANTMENT_TYPE_TOTEM:
+ return [Stat::WEAPON_DPS];
+ case ENCHANTMENT_TYPE_STAT: // ITEM_MOD_*
+ return [Stat::getIndexFrom(Stat::IDX_ITEM_MOD, $object)];
+ case ENCHANTMENT_TYPE_RESISTANCE:
+ if ($object == SPELL_SCHOOL_NORMAL)
+ return [Stat::ARMOR];
+ if ($object == SPELL_SCHOOL_HOLY)
+ return [Stat::HOLY_RESISTANCE];
+ if ($object == SPELL_SCHOOL_FIRE)
+ return [Stat::FIRE_RESISTANCE];
+ if ($object == SPELL_SCHOOL_NATURE)
+ return [Stat::NATURE_RESISTANCE];
+ if ($object == SPELL_SCHOOL_FROST)
+ return [Stat::FROST_RESISTANCE];
+ if ($object == SPELL_SCHOOL_SHADOW)
+ return [Stat::SHADOW_RESISTANCE];
+ if ($object == SPELL_SCHOOL_ARCANE)
+ return [Stat::ARCANE_RESISTANCE];
+
+ return [];
+ case ENCHANTMENT_TYPE_EQUIP_SPELL: // handled one level up
+ case ENCHANTMENT_TYPE_COMBAT_SPELL: // we do not average effects, so skip
+ case ENCHANTMENT_TYPE_USE_SPELL:
+ default:
+ return [];
+ }
+
+ return [];
+ }
+
+ public static function convertCombatRating(int $mask) : array
+ {
+ if (($mask & 0x00E0) == 0x00E0) // hit rating - all subcats (5:mle, 6:rgd, 7:spl)
+ return [Stat::HIT_RTG]; // generic hit rating
+
+ if (($mask & 0x0700) == 0x0700) // crit done rating - all subcats (8:mle, 9:rgd, 10:spl)
+ return [Stat::CRIT_RTG]; // generic crit rating
+
+ if (($mask & 0x1C000) == 0x1C000) // crit taken rating - all subcats (14:mle, 15:rgd, 16:spl)
+ return [Stat::RESILIENCE_RTG]; // resilience
+
+ $result = []; // there really shouldn't be multiple ratings in that mask besides the cases above, but who knows..
+ foreach (Stat::getCombatRatingsFor() as $stat => $cr)
+ if ($mask & (1 << $cr))
+ $result[] = $stat;
+
+ return $result;
+ }
+
+ private static function convertSpellEffect(int $auraId, int $miscValue, int &$amount) : array
+ {
+ $stats = [];
+
+ switch ($auraId)
+ {
+ case SPELL_AURA_MOD_STAT:
+ if ($miscValue < 0) // all stats
+ return [Stat::AGILITY, Stat::STRENGTH, Stat::INTELLECT, Stat::SPIRIT, Stat::STAMINA];
+ if ($miscValue == STAT_STRENGTH) // one stat
+ return [Stat::STRENGTH];
+ if ($miscValue == STAT_AGILITY)
+ return [Stat::AGILITY];
+ if ($miscValue == STAT_STAMINA)
+ return [Stat::STAMINA];
+ if ($miscValue == STAT_INTELLECT)
+ return [Stat::INTELLECT];
+ if ($miscValue == STAT_SPIRIT)
+ return [Stat::SPIRIT];
+
+ return []; // one bullshit
+ case SPELL_AURA_MOD_INCREASE_HEALTH:
+ case SPELL_AURA_MOD_INCREASE_HEALTH_NONSTACK:
+ case SPELL_AURA_MOD_INCREASE_HEALTH_2:
+ return [Stat::HEALTH];
+ case SPELL_AURA_MOD_DAMAGE_DONE:
+ // + weapon damage
+ if ($miscValue == (1 << SPELL_SCHOOL_NORMAL))
+ return [Stat::WEAPON_DAMAGE];
+
+ // full magic mask, also counts towards healing
+ if ($miscValue == SPELL_MAGIC_SCHOOLS)
+ return [Stat::SPELL_POWER, Stat::DAMAGE_SPELL_POWER, Stat::HEALING_SPELL_POWER];
+
+ // HolySpellpower (deprecated; still used in randomproperties)
+ if ($miscValue & (1 << SPELL_SCHOOL_HOLY))
+ $stats[] = Stat::HOLY_SPELL_POWER;
+
+ // FireSpellpower (deprecated; still used in randomproperties)
+ if ($miscValue & (1 << SPELL_SCHOOL_FIRE))
+ $stats[] = Stat::FIRE_SPELL_POWER;
+
+ // NatureSpellpower (deprecated; still used in randomproperties)
+ if ($miscValue & (1 << SPELL_SCHOOL_NATURE))
+ $stats[] = Stat::NATURE_SPELL_POWER;
+
+ // FrostSpellpower (deprecated; still used in randomproperties)
+ if ($miscValue & (1 << SPELL_SCHOOL_FROST))
+ $stats[] = Stat::FROST_SPELL_POWER;
+
+ // ShadowSpellpower (deprecated; still used in randomproperties)
+ if ($miscValue & (1 << SPELL_SCHOOL_SHADOW))
+ $stats[] = Stat::SHADOW_SPELL_POWER;
+
+ // ArcaneSpellpower (deprecated; still used in randomproperties)
+ if ($miscValue & (1 << SPELL_SCHOOL_ARCANE))
+ $stats[] = Stat::ARCANE_SPELL_POWER;
+
+ return $stats;
+ case SPELL_AURA_MOD_HEALING_DONE: // not as a mask..
+ return [Stat::HEALING_SPELL_POWER];
+ case SPELL_AURA_MOD_INCREASE_ENERGY: // MiscVal:type see defined Powers only energy/mana in use
+ if ($miscValue == POWER_ENERGY)
+ return [Stat::ENERGY];
+ if ($miscValue == POWER_RAGE)
+ return [Stat::RAGE];
+ if ($miscValue == POWER_MANA)
+ return [Stat::MANA];
+ if ($miscValue == POWER_RUNIC_POWER)
+ return [Stat::RUNIC_POWER];
+
+ return [];
+ case SPELL_AURA_MOD_RATING:
+ case SPELL_AURA_MOD_RATING_FROM_STAT:
+ if ($stat = self::convertCombatRating($miscValue))
+ return $stat;
+
+ return [];
+ case SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE:
+ case SPELL_AURA_MOD_BASE_RESISTANCE:
+ case SPELL_AURA_MOD_RESISTANCE:
+ // Armor only if explicitly specified
+ if ($miscValue == (1 << SPELL_SCHOOL_NORMAL))
+ return [Stat::ARMOR];
+
+ // Holy resistance only if explicitly specified (should it even exist...?)
+ if ($miscValue == (1 << SPELL_SCHOOL_HOLY))
+ return [Stat::HOLY_RESISTANCE];
+
+ if ($miscValue & (1 << SPELL_SCHOOL_FIRE))
+ $stats[] = Stat::FIRE_RESISTANCE;
+ if ($miscValue & (1 << SPELL_SCHOOL_NATURE))
+ $stats[] = Stat::NATURE_RESISTANCE;
+ if ($miscValue & (1 << SPELL_SCHOOL_FROST))
+ $stats[] = Stat::FROST_RESISTANCE;
+ if ($miscValue & (1 << SPELL_SCHOOL_SHADOW))
+ $stats[] = Stat::SHADOW_RESISTANCE;
+ if ($miscValue & (1 << SPELL_SCHOOL_ARCANE))
+ $stats[] = Stat::ARCANE_RESISTANCE;
+
+ return $stats;
+ case SPELL_AURA_PERIODIC_HEAL: // hp5
+ case SPELL_AURA_MOD_REGEN:
+ case SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT:
+ return [Stat::HEALTH_REGENERATION];
+ case SPELL_AURA_MOD_POWER_REGEN: // mp5
+ return [Stat::MANA_REGENERATION];
+ case SPELL_AURA_MOD_ATTACK_POWER:
+ return [Stat::ATTACK_POWER/*, Stat::RANGED_ATTACK_POWER*/];
+ case SPELL_AURA_MOD_RANGED_ATTACK_POWER:
+ return [Stat::RANGED_ATTACK_POWER];
+ case SPELL_AURA_MOD_SHIELD_BLOCKVALUE:
+ return [Stat::BLOCK];
+ case SPELL_AURA_MOD_EXPERTISE:
+ return [Stat::EXPERTISE];
+ case SPELL_AURA_MOD_TARGET_RESISTANCE:
+ $amount = abs($amount); // functionally negative, but we work with the absolute amount
+ if ($miscValue == 0x7C) // SPELL_MAGIC_SCHOOLS & ~SPELL_SCHOOL_HOLY
+ return [Stat::SPELL_PENETRATION];
+ }
+
+ return [];
+ }
+}
+
+?>
diff --git a/includes/types/enchantment.class.php b/includes/types/enchantment.class.php
index b8af0c4df..8cddd361d 100644
--- a/includes/types/enchantment.class.php
+++ b/includes/types/enchantment.class.php
@@ -35,35 +35,25 @@ public function __construct($conditions = [])
if ($curTpl['object'.$i] <= 0)
continue;
- switch ($curTpl['type'.$i])
+ switch ($curTpl['type'.$i]) // SPELL_TRIGGER_* just reused for wording
{
- case 1:
+ case ENCHANTMENT_TYPE_COMBAT_SPELL:
$proc = -$this->getField('ppmRate') ?: ($this->getField('procChance') ?: $this->getField('amount'.$i));
- $curTpl['spells'][$i] = [$curTpl['object'.$i], 2, $curTpl['charges'], $proc];
+ $curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_HIT, $curTpl['charges'], $proc];
$this->relSpells[] = $curTpl['object'.$i];
break;
- case 3:
- $curTpl['spells'][$i] = [$curTpl['object'.$i], 1, $curTpl['charges'], 0];
+ case ENCHANTMENT_TYPE_EQUIP_SPELL:
+ $curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_EQUIP, $curTpl['charges'], 0];
$this->relSpells[] = $curTpl['object'.$i];
break;
- case 7:
- $curTpl['spells'][$i] = [$curTpl['object'.$i], 0, $curTpl['charges'], 0];
+ case ENCHANTMENT_TYPE_USE_SPELL:
+ $curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_USE, $curTpl['charges'], 0];
$this->relSpells[] = $curTpl['object'.$i];
break;
}
}
- // floats are fetched as string from db :<
- $curTpl['dmg'] = floatVal($curTpl['dmg']);
- $curTpl['dps'] = floatVal($curTpl['dps']);
-
- // remove zero-stats
- foreach (Game::$itemMods as $str)
- if ($curTpl[$str] == 0) // empty(0.0f) => true .. yeah, sure
- unset($curTpl[$str]);
-
- if ($curTpl['dps'] == 0)
- unset($curTpl['dps']);
+ $this->jsonStats[$this->id] = (new StatsContainer)->fromJson($curTpl, true);
}
if ($this->relSpells)
@@ -99,18 +89,18 @@ public function getListviewData($addInfoMask = 0x0)
if ($this->curTpl['requiredLevel'] > 0)
$data[$this->id]['reqlevel'] = $this->curTpl['requiredLevel'];
- foreach ($this->curTpl['spells'] as $s)
+ foreach ($this->curTpl['spells'] as [$spellId, $trigger, $charges, $procChance])
{
// enchant is procing or onUse
- if ($s[1] == 2 || $s[1] == 0)
- $data[$this->id]['spells'][$s[0]] = $s[2];
+ if ($trigger == SPELL_TRIGGER_HIT || $trigger == SPELL_TRIGGER_USE)
+ $data[$this->id]['spells'][$spellId] = $charges;
// spell is procing
- else if ($this->relSpells && $this->relSpells->getEntry($s[0]) && ($_ = $this->relSpells->canTriggerSpell()))
+ else if ($this->relSpells && $this->relSpells->getEntry($spellId) && ($_ = $this->relSpells->canTriggerSpell()))
{
foreach ($_ as $idx)
{
$this->triggerIds[] = $this->relSpells->getField('effect'.$idx.'TriggerSpell');
- $data[$this->id]['spells'][$this->relSpells->getField('effect'.$idx.'TriggerSpell')] = $s[2];
+ $data[$this->id]['spells'][$this->relSpells->getField('effect'.$idx.'TriggerSpell')] = $charges;
}
}
}
@@ -118,88 +108,15 @@ public function getListviewData($addInfoMask = 0x0)
if (!$data[$this->id]['spells'])
unset($data[$this->id]['spells']);
- Util::arraySumByKey($data[$this->id], $this->getStatGain());
+ Util::arraySumByKey($data[$this->id], $this->jsonStats[$this->id]->toJson());
}
return $data;
}
- public function getStatGain($addScalingKeys = false)
+ public function getStatGainForCurrent() : array
{
- $data = [];
-
- foreach (Game::$itemMods as $str)
- if (isset($this->curTpl[$str]))
- $data[$str] = $this->curTpl[$str];
-
- if (isset($this->curTpl['dps']))
- $data['dps'] = $this->curTpl['dps'];
-
- // scaling enchantments are saved as 0 to item_stats, thus return empty
- if ($addScalingKeys)
- {
- $spellStats = [];
- if ($this->relSpells)
- $spellStats = $this->relSpells->getStatGain();
-
- for ($h = 1; $h <= 3; $h++)
- {
- $obj = (int)$this->curTpl['object'.$h];
-
- switch ($this->curTpl['type'.$h])
- {
- case 3: // TYPE_EQUIP_SPELL Spells from ObjectX (use of amountX?)
- if (!empty($spellStats[$obj]))
- foreach ($spellStats[$obj] as $mod => $_)
- if ($str = Game::$itemMods[$mod])
- Util::arraySumByKey($data, [$str => 0]);
-
- $obj = null;
- break;
- case 4: // TYPE_RESISTANCE +AmountX resistance for ObjectX School
- switch ($obj)
- {
- case 0: // Physical
- $obj = ITEM_MOD_ARMOR;
- break;
- case 1: // Holy
- $obj = ITEM_MOD_HOLY_RESISTANCE;
- break;
- case 2: // Fire
- $obj = ITEM_MOD_FIRE_RESISTANCE;
- break;
- case 3: // Nature
- $obj = ITEM_MOD_NATURE_RESISTANCE;
- break;
- case 4: // Frost
- $obj = ITEM_MOD_FROST_RESISTANCE;
- break;
- case 5: // Shadow
- $obj = ITEM_MOD_SHADOW_RESISTANCE;
- break;
- case 6: // Arcane
- $obj = ITEM_MOD_ARCANE_RESISTANCE;
- break;
- default:
- $obj = null;
- }
- break;
- case 5: // TYPE_STAT +AmountX for Statistic by type of ObjectX
- if ($obj < 2) // [mana, health] are on [0, 1] respectively and are expected on [1, 2] ..
- $obj++; // 0 is weaponDmg .. ehh .. i messed up somewhere
-
- break; // stats are directly assigned below
- default: // TYPE_NONE dnd stuff; skip assignment below
- $obj = null;
- }
-
- if ($obj !== null)
- if ($str = Game::$itemMods[$obj]) // check if we use these mods
- Util::arraySumByKey($data, [$str => 0]);
- }
- }
-
- return $data;
+ return $this->jsonStats[$this->id]->toJson();
}
public function getRelSpell($id)
diff --git a/includes/types/item.class.php b/includes/types/item.class.php
index 7912c5c9c..7d8afa772 100644
--- a/includes/types/item.class.php
+++ b/includes/types/item.class.php
@@ -13,7 +13,7 @@ class ItemList extends BaseType
public static $dataTable = '?_items';
public $json = [];
- public $itemMods = [];
+ public $jsonStats = [];
public $rndEnchIds = [];
public $subItems = [];
@@ -48,7 +48,9 @@ public function __construct($conditions = [], $miscData = null)
// fix missing icons
$_curTpl['iconString'] = $_curTpl['iconString'] ?: DEFAULT_ICON;
+ // from json to json .. the gentle fuckups of legacy code integration
$this->initJsonStats();
+ $this->jsonStats[$this->id] = (new StatsContainer())->fromJson($_curTpl, true)->toJson(Stat::FLAG_ITEM /* | Stat::FLAG_SERVERSIDE */);
if ($miscData)
{
@@ -280,7 +282,7 @@ public function getExtendedCost($filter = [], &$reqRating = [])
public function getListviewData($addInfoMask = 0x0, $miscData = null)
{
/*
- * ITEMINFO_JSON (0x01): itemMods (including spells) and subitems parsed
+ * ITEMINFO_JSON (0x01): jsonStats (including spells) and subitems parsed
* ITEMINFO_SUBITEMS (0x02): searched by comparison
* ITEMINFO_VENDOR (0x04): costs-obj, when displayed as vendor
* ITEMINFO_GEM (0x10): gem infos and score
@@ -294,7 +296,10 @@ public function getListviewData($addInfoMask = 0x0, $miscData = null)
$this->initSubItems();
if ($addInfoMask & ITEMINFO_JSON)
+ {
$this->extendJsonStats();
+ Util::arraySumByKey($data, $this->jsonStats);
+ }
$extCosts = [];
if ($addInfoMask & ITEMINFO_VENDOR)
@@ -321,9 +326,6 @@ public function getListviewData($addInfoMask = 0x0, $miscData = null)
if ($addInfoMask & ITEMINFO_JSON)
{
- foreach ($this->itemMods[$this->id] as $k => $v)
- $data[$this->id][$k] = $v;
-
if ($_ = intVal(($this->curTpl['minMoneyLoot'] + $this->curTpl['maxMoneyLoot']) / 2))
$data[$this->id]['avgmoney'] = $_;
@@ -679,7 +681,7 @@ public function renderTooltip($interactive = false, $subOf = 0, $enhance = [])
$x .= sprintf(Lang::item('damage', 'single', $sc2 ? 3 : 2), $this->curTpl['dmgMin2'], $sc2 ? Lang::game('sc', $sc2) : null).'
';
if ($_class == ITEM_CLASS_WEAPON)
- $x .= ''.sprintf(Lang::item('dps'), $dps).'
'; // do not use localized format here!
+ $x .= ''.Lang::item('dps', [$dps]).'
';
// display FeralAttackPower if set
if ($fap = $this->getFeralAP())
@@ -1126,7 +1128,7 @@ public function renderTooltip($interactive = false, $subOf = 0, $enhance = [])
{
$xCraft = '';
if ($desc = $this->getField('description', true))
- $x .= ''.Lang::item('trigger', 0).' '.$desc.'
';
+ $x .= ''.Lang::item('trigger', SPELL_TRIGGER_USE).' '.$desc.'
';
// recipe handling (some stray Techniques have subclass == 0), place at bottom of tooltipp
if ($_class == ITEM_CLASS_RECIPE || $this->curTpl['bagFamily'] == 16)
@@ -1208,7 +1210,7 @@ public function renderTooltip($interactive = false, $subOf = 0, $enhance = [])
$this->curTpl['scalingStatValue'] // scaleFlags
);
}
- else // may still use level dependant ratings
+ else // may still use level dependent ratings
{
array_push($link,
$causesScaling ? MAX_LEVEL : 1, // scaleMaxLevel
@@ -1311,12 +1313,6 @@ public function extendJsonStats()
foreach ($this->iterate() as $__)
{
- $this->itemMods[$this->id] = [];
-
- foreach (Game::$itemMods as $mod)
- if ($_ = floatVal($this->curTpl[$mod]))
- Util::arraySumByKey($this->itemMods[$this->id], [$mod => $_]);
-
// fetch and add socketbonusstats
if (!empty($this->json[$this->id]['socketbonus']))
$enchantments[$this->json[$this->id]['socketbonus']][] = $this->id;
@@ -1353,28 +1349,28 @@ public function extendJsonStats()
unset($this->json[$item][$k]);
}
- public function getOnUseStats()
+ public function getOnUseStats() : ?StatsContainer
{
- $onUseStats = [];
+ if ($this->curTpl['class'] != ITEM_CLASS_CONSUMABLE)
+ return null;
+
+ $onUseStats = new StatsContainer();
// convert Spells
- $useSpells = [];
for ($h = 1; $h <= 5; $h++)
{
if ($this->curTpl['spellId'.$h] <= 0)
continue;
- if ($this->curTpl['class'] != ITEM_CLASS_CONSUMABLE || $this->curTpl['spellTrigger'.$h])
+ if ($this->curTpl['spellTrigger'.$h] != SPELL_TRIGGER_USE)
continue;
- $useSpells[] = $this->curTpl['spellId'.$h];
- }
-
- if ($useSpells)
- {
- $eqpSplList = new SpellList(array(['s.id', $useSpells]));
- foreach ($eqpSplList->getStatGain() as $stat)
- Util::arraySumByKey($onUseStats, $stat);
+ if ($spell = DB::Aowow()->selectRow(
+ 'SELECT effect1AuraId, effect1MiscValue, effect1BasePoints, effect1DieSides, effect2AuraId, effect2MiscValue, effect2BasePoints, effect2DieSides, effect3AuraId, effect3MiscValue, effect3BasePoints, effect3DieSides
+ FROM ?_spell
+ WHERE id = ?d',
+ $this->curTpl['spellId'.$h]))
+ $onUseStats->fromSpell($spell);
}
return $onUseStats;
@@ -1414,32 +1410,33 @@ private function canTeachSpell()
return true;
}
- private function getFeralAP()
+ private function getFeralAP() : float
{
// must be weapon
if ($this->curTpl['class'] != ITEM_CLASS_WEAPON)
- return 0;
-
- $subClasses = [14]; // Misc Weapons
- $druid = new CharClassList(array(['id', log(CLASS_DRUID, 2) + 1]));
- if (!$druid->error)
- for ($i = 0; $i < 21; $i++)
- if ($druid->getField('weaponTypeMask') & (1 << $i))
- $subClasses[] = $i;
-
- if (!in_array($this->curTpl['subClass'], $subClasses))
- return 0;
+ return 0.0;
// thats fucked up..
if (!$this->curTpl['delay'])
- return 0;
+ return 0.0;
// must have enough damage
$dps = ($this->curTpl['tplDmgMin1'] + $this->curTpl['dmgMin2'] + $this->curTpl['tplDmgMax1'] + $this->curTpl['dmgMax2']) / (2 * $this->curTpl['delay'] / 1000);
- if ($dps < 54.8)
- return 0;
+ if ($dps <= 54.8)
+ return 0.0;
+
+ $subClasses = [14]; // Misc Weapons
+ $weaponTypeMask = DB::Aowow()->selectCell('SELECT `weaponTypeMask` FROM ?_classes WHERE `id` = ?d', log(CLASS_DRUID, 2) + 1);
+ if ($weaponTypeMask)
+ for ($i = 0; $i < 21; $i++)
+ if ($weaponTypeMask & (1 << $i))
+ $subClasses[] = $i;
- return round(($dps - 54.8) * 14, 0);
+ // cannot be used by druids
+ if (!in_array($this->curTpl['subClass'], $subClasses))
+ return 0.0;
+
+ return round(($dps - 54.8) * 14);
}
private function parseRating($type, $value, $interactive = false, &$scaling = false)
@@ -1449,29 +1446,28 @@ private function parseRating($type, $value, $interactive = false, &$scaling = fa
$reqLvl = $this->curTpl['requiredLevel'] > 1 ? $this->curTpl['requiredLevel'] : MAX_LEVEL;
$level = min(max($reqLvl, $ssdLvl), MAX_LEVEL);
- // unknown rating
- if (in_array($type, [2, 8, 9, 10, 11]) || $type > ITEM_MOD_BLOCK_VALUE || $type < 0)
+ // unknown rating
+ if (!Stat::getIndexFrom(Stat::IDX_ITEM_MOD, $type))
{
if (User::isInGroup(U_GROUP_EMPLOYEE))
return sprintf(Lang::item('statType', count(Lang::item('statType')) - 1), $type, $value);
else
return null;
}
- // level independant Bonus
- else if (in_array($type, Game::$lvlIndepRating))
- return Lang::item('trigger', 1).str_replace('%d', ''.$value, Lang::item('statType', $type));
+
+ // level independent Bonus
+ if (Stat::isLevelIndependent($type))
+ return Lang::item('trigger', SPELL_TRIGGER_EQUIP).str_replace('%d', ''.$value, Lang::item('statType', $type));
+
// rating-Bonuses
- else
- {
- $scaling = true;
+ $scaling = true;
- if ($interactive)
- $js = ' ('.sprintf(Util::$changeLevelString, Util::setRatingLevel($level, $type, $value)).')';
- else
- $js = ' ('.Util::setRatingLevel($level, $type, $value).')';
+ if ($interactive)
+ $js = ' ('.sprintf(Util::$changeLevelString, Util::setRatingLevel($level, $type, $value)).')';
+ else
+ $js = ' ('.Util::setRatingLevel($level, $type, $value).')';
- return Lang::item('trigger', 1).str_replace('%d', ''.$value.$js, Lang::item('statType', $type));
- }
+ return Lang::item('trigger', SPELL_TRIGGER_EQUIP).str_replace('%d', ''.$value.$js, Lang::item('statType', $type));
}
private function getSSDMod($type)
@@ -1583,7 +1579,7 @@ public function initSubItems()
{
$this->rndEnchIds[$eId] = array(
'text' => $enchants->getField('name', true),
- 'stats' => $enchants->getStatGain(true)
+ 'stats' => $enchants->getStatGainForCurrent()
);
}
@@ -1677,7 +1673,7 @@ private function initJsonStats()
'subclass' => $this->curTpl['subClass'],
'subsubclass' => $this->curTpl['subSubClass'],
'heroic' => ($this->curTpl['flags'] & 0x8) >> 3,
- 'side' => $this->curTpl['flagsExtra'] & 0x3 ? 3 - ($this->curTpl['flagsExtra'] & 0x3) : Game::sideByRaceMask($this->curTpl['requiredRace']),
+ 'side' => $this->curTpl['flagsExtra'] & 0x3 ? SIDE_BOTH - ($this->curTpl['flagsExtra'] & 0x3) : Game::sideByRaceMask($this->curTpl['requiredRace']),
'slot' => $this->curTpl['slot'],
'slotbak' => $this->curTpl['slotBak'],
'level' => $this->curTpl['itemLevel'],
@@ -1690,7 +1686,7 @@ private function initJsonStats()
'frores' => $this->curTpl['resFrost'],
'shares' => $this->curTpl['resShadow'],
'arcres' => $this->curTpl['resArcane'],
- 'armorbonus' => max(0, intVal($this->curTpl['armorDamageModifier'])),
+ 'armorbonus' => $this->curTpl['class'] != ITEM_CLASS_ARMOR || $this->curTpl['armorDamageModifier'] <= 0 ? 0 : intVal($this->curTpl['armorDamageModifier']),
'armor' => $this->curTpl['tplArmor'],
'dura' => $this->curTpl['durability'],
'itemset' => $this->curTpl['itemset'],
@@ -1709,8 +1705,8 @@ private function initJsonStats()
$json['dmgtype1'] = $this->curTpl['dmgType1'];
$json['dmgmin1'] = $this->curTpl['tplDmgMin1'] + $this->curTpl['dmgMin2'];
$json['dmgmax1'] = $this->curTpl['tplDmgMax1'] + $this->curTpl['dmgMax2'];
- $json['speed'] = number_format($this->curTpl['delay'] / 1000, 2);
- $json['dps'] = !floatVal($json['speed']) ? 0 : number_format(($json['dmgmin1'] + $json['dmgmax1']) / (2 * $json['speed']), 1);
+ $json['speed'] = round($this->curTpl['delay'] / 1000, 2);
+ $json['dps'] = $json['speed'] ? round(($json['dmgmin1'] + $json['dmgmax1']) / (2 * $json['speed']), 1) : 0;
if (in_array($json['subclass'], [2, 3, 18, 19]))
{
@@ -1734,7 +1730,7 @@ private function initJsonStats()
if ($this->curTpl['class'] == ITEM_CLASS_ARMOR || $this->curTpl['class'] == ITEM_CLASS_WEAPON)
$json['gearscore'] = Util::getEquipmentScore($json['level'], $this->getField('quality'), $json['slot'], $json['nsockets']);
else if ($this->curTpl['class'] == ITEM_CLASS_GEM)
- $json['gearscore'] = Util::getGemScore($json['level'], $this->getField('quality'), $this->getField('requiredSkill') == 755, $this->id);
+ $json['gearscore'] = Util::getGemScore($json['level'], $this->getField('quality'), $this->getField('requiredSkill') == SKILL_JEWELCRAFTING, $this->id);
// clear zero-values afterwards
foreach ($json as $k => $v)
@@ -1809,7 +1805,7 @@ class ItemListFilter extends Filter
14 => -1,
15 => -1
),
- 128 => array( // source
+ 128 => array( // source
1 => true, // Any
2 => false, // None
3 => 1, // Crafted
@@ -2036,18 +2032,15 @@ public function createConditionsForWeights()
foreach ($this->fiData['v']['wt'] as $k => $v)
{
- $str = Util::$itemFilter[$v];
- $qty = intVal($this->fiData['v']['wtv'][$k]);
-
- if ($str == 'rgdspeed') // dont need no duplicate column
- $str = 'speed';
-
- if ($str == 'mledps') // todo (med): unify rngdps and mledps to dps
- $str = 'dps';
+ if ($idx = Stat::getIndexFrom(Stat::IDX_FILTER_CR_ID, $v))
+ {
+ $str = Stat::getJsonString($idx);
+ $qty = intVal($this->fiData['v']['wtv'][$k]);
- $select[] = '(`is`.`'.$str.'` * '.$qty.')';
- $this->wtCnd[] = ['is.'.$str, 0, '>'];
- $wtSum += $qty;
+ $select[] = '(IFNULL(`is`.`'.$str.'`, 0) * '.$qty.')';
+ $this->wtCnd[] = ['is.'.$str, 0, '>'];
+ $wtSum += $qty;
+ }
}
if (count($this->wtCnd) > 1)
@@ -2681,7 +2674,7 @@ protected function cbWeightKeyCheck(&$v)
if (preg_match('/\W/i', $v))
return false;
- return isset(Util::$itemFilter[$v]);
+ return Stat::getIndexFrom(Stat::IDX_FILTER_CR_ID, $v) > 0;
}
}
diff --git a/includes/types/spell.class.php b/includes/types/spell.class.php
index 7eb332bdf..72745cfc2 100644
--- a/includes/types/spell.class.php
+++ b/includes/types/spell.class.php
@@ -58,6 +58,9 @@ class SpellList extends BaseType
SPELL_EFFECT_SCHOOL_DAMAGE, SPELL_EFFECT_ENVIRONMENTAL_DAMAGE, SPELL_EFFECT_POWER_DRAIN, SPELL_EFFECT_HEALTH_LEECH, SPELL_EFFECT_POWER_BURN,
SPELL_EFFECT_HEAL_MAX_HEALTH
);
+ public const EFFECTS_ENCHANTMENT = array(
+ SPELL_EFFECT_ENCHANT_ITEM, SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY, SPELL_EFFECT_ENCHANT_HELD_ITEM, SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC
+ );
public const AURAS_HEAL = array(
SPELL_AURA_DUMMY, SPELL_AURA_PERIODIC_HEAL, SPELL_AURA_PERIODIC_HEALTH_FUNNEL, SPELL_AURA_SCHOOL_ABSORB, SPELL_AURA_MANA_SHIELD,
@@ -190,196 +193,19 @@ public static function getName($id)
// end static use
// required for item-comparison
- public function getStatGain()
+ public function getStatGain() : array
{
$data = [];
foreach ($this->iterate() as $__)
{
- $stats = [];
-
- for ($i = 1; $i <= 3; $i++)
- {
- $pts = $this->calculateAmountForCurrent($i)[1];
- $mv = $this->curTpl['effect'.$i.'MiscValue'];
- $au = $this->curTpl['effect'.$i.'AuraId'];
-
- if (in_array($this->curTpl['effect'.$i.'Id'], [SPELL_EFFECT_ENCHANT_ITEM, SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY]))
- {
- if ($mv && ($json = DB::Aowow()->selectRow('SELECT * FROM ?_item_stats WHERE `type` = ?d AND `typeId` = ?d', Type::ENCHANTMENT, $mv)))
- {
- $mods = [];
- foreach ($json as $str => $val)
- if ($val && ($idx = array_search($str, Game::$itemMods)))
- $mods[$idx] = $val;
-
- if ($mods)
- Util::arraySumByKey($stats, $mods);
- }
-
- continue;
- }
-
- switch ($au)
- {
- case SPELL_AURA_MOD_STAT:
- if ($mv < 0) // all stats
- {
- for ($iMod = ITEM_MOD_AGILITY; $iMod <= ITEM_MOD_STAMINA; $iMod++)
- Util::arraySumByKey($stats, [$iMod => $pts]);
- }
- else if ($mv == STAT_STRENGTH) // one stat
- Util::arraySumByKey($stats, [ITEM_MOD_STRENGTH => $pts]);
- else if ($mv == STAT_AGILITY)
- Util::arraySumByKey($stats, [ITEM_MOD_AGILITY => $pts]);
- else if ($mv == STAT_STAMINA)
- Util::arraySumByKey($stats, [ITEM_MOD_STAMINA => $pts]);
- else if ($mv == STAT_INTELLECT)
- Util::arraySumByKey($stats, [ITEM_MOD_INTELLECT => $pts]);
- else if ($mv == STAT_SPIRIT)
- Util::arraySumByKey($stats, [ITEM_MOD_SPIRIT => $pts]);
- else // one bullshit
- trigger_error('AuraId 29 of spell #'.$this->id.' has wrong statId #'.$mv, E_USER_WARNING);
-
- break;
- case SPELL_AURA_MOD_INCREASE_HEALTH:
- case SPELL_AURA_MOD_INCREASE_HEALTH_NONSTACK:
- case SPELL_AURA_MOD_INCREASE_HEALTH_2:
- Util::arraySumByKey($stats, [ITEM_MOD_HEALTH => $pts]);
- break;
- case SPELL_AURA_MOD_DAMAGE_DONE:
- // + weapon damage
- if ($mv == (1 << SPELL_SCHOOL_NORMAL))
- {
- Util::arraySumByKey($stats, [ITEM_MOD_WEAPON_DMG => $pts]);
- break;
- }
-
- // full magic mask, also counts towards healing
- if ($mv == SPELL_MAGIC_SCHOOLS)
- {
- Util::arraySumByKey($stats, [ITEM_MOD_SPELL_POWER => $pts]);
- Util::arraySumByKey($stats, [ITEM_MOD_SPELL_DAMAGE_DONE => $pts]);
- }
- else
- {
- // HolySpellpower (deprecated; still used in randomproperties)
- if ($mv & (1 << SPELL_SCHOOL_HOLY))
- Util::arraySumByKey($stats, [ITEM_MOD_HOLY_POWER => $pts]);
-
- // FireSpellpower (deprecated; still used in randomproperties)
- if ($mv & (1 << SPELL_SCHOOL_FIRE))
- Util::arraySumByKey($stats, [ITEM_MOD_FIRE_POWER => $pts]);
-
- // NatureSpellpower (deprecated; still used in randomproperties)
- if ($mv & (1 << SPELL_SCHOOL_NATURE))
- Util::arraySumByKey($stats, [ITEM_MOD_NATURE_POWER => $pts]);
-
- // FrostSpellpower (deprecated; still used in randomproperties)
- if ($mv & (1 << SPELL_SCHOOL_FROST))
- Util::arraySumByKey($stats, [ITEM_MOD_FROST_POWER => $pts]);
-
- // ShadowSpellpower (deprecated; still used in randomproperties)
- if ($mv & (1 << SPELL_SCHOOL_SHADOW))
- Util::arraySumByKey($stats, [ITEM_MOD_SHADOW_POWER => $pts]);
-
- // ArcaneSpellpower (deprecated; still used in randomproperties)
- if ($mv & (1 << SPELL_SCHOOL_ARCANE))
- Util::arraySumByKey($stats, [ITEM_MOD_ARCANE_POWER => $pts]);
- }
-
- break;
- case SPELL_AURA_MOD_HEALING_DONE: // not as a mask..
- Util::arraySumByKey($stats, [ITEM_MOD_SPELL_HEALING_DONE => $pts]);
- break;
- case SPELL_AURA_MOD_INCREASE_ENERGY: // MiscVal:type see defined Powers only energy/mana in use
- if ($mv == POWER_HEALTH)
- Util::arraySumByKey($stats, [ITEM_MOD_HEALTH => $pts]);
- else if ($mv == POWER_ENERGY)
- Util::arraySumByKey($stats, [ITEM_MOD_ENERGY => $pts]);
- else if ($mv == POWER_RAGE)
- Util::arraySumByKey($stats, [ITEM_MOD_RAGE => $pts]);
- else if ($mv == POWER_MANA)
- Util::arraySumByKey($stats, [ITEM_MOD_MANA => $pts]);
- else if ($mv == POWER_RUNIC_POWER)
- Util::arraySumByKey($stats, [ITEM_MOD_RUNIC_POWER => $pts]);
-
- break;
- case SPELL_AURA_MOD_RATING:
- case SPELL_AURA_MOD_RATING_FROM_STAT:
- if ($mod = Game::itemModByRatingMask($mv))
- Util::arraySumByKey($stats, [$mod => $pts]);
- break;
- case SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE:
- case SPELL_AURA_MOD_BASE_RESISTANCE:
- case SPELL_AURA_MOD_RESISTANCE:
- // Armor only if explicitly specified
- if ($mv == (1 << SPELL_SCHOOL_NORMAL))
- {
- Util::arraySumByKey($stats, [ITEM_MOD_ARMOR => $pts]);
- break;
- }
+ $data[$this->id] = new StatsContainer();
- // Holy resistance only if explicitly specified (shouldn't even exist...?)
- if ($mv == (1 << SPELL_SCHOOL_HOLY))
- {
- Util::arraySumByKey($stats, [ITEM_MOD_HOLY_RESISTANCE => $pts]);
- break;
- }
+ foreach ($this->canEnchantmentItem() as $i)
+ $data[$this->id]->fromDB(Type::ENCHANTMENT, $this->curTpl['effect'.$i.'MiscValue']);
- for ($j = 0; $j < 7; $j++)
- {
- if (($mv & (1 << $j)) == 0)
- continue;
-
- switch ($j)
- {
- case SPELL_SCHOOL_FIRE:
- Util::arraySumByKey($stats, [ITEM_MOD_FIRE_RESISTANCE => $pts]);
- break;
- case SPELL_SCHOOL_NATURE:
- Util::arraySumByKey($stats, [ITEM_MOD_NATURE_RESISTANCE => $pts]);
- break;
- case SPELL_SCHOOL_FROST:
- Util::arraySumByKey($stats, [ITEM_MOD_FROST_RESISTANCE => $pts]);
- break;
- case SPELL_SCHOOL_SHADOW:
- Util::arraySumByKey($stats, [ITEM_MOD_SHADOW_RESISTANCE => $pts]);
- break;
- case SPELL_SCHOOL_ARCANE:
- Util::arraySumByKey($stats, [ITEM_MOD_ARCANE_RESISTANCE => $pts]);
- break;
- }
- }
- break;
- case SPELL_AURA_PERIODIC_HEAL: // hp5
- case SPELL_AURA_MOD_REGEN:
- case SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT:
- Util::arraySumByKey($stats, [ITEM_MOD_HEALTH_REGEN => $pts]);
- break;
- case SPELL_AURA_MOD_POWER_REGEN: // mp5
- Util::arraySumByKey($stats, [ITEM_MOD_MANA_REGENERATION => $pts]);
- break;
- case SPELL_AURA_MOD_ATTACK_POWER:
- Util::arraySumByKey($stats, [ITEM_MOD_ATTACK_POWER => $pts]);
- break; // ?carries over to rngatkpwr?
- case SPELL_AURA_MOD_RANGED_ATTACK_POWER:
- Util::arraySumByKey($stats, [ITEM_MOD_RANGED_ATTACK_POWER => $pts]);
- break;
- case SPELL_AURA_MOD_SHIELD_BLOCKVALUE:
- Util::arraySumByKey($stats, [ITEM_MOD_BLOCK_VALUE => $pts]);
- break;
- case SPELL_AURA_MOD_EXPERTISE:
- Util::arraySumByKey($stats, [ITEM_MOD_EXPERTISE_RATING => $pts]);
- break;
- case SPELL_AURA_MOD_TARGET_RESISTANCE:
- if ($mv == 0x7C && $pts < 0)
- Util::arraySumByKey($stats, [ITEM_MOD_SPELL_PENETRATION => -$pts]);
- break;
- }
- }
-
- $data[$this->id] = $stats;
+ // todo: should enchantments be included here...?
+ $data[$this->id]->fromSpell($this->curTpl);
}
return $data;
@@ -390,21 +216,10 @@ public function getProfilerMods()
// weapon hand check: param: slot, class, subclass, value
$whCheck = '$function() { var j, w = _inventory.getInventory()[%d]; if (!w[0] || !g_items[w[0]]) { return 0; } j = g_items[w[0]].jsonequip; return (j.classs == %d && (%d & (1 << (j.subclass)))) ? %d : 0; }';
- $data = $this->getStatGain(); // flat gains
- foreach ($data as $id => &$spellData)
+ $data = []; // flat gains
+ foreach ($this->getStatGain() as $id => $spellData)
{
- foreach ($spellData as $modId => $val)
- {
- if (!isset(Game::$itemMods[$modId]))
- continue;
-
- if ($modId == ITEM_MOD_EXPERTISE_RATING) // not a rating .. pure expertise
- $spellData['exp'] = $val;
- else
- $spellData[Game::$itemMods[$modId]] = $val;
-
- unset($spellData[$modId]);
- }
+ $data[$id] = $spellData->toJson(STAT::FLAG_ITEM | STAT::FLAG_PROFILER);
// apply weapon restrictions
$this->getEntry($id);
@@ -414,8 +229,8 @@ public function getProfilerMods()
if ($class != ITEM_CLASS_WEAPON || !$subClass)
continue;
- foreach ($spellData as $json => $pts)
- $spellData[$json] = [1, 'functionOf', sprintf($whCheck, $slot, $class, $subClass, $pts)];
+ foreach ($data[$id] as $key => $amt)
+ $data[$id][$key] = [1, 'functionOf', sprintf($whCheck, $slot, $class, $subClass, $amt)];
}
// 4 possible modifiers found
@@ -470,22 +285,9 @@ public function getProfilerMods()
foreach ($this->iterate() as $id => $__)
{
- // Priest: Spirit of Redemption is a spell but also a passive. *yaaayyyy*
- if (($this->getField('cuFlags') & SPELL_CU_TALENTSPELL) && $id != 20711)
- continue;
-
- // curious cases of OH MY FUCKING GOD WHY?!
- if ($id == 16268) // Shaman - Spirit Weapons (parry is normaly stored in g_statistics)
- {
- $data[$id]['parrypct'] = [5, 'add'];
- continue;
- }
-
- if ($id == 20550) // Tauren - Endurance (dependant on base health) ... if you are looking for something elegant, look away!
- {
- $data[$id]['health'] = [0.05, 'functionOf', '$function(p) { return g_statistics.combo[p.classs][p.level][5]; }'];
+ // kept for reference - if (($this->getField('cuFlags') & SPELL_CU_TALENTSPELL) && $id != 20711)
+ if (!($this->getField('attributes0') & SPELL_ATTR0_PASSIVE))
continue;
- }
for ($i = 1; $i < 4; $i++)
{
@@ -502,6 +304,15 @@ public function getProfilerMods()
as a flat value (that is equal to the percentage, like they should be). So the stats-table won't show the actual deficit
*/
+
+ // Shaman - Spirit Weapons (16268) (parry is normaly stored in g_statistics)
+ // i should recurse into SPELL_EFFECT_LEARN_SPELL and apply SPELL_EFFECT_PARRY from there
+ if ($id = 16268)
+ {
+ $data[$id]['parrypct'] = [5, 'add'];
+ continue;
+ }
+
switch ($au)
{
case SPELL_AURA_MOD_RESISTANCE_PCT:
@@ -524,7 +335,9 @@ public function getProfilerMods()
$modXByStat($data[$id], null, $pts);
else if ($mv < 0) // all stats
for ($iMod = ITEM_MOD_AGILITY; $iMod <= ITEM_MOD_STAMINA; $iMod++)
- $data[$id][Game::$itemMods[$iMod]] = [$pts / 100, 'percentOf', Game::$itemMods[$iMod]];
+ if ($idx = Stat::getIndexFrom(Stat::IDX_ITEM_MOD, $iMod))
+ if ($key = Stat::getJsonString($idx))
+ $data[$id][$key] = [$pts / 100, 'percentOf', $key];
break;
case SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT:
$mv = $mv ?: SPELL_MAGIC_SCHOOLS;
@@ -579,15 +392,18 @@ public function getProfilerMods()
else if ($mv == POWER_ENERGY)
$data[$id]['energy'] = [$pts / 100, 'percentOf', 'energy'];
else if ($mv == POWER_MANA)
- $data[$id]['mana'] = [$pts / 100, 'percentOf', 'mana'];
+ $data[$id]['mana'] = [$pts / 100, 'percentOf', 'mana'];
else if ($mv == POWER_RAGE)
- $data[$id]['rage'] = [$pts / 100, 'percentOf', 'rage'];
+ $data[$id]['rage'] = [$pts / 100, 'percentOf', 'rage'];
else if ($mv == POWER_RUNIC_POWER)
- $data[$id]['runic'] = [$pts / 100, 'percentOf', 'runic'];
+ $data[$id]['runic'] = [$pts / 100, 'percentOf', 'runic'];
break;
case SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT:
$data[$id]['health'] = [$pts / 100, 'percentOf', 'health'];
break;
+ case SPELL_AURA_MOD_BASE_HEALTH_PCT: // only Tauren - Endurance (20550) ... if you are looking for something elegant, look away!
+ $data[$id]['health'] = [$pts / 100, 'functionOf', '$function(p) { return g_statistics.combo[p.classs][p.level][5]; }'];
+ break;
case SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT:
$data[$id]['block'] = [$pts / 100, 'percentOf', 'block'];
break;
@@ -910,7 +726,7 @@ private function calculateAmountForCurrent(int $effIdx, ?SpellList $altTpl = nul
];
}
- public function canCreateItem()
+ public function canCreateItem() : array
{
$idx = [];
for ($i = 1; $i < 4; $i++)
@@ -921,7 +737,7 @@ public function canCreateItem()
return $idx;
}
- public function canTriggerSpell()
+ public function canTriggerSpell() : array
{
$idx = [];
for ($i = 1; $i < 4; $i++)
@@ -932,7 +748,7 @@ public function canTriggerSpell()
return $idx;
}
- public function canTeachSpell()
+ public function canTeachSpell() : array
{
$idx = [];
for ($i = 1; $i < 4; $i++)
@@ -943,6 +759,16 @@ public function canTeachSpell()
return $idx;
}
+ public function canEnchantmentItem() : array
+ {
+ $idx = [];
+ for ($i = 1; $i < 4; $i++)
+ if (in_array($this->curTpl['effect'.$i.'Id'], SpellList::EFFECTS_ENCHANTMENT))
+ $idx[] = $i;
+
+ return $idx;
+ }
+
public function isChanneledSpell()
{
return $this->curTpl['attributes1'] & (SPELL_ATTR1_CHANNELED_1 | SPELL_ATTR1_CHANNELED_2);
@@ -1243,16 +1069,16 @@ private function resolveVariableString($varParts)
}
// Aura giving combat ratings
- $rType = 0;
+ $rType = [];
if ($aura == SPELL_AURA_MOD_RATING)
- if ($rType = Game::itemModByRatingMask($mv))
+ if ($rType = StatsContainer::convertCombatRating($mv))
$this->scaling[$this->id] = true;
// Aura end
if ($rType)
{
$result[2] = '%s (%s)';
- $result[4] = $rType;
+ $result[4] = $rType[0]; // could be multiple ratings in theory, but not expected to be
}
/* todo: export to and solve formulas in javascript e.g.: spell 10187 - ${$42213m1*8*$} with $mult = ${${$?s31678[${1.05}][${${$?s31677[${1.04}][${${$?s31676[${1.03}][${${$?s31675[${1.02}][${${$?s31674[${1.01}][${1}]}}]}}]}}]}}]}*${$?s12953[${1.06}][${${$?s12952[${1.04}][${${$?s11151[${1.02}][${1}]}}]}}]}}
else if ($this->interactive && ($modStrMin || $modStrMax))
@@ -1336,16 +1162,16 @@ private function resolveVariableString($varParts)
eval("\$max = $max $op $oparg;");
}
// Aura giving combat ratings
- $rType = 0;
+ $rType = [];
if ($aura == SPELL_AURA_MOD_RATING)
- if ($rType = Game::itemModByRatingMask($mv))
+ if ($rType = StatsContainer::convertCombatRating($mv))
$this->scaling[$this->id] = true;
// Aura end
if ($rType)
{
$result[2] = '%s (%s)';
- $result[4] = $rType;
+ $result[4] = $rType[0]; // could be multiple ratings in theory, but not expected to be
}
else if (($modStrMin || $modStrMax) && $this->interactive)
{
@@ -1659,7 +1485,7 @@ functions in use .. caseInsensitive
// step 4: find and eliminate regular variables
$data = $this->handleVariables($data, true);
- // step 5: variable-dependant variable-text
+ // step 5: variable-dependent variable-text
// special case $lONE:ELSE[:ELSE2]; or $|ONE:ELSE[:ELSE2];
while (preg_match('/([\d\.]+)([^\d]*)(\$[l|]:*)([^:]*):([^;]*);/i', $data, $m))
{
diff --git a/includes/utilities.php b/includes/utilities.php
index 6fea7ee7e..b5a6571d4 100644
--- a/includes/utilities.php
+++ b/includes/utilities.php
@@ -501,17 +501,6 @@ abstract class Util
30 => 10, 31 => 10, 32 => 14, 33 => 0, 34 => 0, 35 => 28.75, 36 => 10, 37 => 2.5, 44 => 4.268292513760655
);
- public static $itemFilter = array(
- 20 => 'str', 21 => 'agi', 23 => 'int', 22 => 'sta', 24 => 'spi', 25 => 'arcres', 26 => 'firres', 27 => 'natres',
- 28 => 'frores', 29 => 'shares', 30 => 'holres', 37 => 'mleatkpwr', 32 => 'dps', 35 => 'damagetype', 33 => 'dmgmin1', 34 => 'dmgmax1',
- 36 => 'speed', 38 => 'rgdatkpwr', 39 => 'rgdhitrtng', 40 => 'rgdcritstrkrtng', 41 => 'armor', 44 => 'blockrtng', 43 => 'block', 42 => 'defrtng',
- 45 => 'dodgertng', 46 => 'parryrtng', 48 => 'splhitrtng', 49 => 'splcritstrkrtng', 50 => 'splheal', 51 => 'spldmg', 52 => 'arcsplpwr', 53 => 'firsplpwr',
- 54 => 'frosplpwr', 55 => 'holsplpwr', 56 => 'natsplpwr', 60 => 'healthrgn', 61 => 'manargn', 57 => 'shasplpwr', 77 => 'atkpwr', 78 => 'mlehastertng',
- 79 => 'resirtng', 84 => 'mlecritstrkrtng', 94 => 'splpen', 95 => 'mlehitrtng', 96 => 'critstrkrtng', 97 => 'feratkpwr', 100 => 'nsockets', 101 => 'rgdhastertng',
- 102 => 'splhastertng', 103 => 'hastertng', 114 => 'armorpenrtng', 115 => 'health', 116 => 'mana', 117 => 'exprtng', 119 => 'hitrtng', 123 => 'splpwr',
- 134 => 'mledps', 135 => 'mledmgmin', 136 => 'mledmgmax', 137 => 'mlespeed', 138 => 'rgddps', 139 => 'rgddmgmin', 140 => 'rgddmgmax', 141 => 'rgdspeed'
- );
-
public static $ssdMaskFields = array(
'shoulderMultiplier', 'trinketMultiplier', 'weaponMultiplier', 'primBudged',
'rangedMultiplier', 'clothShoulderArmor', 'leatherShoulderArmor', 'mailShoulderArmor',
diff --git a/pages/enchantment.php b/pages/enchantment.php
index 652a7c734..ef490bb68 100644
--- a/pages/enchantment.php
+++ b/pages/enchantment.php
@@ -98,30 +98,29 @@ protected function generateContent()
switch ($_ty)
{
- case 1:
- case 3:
- case 7:
- $sArr = $this->subject->getField('spells')[$i];
- $spl = $this->subject->getRelSpell($sArr[0]);
- $this->effects[$i]['name'] = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'Type: '.$_ty, Lang::item('trigger', $sArr[1])) : Lang::item('trigger', $sArr[1]);
- $this->effects[$i]['proc'] = $sArr[3];
+ case ENCHANTMENT_TYPE_COMBAT_SPELL:
+ case ENCHANTMENT_TYPE_EQUIP_SPELL:
+ case ENCHANTMENT_TYPE_USE_SPELL:
+ [$spellId, $trigger, $charges, $procChance] = $this->subject->getField('spells')[$i];
+ $spl = $this->subject->getRelSpell($spellId);
+ $this->effects[$i]['name'] = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'Type: '.$_ty, Lang::item('trigger', $trigger)) : Lang::item('trigger', $trigger);
+ $this->effects[$i]['proc'] = $procChance;
$this->effects[$i]['value'] = $_qty ?: null;
$this->effects[$i]['icon'] = array(
- 'name' => !$spl ? Util::ucFirst(Lang::game('spell')).' #'.$sArr[0] : Util::localizedString($spl, 'name'),
- 'id' => $sArr[0],
- 'count' => $sArr[2]
+ 'name' => !$spl ? Util::ucFirst(Lang::game('spell')).' #'.$spellId : Util::localizedString($spl, 'name'),
+ 'id' => $spellId,
+ 'count' => $charges
);
break;
- case 5:
- if ($_obj < 2) // [mana, health] are on [0, 1] respectively and are expected on [1, 2] ..
- $_obj++; // 0 is weaponDmg .. ehh .. i messed up somewhere
-
- $this->effects[$i]['tip'] = [$_obj, Game::$itemMods[$_obj]];
+ case ENCHANTMENT_TYPE_STAT:
+ if ($idx = Stat::getIndexFrom(Stat::IDX_ITEM_MOD, $_obj))
+ if ($jsonStat = Stat::getJsonString($idx))
+ $this->effects[$i]['tip'] = [$_obj, $jsonStat];
// DO NOT BREAK!
- case 2:
- case 6:
- case 8:
- case 4:
+ case ENCHANTMENT_TYPE_DAMAGE:
+ case ENCHANTMENT_TYPE_TOTEM:
+ case ENCHANTMENT_TYPE_PRISMATIC_SOCKET:
+ case ENCHANTMENT_TYPE_RESISTANCE:
$this->effects[$i]['name'] = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'Type: '.$_ty, Lang::enchantment('types', $_ty)) : Lang::enchantment('types', $_ty);
$this->effects[$i]['value'] = $_qty;
if ($_ty == 4)
diff --git a/pages/enchantments.php b/pages/enchantments.php
index 2f772610a..d66a3b8e2 100644
--- a/pages/enchantments.php
+++ b/pages/enchantments.php
@@ -59,8 +59,8 @@ protected function generateContent()
$this->filter['initData']['sc'] = $x;
$xCols = $this->filterObj->getExtraCols();
- foreach (Util::$itemFilter as $fiId => $str)
- if (array_column($tabData['data'], $str))
+ foreach (Stat::getFilterCriteriumIdFor() as $idx => $fiId)
+ if (array_column($tabData['data'], Stat::getJsonString($idx)))
$xCols[] = $fiId;
if (array_column($tabData['data'], 'dmg'))
diff --git a/pages/item.php b/pages/item.php
index feb51c01a..0b0f2ec36 100644
--- a/pages/item.php
+++ b/pages/item.php
@@ -1112,12 +1112,12 @@ protected function generateXML()
if ($_ = $this->subject->getField('cooldown')) // cooldown
$json['cooldown'] = $_ / 1000;
- foreach ($this->subject->itemMods[$this->typeId] as $mod => $qty)
- $json[$mod] = $qty;
+ Util::arraySumByKey($json, $this->subject->jsonStats[$this->typeId] ?? []);
foreach ($this->subject->json[$this->typeId] as $name => $qty)
- if (in_array($name, Util::$itemFilter))
- $json[$name] = $qty;
+ if ($idx = Stat::getIndexFrom(Stat::IDX_JSON_STR, $name))
+ if (Stat::getFilterCriteriumId($idx))
+ $json[$name] = $qty;
$xml->addChild('jsonEquip')->addCData(substr(json_encode($json), 1, -1));
@@ -1125,8 +1125,8 @@ protected function generateXML()
if ($onUse = $this->subject->getOnUseStats())
{
$j = '';
- foreach ($onUse as $idx => $qty)
- $j .= ',"'.Game::$itemMods[$idx].'":'.$qty;
+ foreach ($onUse->toJson() as $key => $amt)
+ $j .= ',"'.$key.'":'.$amt;
$xml->addChild('jsonUse')->addCData(substr($j, 1));
}
diff --git a/setup/db_structure.sql b/setup/db_structure.sql
index 89f400ccb..fdb3ad0e5 100644
--- a/setup/db_structure.sql
+++ b/setup/db_structure.sql
@@ -1103,88 +1103,88 @@ DROP TABLE IF EXISTS `aowow_item_stats`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `aowow_item_stats` (
- `type` smallint unsigned NOT NULL,
- `typeId` mediumint unsigned NOT NULL,
- `nsockets` tinyint unsigned NOT NULL DEFAULT 0,
- `dmgmin1` smallint unsigned NOT NULL DEFAULT 0,
- `dmgmax1` smallint unsigned NOT NULL DEFAULT 0,
- `speed` float(8,2) NOT NULL DEFAULT 0.00,
- `dps` float(8,2) NOT NULL DEFAULT 0.00,
- `mledmgmin` smallint unsigned NOT NULL DEFAULT 0,
- `mledmgmax` smallint unsigned NOT NULL DEFAULT 0,
- `mlespeed` float(8,2) NOT NULL DEFAULT 0.00,
- `mledps` float(8,2) NOT NULL DEFAULT 0.00,
- `rgddmgmin` smallint unsigned NOT NULL DEFAULT 0,
- `rgddmgmax` smallint unsigned NOT NULL DEFAULT 0,
- `rgdspeed` float(8,2) NOT NULL DEFAULT 0.00,
- `rgddps` float(8,2) NOT NULL DEFAULT 0.00,
- `dmg` float(8,2) NOT NULL DEFAULT 0.00,
- `damagetype` tinyint NOT NULL DEFAULT 0,
- `mana` mediumint NOT NULL DEFAULT 0,
- `health` mediumint NOT NULL DEFAULT 0,
- `agi` mediumint NOT NULL DEFAULT 0,
- `str` mediumint NOT NULL DEFAULT 0,
- `int` mediumint NOT NULL DEFAULT 0,
- `spi` mediumint NOT NULL DEFAULT 0,
- `sta` mediumint NOT NULL DEFAULT 0,
- `energy` mediumint NOT NULL DEFAULT 0,
- `rage` mediumint NOT NULL DEFAULT 0,
- `focus` mediumint NOT NULL DEFAULT 0,
- `runicpwr` mediumint NOT NULL DEFAULT 0,
- `defrtng` mediumint NOT NULL DEFAULT 0,
- `dodgertng` mediumint NOT NULL DEFAULT 0,
- `parryrtng` mediumint NOT NULL DEFAULT 0,
- `blockrtng` mediumint NOT NULL DEFAULT 0,
- `mlehitrtng` mediumint NOT NULL DEFAULT 0,
- `rgdhitrtng` mediumint NOT NULL DEFAULT 0,
- `splhitrtng` mediumint NOT NULL DEFAULT 0,
- `mlecritstrkrtng` mediumint NOT NULL DEFAULT 0,
- `rgdcritstrkrtng` mediumint NOT NULL DEFAULT 0,
- `splcritstrkrtng` mediumint NOT NULL DEFAULT 0,
- `_mlehitrtng` mediumint NOT NULL DEFAULT 0,
- `_rgdhitrtng` mediumint NOT NULL DEFAULT 0,
- `_splhitrtng` mediumint NOT NULL DEFAULT 0,
- `_mlecritstrkrtng` mediumint NOT NULL DEFAULT 0,
- `_rgdcritstrkrtng` mediumint NOT NULL DEFAULT 0,
- `_splcritstrkrtng` mediumint NOT NULL DEFAULT 0,
- `mlehastertng` mediumint NOT NULL DEFAULT 0,
- `rgdhastertng` mediumint NOT NULL DEFAULT 0,
- `splhastertng` mediumint NOT NULL DEFAULT 0,
- `hitrtng` mediumint NOT NULL DEFAULT 0,
- `critstrkrtng` mediumint NOT NULL DEFAULT 0,
- `_hitrtng` mediumint NOT NULL DEFAULT 0,
- `_critstrkrtng` mediumint NOT NULL DEFAULT 0,
- `resirtng` mediumint NOT NULL DEFAULT 0,
- `hastertng` mediumint NOT NULL DEFAULT 0,
- `exprtng` mediumint NOT NULL DEFAULT 0,
- `atkpwr` mediumint NOT NULL DEFAULT 0,
- `mleatkpwr` mediumint NOT NULL DEFAULT 0,
- `rgdatkpwr` mediumint NOT NULL DEFAULT 0,
- `feratkpwr` mediumint NOT NULL DEFAULT 0,
- `splheal` mediumint NOT NULL DEFAULT 0,
- `spldmg` mediumint NOT NULL DEFAULT 0,
- `manargn` mediumint NOT NULL DEFAULT 0,
- `armorpenrtng` mediumint NOT NULL DEFAULT 0,
- `splpwr` mediumint NOT NULL DEFAULT 0,
- `healthrgn` mediumint NOT NULL DEFAULT 0,
- `splpen` mediumint NOT NULL DEFAULT 0,
- `block` mediumint NOT NULL DEFAULT 0,
- `mastrtng` mediumint NOT NULL DEFAULT 0,
- `armor` mediumint NOT NULL DEFAULT 0,
- `armorbonus` mediumint NOT NULL DEFAULT 0,
- `firres` mediumint NOT NULL DEFAULT 0,
- `frores` mediumint NOT NULL DEFAULT 0,
- `holres` mediumint NOT NULL DEFAULT 0,
- `shares` mediumint NOT NULL DEFAULT 0,
- `natres` mediumint NOT NULL DEFAULT 0,
- `arcres` mediumint NOT NULL DEFAULT 0,
- `firsplpwr` mediumint NOT NULL DEFAULT 0,
- `frosplpwr` mediumint NOT NULL DEFAULT 0,
- `holsplpwr` mediumint NOT NULL DEFAULT 0,
- `shasplpwr` mediumint NOT NULL DEFAULT 0,
- `natsplpwr` mediumint NOT NULL DEFAULT 0,
- `arcsplpwr` mediumint NOT NULL DEFAULT 0,
- PRIMARY KEY (`typeId`,`type`)
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(8) NOT NULL,
+ `nsockets` tinyint(3) unsigned NULL,
+ `dps` float(8,2) NULL,
+ `damagetype` tinyint(4) NULL,
+ `dmgmin1` mediumint(5) unsigned NULL,
+ `dmgmax1` mediumint(5) unsigned NULL,
+ `speed` float(8,2) NULL,
+ `mledps` float(8,2) NULL,
+ `mledmgmin` mediumint(5) unsigned NULL,
+ `mledmgmax` mediumint(5) unsigned NULL,
+ `mlespeed` float(8,2) NULL,
+ `rgddps` float(8,2) NULL,
+ `rgddmgmin` mediumint(5) unsigned NULL,
+ `rgddmgmax` mediumint(5) unsigned NULL,
+ `rgdspeed` float(8,2) NULL,
+ `dmg` float(8,2) NULL,
+ `mana` mediumint(6) NULL,
+ `health` mediumint(6) NULL,
+ `agi` mediumint(6) NULL,
+ `str` mediumint(6) NULL,
+ `int` mediumint(6) NULL,
+ `spi` mediumint(6) NULL,
+ `sta` mediumint(6) NULL,
+ `energy` mediumint(6) NULL,
+ `rage` mediumint(6) NULL,
+ `focus` mediumint(6) NULL,
+ `runic` mediumint(6) NULL,
+ `defrtng` mediumint(6) NULL,
+ `dodgertng` mediumint(6) NULL,
+ `parryrtng` mediumint(6) NULL,
+ `blockrtng` mediumint(6) NULL,
+ `mlehitrtng` mediumint(6) NULL,
+ `rgdhitrtng` mediumint(6) NULL,
+ `splhitrtng` mediumint(6) NULL,
+ `mlecritstrkrtng` mediumint(6) NULL,
+ `rgdcritstrkrtng` mediumint(6) NULL,
+ `splcritstrkrtng` mediumint(6) NULL,
+ `_mlehitrtng` mediumint(6) NULL,
+ `_rgdhitrtng` mediumint(6) NULL,
+ `_splhitrtng` mediumint(6) NULL,
+ `_mlecritstrkrtng` mediumint(6) NULL,
+ `_rgdcritstrkrtng` mediumint(6) NULL,
+ `_splcritstrkrtng` mediumint(6) NULL,
+ `mlehastertng` mediumint(6) NULL,
+ `rgdhastertng` mediumint(6) NULL,
+ `splhastertng` mediumint(6) NULL,
+ `hitrtng` mediumint(6) NULL,
+ `critstrkrtng` mediumint(6) NULL,
+ `_hitrtng` mediumint(6) NULL,
+ `_critstrkrtng` mediumint(6) NULL,
+ `resirtng` mediumint(6) NULL,
+ `hastertng` mediumint(6) NULL,
+ `exprtng` mediumint(6) NULL,
+ `atkpwr` mediumint(6) NULL,
+ `mleatkpwr` mediumint(6) NULL,
+ `rgdatkpwr` mediumint(6) NULL,
+ `feratkpwr` mediumint(6) NULL,
+ `splheal` mediumint(6) NULL,
+ `spldmg` mediumint(6) NULL,
+ `manargn` mediumint(6) NULL,
+ `armorpenrtng` mediumint(6) NULL,
+ `splpwr` mediumint(6) NULL,
+ `healthrgn` mediumint(6) NULL,
+ `splpen` mediumint(6) NULL,
+ `block` mediumint(6) NULL,
+ `mastrtng` mediumint(6) NULL,
+ `armor` mediumint(6) NULL,
+ `armorbonus` mediumint(6) NULL,
+ `firres` mediumint(6) NULL,
+ `frores` mediumint(6) NULL,
+ `holres` mediumint(6) NULL,
+ `shares` mediumint(6) NULL,
+ `natres` mediumint(6) NULL,
+ `arcres` mediumint(6) NULL,
+ `firsplpwr` mediumint(6) NULL,
+ `frosplpwr` mediumint(6) NULL,
+ `holsplpwr` mediumint(6) NULL,
+ `shasplpwr` mediumint(6) NULL,
+ `natsplpwr` mediumint(6) NULL,
+ `arcsplpwr` mediumint(6) NULL,
+ PRIMARY KEY (`type`,`typeId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1601,7 +1601,6 @@ CREATE TABLE `aowow_itemset` (
`bonusText_loc4` text DEFAULT NULL,
`bonusText_loc6` text DEFAULT NULL,
`bonusText_loc8` text DEFAULT NULL,
- `bonusParsed` varchar(256) DEFAULT NULL COMMENT 'serialized itemMods',
`npieces` tinyint NOT NULL DEFAULT 0,
`minLevel` smallint NOT NULL DEFAULT 0,
`maxLevel` smallint NOT NULL DEFAULT 0,
@@ -3228,7 +3227,7 @@ UNLOCK TABLES;
LOCK TABLES `aowow_dbversion` WRITE;
/*!40000 ALTER TABLE `aowow_dbversion` DISABLE KEYS */;
-INSERT INTO `aowow_dbversion` VALUES (1717354215,0,NULL,NULL);
+INSERT INTO `aowow_dbversion` VALUES (1718468661,0,NULL,NULL);
/*!40000 ALTER TABLE `aowow_dbversion` ENABLE KEYS */;
UNLOCK TABLES;
diff --git a/setup/tools/filegen/enchants.func.php b/setup/tools/filegen/enchants.func.php
index 30c9b646f..efd043c86 100644
--- a/setup/tools/filegen/enchants.func.php
+++ b/setup/tools/filegen/enchants.func.php
@@ -145,7 +145,7 @@ function enchants()
'skill' => -1, // modified if skill
'slots' => [], // determined per spell but set per item
'enchantment' => $enchantments->getField('name', true),
- 'jsonequip' => $enchantments->getStatGain(),
+ 'jsonequip' => $enchantments->getStatGainForCurrent(),
'temp' => 0, // always 0
'classes' => 0, // modified by item
'gearscore' => 0 // set later
diff --git a/setup/tools/filegen/gems.func.php b/setup/tools/filegen/gems.func.php
index 810631a43..7eb15f213 100644
--- a/setup/tools/filegen/gems.func.php
+++ b/setup/tools/filegen/gems.func.php
@@ -73,7 +73,7 @@ function gems()
{
if (!$enchantments->getEntry($pop['enchId']))
{
- CLI::write(' * could not find enchantment #'.$pop['enchId'].' referenced by item #'.$gem['itemId'], CLI::LOG_WARN);
+ CLI::write(' * could not find enchantment #'.$pop['enchId'].' referenced by item #'.$pop['itemId'], CLI::LOG_WARN);
continue;
}
@@ -82,10 +82,10 @@ function gems()
'quality' => $pop['quality'],
'icon' => strToLower($pop['icon']),
'enchantment' => $enchantments->getField('name', true),
- 'jsonequip' => $enchantments->getStatGain(),
+ 'jsonequip' => $enchantments->getStatGainForCurrent(),
'colors' => $pop['colors'],
'expansion' => $pop['expansion'],
- 'gearscore' => Util::getGemScore($pop['itemLevel'], $pop['quality'], $pop['requiredSkill'] == 755, $pop['itemId'])
+ 'gearscore' => Util::getGemScore($pop['itemLevel'], $pop['quality'], $pop['requiredSkill'] == SKILL_JEWELCRAFTING, $pop['itemId'])
);
}
diff --git a/setup/tools/filegen/itemsets.func.php b/setup/tools/filegen/itemsets.func.php
index a0ba2466e..80ccd69eb 100644
--- a/setup/tools/filegen/itemsets.func.php
+++ b/setup/tools/filegen/itemsets.func.php
@@ -81,40 +81,38 @@ function itemsets()
if ($set['item'.$i])
$setOut['pieces'][] = $set['item'.$i];
+ $_spells = [];
for ($i = 1; $i < 9; $i++)
{
- if (!$set['bonus'.$i] || !$set['spell'.$i])
+ if (!$set['spell'.$i] || isset($jsonBonus[$set['spell'.$i]]))
continue;
- // costy and locale-independant -> cache
- if (!isset($jsonBonus[$set['spell'.$i]]))
- $jsonBonus[$set['spell'.$i]] = (new SpellList(array(['s.id', (int)$set['spell'.$i]])))->getStatGain()[$set['spell'.$i]];
-
- if (!isset($setOut['setbonus'][$set['bonus'.$i]]))
- $setOut['setbonus'][$set['bonus'.$i]] = $jsonBonus[$set['spell'.$i]];
- else
- Util::arraySumByKey($setOut['setbonus'][$set['bonus'.$i]], $jsonBonus[$set['spell'.$i]]);
+ $_spells[] = $set['spell'.$i];
}
- foreach ($setOut['setbonus'] as $k => $v)
+ // costy and locale-independant -> cache
+ if ($_spells)
+ $jsonBonus += (new SpellList(array(['s.id', $_spells])))->getStatGain();
+
+ $setbonus = [];
+ for ($i = 1; $i < 9; $i++)
{
- if (empty($v))
- unset($setOut['setbonus'][$k]);
- else
+ $itemQty = $set['bonus'.$i];
+ $itemSpl = $set['spell'.$i];
+ if (!$itemQty || !$itemSpl || !isset($jsonBonus[$itemSpl]))
+ continue;
+
+ if ($x = $jsonBonus[$itemSpl]->toJson(Stat::FLAG_ITEM))
{
- foreach ($v as $sk => $sv)
- {
- if ($str = Game::$itemMods[$sk])
- {
- $setOut['setbonus'][$k][$str] = $sv;
- unset($setOut['setbonus'][$k][$sk]);
- }
- }
+ if (!isset($setbonus[$itemQty]))
+ $setbonus[$itemQty] = [];
+
+ Util::arraySumByKey($setbonus[$itemQty], $x);
}
}
- if (empty($setOut['setbonus']))
- unset($setOut['setbonus']);
+ if ($setbonus)
+ $setOut['setbonus'] = $setbonus;
$itemsetOut[$setOut['id']] = $setOut;
}
diff --git a/setup/tools/filegen/talentCalc.func.php b/setup/tools/filegen/talentCalc.func.php
index f3b75ab3c..a8e9865aa 100644
--- a/setup/tools/filegen/talentCalc.func.php
+++ b/setup/tools/filegen/talentCalc.func.php
@@ -122,23 +122,23 @@ function talentCalc()
}
$result[$tabIdx]['t'][$talentIdx] = array(
- 'i' => $i,
- 'n' => $n,
- 'm' => $m,
- 'd' => $d,
- 's' => $s,
- 'x' => $x,
- 'y' => $y,
- 'j' => $j
+ 'i' => $i, // talent id
+ 'n' => $n, // talent name
+ 'm' => $m, // maxRank
+ 'd' => $d, // [descriptions]
+ 's' => $s, // [spellIds]
+ 'x' => $x, // col #
+ 'y' => $y, // row #
+ 'j' => $j // spell mods applied when used in profiler
);
- if (isset($r))
+ if (isset($r)) // [reqTalentId, reqRank]
$result[$tabIdx]['t'][$talentIdx]['r'] = $r;
- if (!empty($t))
+ if (!empty($t)) // talentspell tooltip
$result[$tabIdx]['t'][$talentIdx]['t'] = $t;
- if (!empty($f))
+ if (!empty($f)) // [petFamilyId]
$result[$tabIdx]['t'][$talentIdx]['f'] = $f;
if ($class)
diff --git a/setup/tools/sqlgen/item_stats.func.php b/setup/tools/sqlgen/item_stats.func.php
index 30485c7d3..e635bc6b6 100644
--- a/setup/tools/sqlgen/item_stats.func.php
+++ b/setup/tools/sqlgen/item_stats.func.php
@@ -9,17 +9,17 @@
class ItemStatSetup extends ItemList
{
- private $statCols = [];
+ private $relSpells = [];
+ private $relEnchants = [];
- public function __construct($start, $limit, array $ids, array $enchStats)
+ public function __construct($start, $limit, array $ids, array $relEnchants, array $relSpells)
{
- $this->statCols = DB::Aowow()->selectCol('SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_NAME` LIKE "%item_stats"');
$this->queryOpts['i']['o'] = 'i.id ASC';
unset($this->queryOpts['is']); // do not reference the stats table we are going to write to
$conditions = array(
['i.id', $start, '>'],
- ['class', [ITEM_CLASS_WEAPON, ITEM_CLASS_GEM, ITEM_CLASS_ARMOR, ITEM_CLASS_CONSUMABLE]],
+ ['class', [ITEM_CLASS_WEAPON, ITEM_CLASS_GEM, ITEM_CLASS_ARMOR, ITEM_CLASS_CONSUMABLE, ITEM_CLASS_AMMUNITION]],
$limit
);
@@ -28,102 +28,26 @@ public function __construct($start, $limit, array $ids, array $enchStats)
parent::__construct($conditions);
- $this->enchParsed = $enchStats;
+ $this->relSpells = $relSpells;
+ $this->relEnchants = $relEnchants;
}
public function writeStatsTable()
{
- $enchantments = []; // buffer Ids for lookup id => src; src>0: socketBonus; src<0: gemEnchant
-
- foreach ($this->iterate() as $__)
+ foreach ($this->iterate() as $id => $curTpl)
{
- $this->itemMods[$this->id] = [];
-
- // also occurs as seperate field (gets summed in calculation but not in tooltip)
- if ($_ = $this->getField('block'))
- $this->itemMods[$this->id][ITEM_MOD_BLOCK_VALUE] = $_;
-
- // convert itemMods to stats
- for ($h = 1; $h <= 10; $h++)
- {
- $mod = $this->curTpl['statType'.$h];
- $val = $this->curTpl['statValue'.$h];
- if (!$mod || !$val)
- continue;
+ $spellIds = [];
- Util::arraySumByKey($this->itemMods[$this->id], [$mod => $val]);
- }
-
- // convert spells to stats
- $equipSpells = [];
- for ($h = 1; $h <= 5; $h++)
- {
- if ($this->curTpl['spellId'.$h] <= 0)
- continue;
+ for ($i = 1; $i <= 5; $i++)
+ if ($this->curTpl['spellId'.$i] > 0 && !isset($this->relSpells[$this->curTpl['spellId'.$i]]) && (($this->curTpl['class'] == ITEM_CLASS_CONSUMABLE && $this->curTpl['spellTrigger'.$i] == SPELL_TRIGGER_USE) || $this->curTpl['spellTrigger'.$i] == SPELL_TRIGGER_EQUIP))
+ $spellIds[] = $this->curTpl['spellId'.$i];
- // armor & weapons only onEquip && consumables only onUse
- if (!(in_array($this->curTpl['class'], [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR]) && $this->curTpl['spellTrigger'.$h] == SPELL_TRIGGER_EQUIP) &&
- !( $this->curTpl['class'] == ITEM_CLASS_CONSUMABLE && $this->curTpl['spellTrigger'.$h] == SPELL_TRIGGER_USE))
- continue;
+ if ($spellIds) // array_merge kills the keys
+ $this->relSpells = array_replace($this->relSpells, DB::Aowow()->select('SELECT *, id AS ARRAY_KEY FROM ?_spell WHERE id IN (?a)', $spellIds));
- $equipSpells[] = $this->curTpl['spellId'.$h];
- }
-
- if ($equipSpells)
- {
- $eqpSplList = new SpellList(array(['s.id', $equipSpells]));
- foreach ($eqpSplList->getStatGain() as $stats)
- Util::arraySumByKey($this->itemMods[$this->id], $stats);
- }
-
- // prepare: convert enchantments to stats
- if (!empty($this->json[$this->id]['socketbonus']))
- $enchantments[$this->json[$this->id]['socketbonus']][] = $this->id;
- if ($geId = $this->curTpl['gemEnchantmentId'])
- $enchantments[$geId][] = -$this->id;
- }
-
- // execute: convert enchantments to stats
- // and merge enchantments back
- foreach ($enchantments as $eId => $items)
- {
- if (empty($this->enchParsed[$eId]))
- continue;
-
- foreach ($items as $item)
- {
- if ($item > 0) // apply socketBonus
- $this->json[$item]['socketbonusstat'] = $this->enchParsed[$eId];
- else /* if ($item < 0) */ // apply gemEnchantment
- Util::arraySumByKey($this->json[-$item], $this->enchParsed[$eId]);
- }
- }
-
- // collect data and write to DB
- foreach ($this->iterate() as $__)
- {
- $updateFields = ['type' => Type::ITEM, 'typeId' => $this->id];
-
- foreach (@$this->json[$this->id] as $k => $v)
- {
- if (!in_array($k, $this->statCols) || !$v || $k == 'id')
- continue;
-
- $updateFields[$k] = number_format($v, 2, '.', '');
- }
-
- if (isset($this->itemMods[$this->id]))
- {
- foreach ($this->itemMods[$this->id] as $k => $v)
- {
- if (!$v)
- continue;
- if ($str = Game::$itemMods[$k])
- $updateFields[$str] = number_format($v, 2, '.', '');
- }
- }
-
- DB::Aowow()->query('REPLACE INTO ?_item_stats (?#) VALUES (?a)', array_keys($updateFields), array_values($updateFields));
+ // fromItem: itemMods, spell, enchants from template - fromJson: calculated stats (feralAP, dps, ...)
+ if ($stats = (new StatsContainer($this->relSpells, $this->relEnchants))->fromItem($curTpl)->fromJson($this->json[$id])->toJson(Stat::FLAG_ITEM | Stat::FLAG_SERVERSIDE))
+ DB::Aowow()->query('INSERT INTO ?_item_stats (?#) VALUES (?a)', array_merge(['type', 'typeId'], array_keys($stats)), array_merge([Type::ITEM, $this->id], array_values($stats)));
}
}
}
@@ -135,127 +59,50 @@ public function writeStatsTable()
protected $tblDependencyAowow = ['items', 'spell'];
protected $dbcSourceFiles = ['spellitemenchantment'];
- private function enchantment_stats() : array
+ private $relSpells = [];
+
+ private function enchantment_stats(?int &$total = 0, ?int &$effective = 0) : array
{
- $statCols = DB::Aowow()->selectCol('SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_NAME` LIKE "%item_stats"');
- $enchants = DB::Aowow()->select('SELECT *, id AS ARRAY_KEY FROM dbc_spellitemenchantment');
- $spells = [];
- $spellStats = [];
+ $enchants = DB::Aowow()->select('SELECT *, id AS ARRAY_KEY FROM dbc_spellitemenchantment');
+ $spells = [];
+ $result = [];
+ $effective = 0;
+ $total = count($enchants);
foreach ($enchants as $eId => $e)
- {
- for ($i = 1; $i <=3; $i++)
- {
- // trigger: onEquip + valid SpellId
- if ($e['object'.$i] > 0 && $e['type'.$i] == 3)
+ for ($i = 1; $i <= 3; $i++)
+ if ($e['object'.$i] > 0 && $e['type'.$i] == ENCHANTMENT_TYPE_EQUIP_SPELL)
$spells[] = $e['object'.$i];
- }
- }
if ($spells)
- $spellStats = (new SpellList(array(['id', $spells], Cfg::get('SQL_LIMIT_NONE'))))->getStatGain();
+ $this->relSpells = DB::Aowow()->select('SELECT *, id AS ARRAY_KEY FROM ?_spell WHERE id IN (?a)', $spells);
- $result = [];
foreach ($enchants as $eId => $e)
- {
- // parse stats
- $result[$eId] = [];
- for ($h = 1; $h <= 3; $h++)
- {
- $obj = (int)$e['object'.$h];
- $val = (int)$e['amount'.$h];
-
- switch ($e['type'.$h])
- {
- case 6: // TYPE_TOTEM +AmountX as DPS (Rockbiter)
- $result[$eId]['dps'] = $val; // we do not use dps as itemMod, so apply it directly
- $obj = null;
- break;
- case 2: // TYPE_DAMAGE +AmountX damage
- $obj = ITEM_MOD_WEAPON_DMG;
- break;
- // case 1: // TYPE_COMBAT_SPELL proc spell from ObjectX (amountX == procChance)
- // case 7: // TYPE_USE_SPELL Engineering gadgets
- case 3: // TYPE_EQUIP_SPELL Spells from ObjectX (use of amountX?)
- if (!empty($spellStats[$obj]))
- foreach ($spellStats[$obj] as $mod => $val)
- if ($str = Game::$itemMods[$mod])
- Util::arraySumByKey($result[$eId], [$str => $val]);
-
- $obj = null;
- break;
- case 4: // TYPE_RESISTANCE +AmountX resistance for ObjectX School
- switch ($obj)
- {
- case 0: // Physical
- $obj = ITEM_MOD_ARMOR;
- break;
- case 1: // Holy
- $obj = ITEM_MOD_HOLY_RESISTANCE;
- break;
- case 2: // Fire
- $obj = ITEM_MOD_FIRE_RESISTANCE;
- break;
- case 3: // Nature
- $obj = ITEM_MOD_NATURE_RESISTANCE;
- break;
- case 4: // Frost
- $obj = ITEM_MOD_FROST_RESISTANCE;
- break;
- case 5: // Shadow
- $obj = ITEM_MOD_SHADOW_RESISTANCE;
- break;
- case 6: // Arcane
- $obj = ITEM_MOD_ARCANE_RESISTANCE;
- break;
- default:
- $obj = null;
- }
- break;
- case 5: // TYPE_STAT +AmountX for Statistic by type of ObjectX
- if ($obj < 2) // [mana, health] are on [0, 1] respectively and are expected on [1, 2] ..
- $obj++; // 0 is weaponDmg .. ehh .. i messed up somewhere
-
- break; // stats are directly assigned below
- case 8: // TYPE_PRISMATIC_SOCKET Extra Sockets AmountX as socketCount (ignore)
- $result[$eId]['nsockets'] = $val; // there is no itemmod for sockets, so apply it directly
- default: // TYPE_NONE dnd stuff; skip assignment below
- $obj = null;
- }
-
- if ($obj !== null)
- if ($str = Game::$itemMods[$obj]) // check if we use these mods
- Util::arraySumByKey($result[$eId], [$str => $val]);
- }
-
- $updateCols = ['type' => Type::ENCHANTMENT, 'typeId' => $eId];
- foreach ($result[$eId] as $k => $v)
+ if ($result[$eId] = (new StatsContainer($this->relSpells))->fromEnchantment($e)->toJson(Stat::FLAG_ITEM | Stat::FLAG_SERVERSIDE))
{
- if (!in_array($k, $statCols) || !$v || $k == 'id')
- continue;
-
- $updateCols[$k] = number_format($v, 2, '.', '');
+ DB::Aowow()->query('INSERT INTO ?_item_stats (?#) VALUES (?a)', array_merge(['type', 'typeId'], array_keys($result[$eId])), array_merge([Type::ENCHANTMENT, $eId], array_values($result[$eId])));
+ $effective++;
}
- DB::Aowow()->query('REPLACE INTO ?_item_stats (?#) VALUES (?a)', array_keys($updateCols), array_values($updateCols));
- }
-
- return $result;
+ return $enchants;
}
public function generate(array $ids = []) : bool
{
- $offset = 0;
+ DB::Aowow()->query('TRUNCATE ?_item_stats');
CLI::write(' - applying stats for enchantments');
- $enchStats = $this->enchantment_stats();
- CLI::write(' '.count($enchStats).' enchantments parsed');
+
+ $enchStats = $this->enchantment_stats($total, $effective);
+ CLI::write(' '.$effective.'+'.($total - $effective).' enchantments parsed');
+
CLI::write(' - applying stats for items');
$i = 0;
+ $offset = 0;
while (true)
{
- $items = new ItemStatSetup($offset, SqlGen::$sqlBatchSize, $ids, $enchStats);
+ $items = new ItemStatSetup($offset, SqlGen::$sqlBatchSize, $ids, $enchStats, $this->relSpells);
if ($items->error)
break;
diff --git a/setup/tools/sqlgen/itemset.func.php b/setup/tools/sqlgen/itemset.func.php
index b6f0f152f..93db167c3 100644
--- a/setup/tools/sqlgen/itemset.func.php
+++ b/setup/tools/sqlgen/itemset.func.php
@@ -213,22 +213,16 @@ public function generate(array $ids = []) : bool
/* calc statbonuses */
/********************/
- $gains = $spells = $mods = [];
+ $spells = [];
for ($i = 1; $i < 9; $i++)
if ($setData['spellId'.$i] > 0 && $setData['itemCount'.$i] > 0)
$spells[$i] = [$setData['spellId'.$i], $setData['itemCount'.$i]];
$bonusSpells = new SpellList(array(['s.id', array_column($spells, 0)]));
- $mods = $bonusSpells->getStatGain();
$spells = array_pad($spells, 8, [0, 0]);
- for ($i = 1; $i < 9; $i++)
- if ($setData['itemCount'.$i] > 0 && !empty($mods[$setData['spellId'.$i]]))
- $gains[$setData['itemCount'.$i]] = $mods[$setData['spellId'.$i]];
-
- $row['bonusParsed'] = serialize($gains);
foreach (array_column($spells, 0) as $idx => $spellId)
$row['spell'.($idx+1)] = $spellId;
foreach (array_column($spells, 1) as $idx => $nItems)
diff --git a/setup/updates/1718468660_01.sql b/setup/updates/1718468660_01.sql
new file mode 100644
index 000000000..ba6fef739
--- /dev/null
+++ b/setup/updates/1718468660_01.sql
@@ -0,0 +1,91 @@
+ALTER TABLE `aowow_itemset`
+ DROP COLUMN `bonusParsed`;
+
+DROP TABLE IF EXISTS `aowow_item_stats`;
+CREATE TABLE `aowow_item_stats` (
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(8) NOT NULL,
+ `nsockets` tinyint(3) unsigned NULL,
+ `dps` float(8,2) NULL,
+ `damagetype` tinyint(4) NULL,
+ `dmgmin1` mediumint(5) unsigned NULL,
+ `dmgmax1` mediumint(5) unsigned NULL,
+ `speed` float(8,2) NULL,
+ `mledps` float(8,2) NULL,
+ `mledmgmin` mediumint(5) unsigned NULL,
+ `mledmgmax` mediumint(5) unsigned NULL,
+ `mlespeed` float(8,2) NULL,
+ `rgddps` float(8,2) NULL,
+ `rgddmgmin` mediumint(5) unsigned NULL,
+ `rgddmgmax` mediumint(5) unsigned NULL,
+ `rgdspeed` float(8,2) NULL,
+ `dmg` float(8,2) NULL,
+ `mana` mediumint(6) NULL,
+ `health` mediumint(6) NULL,
+ `agi` mediumint(6) NULL,
+ `str` mediumint(6) NULL,
+ `int` mediumint(6) NULL,
+ `spi` mediumint(6) NULL,
+ `sta` mediumint(6) NULL,
+ `energy` mediumint(6) NULL,
+ `rage` mediumint(6) NULL,
+ `focus` mediumint(6) NULL,
+ `runic` mediumint(6) NULL,
+ `defrtng` mediumint(6) NULL,
+ `dodgertng` mediumint(6) NULL,
+ `parryrtng` mediumint(6) NULL,
+ `blockrtng` mediumint(6) NULL,
+ `mlehitrtng` mediumint(6) NULL,
+ `rgdhitrtng` mediumint(6) NULL,
+ `splhitrtng` mediumint(6) NULL,
+ `mlecritstrkrtng` mediumint(6) NULL,
+ `rgdcritstrkrtng` mediumint(6) NULL,
+ `splcritstrkrtng` mediumint(6) NULL,
+ `_mlehitrtng` mediumint(6) NULL,
+ `_rgdhitrtng` mediumint(6) NULL,
+ `_splhitrtng` mediumint(6) NULL,
+ `_mlecritstrkrtng` mediumint(6) NULL,
+ `_rgdcritstrkrtng` mediumint(6) NULL,
+ `_splcritstrkrtng` mediumint(6) NULL,
+ `mlehastertng` mediumint(6) NULL,
+ `rgdhastertng` mediumint(6) NULL,
+ `splhastertng` mediumint(6) NULL,
+ `hitrtng` mediumint(6) NULL,
+ `critstrkrtng` mediumint(6) NULL,
+ `_hitrtng` mediumint(6) NULL,
+ `_critstrkrtng` mediumint(6) NULL,
+ `resirtng` mediumint(6) NULL,
+ `hastertng` mediumint(6) NULL,
+ `exprtng` mediumint(6) NULL,
+ `atkpwr` mediumint(6) NULL,
+ `mleatkpwr` mediumint(6) NULL,
+ `rgdatkpwr` mediumint(6) NULL,
+ `feratkpwr` mediumint(6) NULL,
+ `splheal` mediumint(6) NULL,
+ `spldmg` mediumint(6) NULL,
+ `manargn` mediumint(6) NULL,
+ `armorpenrtng` mediumint(6) NULL,
+ `splpwr` mediumint(6) NULL,
+ `healthrgn` mediumint(6) NULL,
+ `splpen` mediumint(6) NULL,
+ `block` mediumint(6) NULL,
+ `mastrtng` mediumint(6) NULL,
+ `armor` mediumint(6) NULL,
+ `armorbonus` mediumint(6) NULL,
+ `firres` mediumint(6) NULL,
+ `frores` mediumint(6) NULL,
+ `holres` mediumint(6) NULL,
+ `shares` mediumint(6) NULL,
+ `natres` mediumint(6) NULL,
+ `arcres` mediumint(6) NULL,
+ `firsplpwr` mediumint(6) NULL,
+ `frosplpwr` mediumint(6) NULL,
+ `holsplpwr` mediumint(6) NULL,
+ `shasplpwr` mediumint(6) NULL,
+ `natsplpwr` mediumint(6) NULL,
+ `arcsplpwr` mediumint(6) NULL,
+ PRIMARY KEY (`type`,`typeId`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+UPDATE `aowow_dbversion`
+ SET `sql` = CONCAT(IFNULL(`sql`, ''), ' item_stats');
diff --git a/static/js/filters.js b/static/js/filters.js
index 7e5e3b693..a552b404d 100644
--- a/static/js/filters.js
+++ b/static/js/filters.js
@@ -169,7 +169,7 @@ var fi_filters = {
{ id: 87, name: 'reagentforability', type: 'profession' },
{ id: 63, name: 'buyprice', type: 'num', noweights: 1 },
{ id: 154, name: 'refundable', type: 'yn' },
- { id: 165, name: 'repaircost', type: 'num' },
+ { id: 165, name: 'repaircost', type: 'num', noweights: 1 },
{ id: 64, name: 'sellprice', type: 'num', noweights: 1 },
{ id: 157, name: 'smartloot', type: 'yn' },
{ id: 6, name: 'startsquest', type: 'side' },
diff --git a/static/js/locale_dede.js b/static/js/locale_dede.js
index 831286467..e00e27eb4 100644
--- a/static/js/locale_dede.js
+++ b/static/js/locale_dede.js
@@ -4772,7 +4772,7 @@ var LANG = {
su_addscale: "Gewichtung",
su_additem: "Gegenstand",
su_addset: "Set",
- su_toggle: "Klickt, um die Darstellung anzuzeigen",
+ su_toggle: "Klickt, um die Darstellung umzuschalten",
su_preset: "Vorlage: ",
su_name: "Name: ",