From 7e0be113238dd93727fdfcc29a9e8e8ee445052c Mon Sep 17 00:00:00 2001 From: Sarjuuk Date: Tue, 18 Jun 2024 15:31:52 +0200 Subject: [PATCH] Stats/CombatRatings * define magic numbers from combat ratings * move forgotten rating base values from Util to Stats * fix localized rating strings for item and spell tooltips (expected float, got string) * some cleanup in item/spell types to make it more clear when an internal stat is being handled, not an itemMod or combatRating --- includes/defines.php | 30 ++++- includes/stats.class.php | 240 ++++++++++++++++++--------------- includes/types/item.class.php | 42 +++--- includes/types/spell.class.php | 160 +++++++++++----------- includes/utilities.php | 20 +-- localization/locale_dede.php | 2 +- localization/locale_enus.php | 2 +- localization/locale_eses.php | 2 +- localization/locale_frfr.php | 2 +- localization/locale_ruru.php | 2 +- localization/locale_zhcn.php | 2 +- 11 files changed, 280 insertions(+), 224 deletions(-) diff --git a/includes/defines.php b/includes/defines.php index 5f1b4c58..93d559e6 100644 --- a/includes/defines.php +++ b/includes/defines.php @@ -472,6 +472,34 @@ define('ITEM_MOD_HEALTH_REGEN', 46); define('ITEM_MOD_SPELL_PENETRATION', 47); define('ITEM_MOD_BLOCK_VALUE', 48); +// Combat Ratings +define('CR_WEAPON_SKILL', 0); +define('CR_DEFENSE_SKILL', 1); +define('CR_DODGE', 2); +define('CR_PARRY', 3); +define('CR_BLOCK', 4); +define('CR_HIT_MELEE', 5); +define('CR_HIT_RANGED', 6); +define('CR_HIT_SPELL', 7); +define('CR_CRIT_MELEE', 8); +define('CR_CRIT_RANGED', 9); +define('CR_CRIT_SPELL', 10); +define('CR_HIT_TAKEN_MELEE', 11); +define('CR_HIT_TAKEN_RANGED', 12); +define('CR_HIT_TAKEN_SPELL', 13); +define('CR_CRIT_TAKEN_MELEE', 14); +define('CR_CRIT_TAKEN_RANGED', 15); +define('CR_CRIT_TAKEN_SPELL', 16); +define('CR_HASTE_MELEE', 17); +define('CR_HASTE_RANGED', 18); +define('CR_HASTE_SPELL', 19); +define('CR_WEAPON_SKILL_MAINHAND', 20); +define('CR_WEAPON_SKILL_OFFHAND', 21); +define('CR_WEAPON_SKILL_RANGED', 22); +define('CR_EXPERTISE', 23); +define('CR_ARMOR_PENETRATION', 24); +// define('CR_MASTERY', 25); // not in 335a + // Powers define('POWER_MANA', 0); define('POWER_RAGE', 1); @@ -553,7 +581,7 @@ define('LOCK_PROPERTY_MINING', 3); define('NPC_TYPEFLAG_HERBLOOT', 0x0100); define('NPC_TYPEFLAG_MININGLOOT', 0x0200); define('NPC_TYPEFLAG_ENGINEERLOOT', 0x8000); -define('NPC_TYPEFLAG_SPECIALLOOT', 0x8300); +define('NPC_TYPEFLAG_SPECIALLOOT', NPC_TYPEFLAG_ENGINEERLOOT | NPC_TYPEFLAG_MININGLOOT | NPC_TYPEFLAG_HERBLOOT); define('NPC_RANK_NORMAL', 0); define('NPC_RANK_ELITE', 1); diff --git a/includes/stats.class.php b/includes/stats.class.php index 615d11b2..bc736b97 100644 --- a/includes/stats.class.php +++ b/includes/stats.class.php @@ -122,107 +122,120 @@ abstract class Stat // based on g_statTo public const IDX_FLAGS = 4; private static /* array */ $data = array( - self::HEALTH => ['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], + self::HEALTH => ['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, CR_DEFENSE_SKILL, 42, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::DODGE_RTG => ['dodgertng', ITEM_MOD_DODGE_RATING, CR_DODGE, 45, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::PARRY_RTG => ['parryrtng', ITEM_MOD_PARRY_RATING, CR_PARRY, 46, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::BLOCK_RTG => ['blockrtng', ITEM_MOD_BLOCK_RATING, CR_BLOCK, 44, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::MELEE_HIT_RTG => ['mlehitrtng', ITEM_MOD_HIT_MELEE_RATING, CR_HIT_MELEE, 95, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::RANGED_HIT_RTG => ['rgdhitrtng', ITEM_MOD_HIT_RANGED_RATING, CR_HIT_RANGED, 39, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::SPELL_HIT_RTG => ['splhitrtng', ITEM_MOD_HIT_SPELL_RATING, CR_HIT_SPELL, 48, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::MELEE_CRIT_RTG => ['mlecritstrkrtng', ITEM_MOD_CRIT_MELEE_RATING, CR_CRIT_MELEE, 84, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::RANGED_CRIT_RTG => ['rgdcritstrkrtng', ITEM_MOD_CRIT_RANGED_RATING, CR_CRIT_RANGED, 40, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::SPELL_CRIT_RTG => ['splcritstrkrtng', ITEM_MOD_CRIT_SPELL_RATING, CR_CRIT_SPELL, 49, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::MELEE_HIT_TAKEN_RTG => ['_mlehitrtng', ITEM_MOD_HIT_TAKEN_MELEE_RATING, CR_HIT_TAKEN_MELEE, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::RANGED_HIT_TAKEN_RTG => ['_rgdhitrtng', ITEM_MOD_HIT_TAKEN_RANGED_RATING, CR_HIT_TAKEN_RANGED, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::SPELL_HIT_TAKEN_RTG => ['_splhitrtng', ITEM_MOD_HIT_TAKEN_SPELL_RATING, CR_HIT_TAKEN_SPELL, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::MELEE_CRIT_TAKEN_RTG => ['_mlecritstrkrtng', ITEM_MOD_CRIT_TAKEN_MELEE_RATING, CR_CRIT_TAKEN_MELEE, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::RANGED_CRIT_TAKEN_RTG => ['_rgdcritstrkrtng', ITEM_MOD_CRIT_TAKEN_RANGED_RATING, CR_CRIT_TAKEN_RANGED, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::SPELL_CRIT_TAKEN_RTG => ['_splcritstrkrtng', ITEM_MOD_CRIT_TAKEN_SPELL_RATING, CR_CRIT_TAKEN_SPELL, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::MELEE_HASTE_RTG => ['mlehastertng', ITEM_MOD_HASTE_MELEE_RATING, CR_HASTE_MELEE, 78, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::RANGED_HASTE_RTG => ['rgdhastertng', ITEM_MOD_HASTE_RANGED_RATING, CR_HASTE_RANGED, 101, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::SPELL_HASTE_RTG => ['splhastertng', ITEM_MOD_HASTE_SPELL_RATING, CR_HASTE_SPELL, 102, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::HIT_RTG => ['hitrtng', ITEM_MOD_HIT_RATING, -CR_HIT_MELEE, 119, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::CRIT_RTG => ['critstrkrtng', ITEM_MOD_CRIT_RATING, -CR_CRIT_MELEE, 96, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::HIT_TAKEN_RTG => ['_hitrtng', ITEM_MOD_HIT_TAKEN_RATING, -CR_HIT_TAKEN_MELEE, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::CRIT_TAKEN_RTG => ['_critstrkrtng', ITEM_MOD_CRIT_TAKEN_RATING, -CR_CRIT_TAKEN_MELEE, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::RESILIENCE_RTG => ['resirtng', ITEM_MOD_RESILIENCE_RATING, -CR_CRIT_TAKEN_MELEE, 79, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::HASTE_RTG => ['hastertng', ITEM_MOD_HASTE_RATING, -CR_HASTE_MELEE, 103, self::FLAG_ITEM | self::FLAG_LVL_SCALING], + self::EXPERTISE_RTG => ['exprtng', ITEM_MOD_EXPERTISE_RATING, CR_EXPERTISE, 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, CR_ARMOR_PENETRATION, 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, CR_MASTERY, 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], + 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] + 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] + ); + + /* Combat Rating needed for 1% effect at level 60 (Note: Shaman, Druid, Paladin and Death Knight have a /1.3 modifier on HASTE not set here) + * Data taken from gtcombatratings.dbc for level 60 [idx % 100 = 59] + * Corrections from gtoctclasscombatratingscalar.dbc with Warrior as base [idx = ratingId + 1] + * Maybe create this data during setup, but then again it will never change for 3.3.5a + */ + private static $crPerPctPoint = array( + CR_WEAPON_SKILL => 2.50, CR_DEFENSE_SKILL => 1.50, CR_DODGE => 13.80, CR_PARRY => 13.80, CR_BLOCK => 5.00, + CR_HIT_MELEE => 10.00, CR_HIT_RANGED => 10.00, CR_HIT_SPELL => 8.00, CR_CRIT_MELEE => 14.00, CR_CRIT_RANGED => 14.00, + CR_CRIT_SPELL => 14.00, CR_HIT_TAKEN_MELEE => 10.00, CR_HIT_TAKEN_RANGED => 10.00, CR_HIT_TAKEN_SPELL => 8.00, CR_CRIT_TAKEN_MELEE => 28.75, + CR_CRIT_TAKEN_RANGED => 28.75, CR_CRIT_TAKEN_SPELL => 28.75, CR_HASTE_MELEE => 10.00, CR_HASTE_RANGED => 10.00, CR_HASTE_SPELL => 10.00, + CR_WEAPON_SKILL_MAINHAND => 2.50, CR_WEAPON_SKILL_OFFHAND => 2.50, CR_WEAPON_SKILL_RANGED => 2.50, CR_EXPERTISE => 2.50, CR_ARMOR_PENETRATION => 4.69512 / 1.1, ); public static function isLevelIndependent(int $stat) : bool @@ -233,6 +246,19 @@ abstract class Stat // based on g_statTo return !(self::$data[$stat][self::IDX_FLAGS] & self::FLAG_LVL_SCALING); } + public static function getRatingPctFactor(int $stat) : float + { + // Note: this makes the weapon skill related combat ratings inaccessible. Is this relevant..? + if (!isset(self::$data[$stat]) || self::$data[$stat][self::IDX_COMBAT_RATING] === null) + return 0.0; + + // note: originally any CRIT_TAKEN_RTG stat was set to 0 in favor of RESILIENCE_RTG + // we keep the dbc value and just link RESILIENCE_RTG to CRIT_TAKEN_RTG + // note2: the js expects some stats to be directly mapped to a combat rating that doesn't exist + // picked the next best one in this case and denoted it with a negative value in the $data dump + return self::$crPerPctPoint[abs(self::$data[$stat][self::IDX_COMBAT_RATING])]; + } + public static function getJsonString(int $stat) : string { if (!isset(self::$data[$stat])) @@ -271,7 +297,7 @@ abstract class Stat // based on g_statTo { $x = []; foreach (self::$data as $k => [, , $c, , $f]) - if ($c && (!$flags || $flags & $f)) + if ($c > 0 && (!$flags || $flags & $f)) $x[$k] = $c; return $x; @@ -416,7 +442,7 @@ class StatsContainer return $this; } - public function fromDB(int $type, int $typeId, int $fieldFlags = 0x0) : self + public function fromDB(int $type, int $typeId, int $fieldFlags = Stat::FLAG_NONE) : 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) { @@ -446,7 +472,7 @@ class StatsContainer /* Output */ /**********/ - public function toJson(int $outFlags = 0x0) : array + public function toJson(int $outFlags = Stat::FLAG_NONE) : array { $out = []; foreach ($this->store as $stat => $amt) @@ -523,13 +549,17 @@ class StatsContainer public static function convertCombatRating(int $mask) : array { - if (($mask & 0x00E0) == 0x00E0) // hit rating - all subcats (5:mle, 6:rgd, 7:spl) + $hitMask = (1 << CR_HIT_MELEE) | (1 << CR_HIT_RANGED) | (1 << CR_HIT_SPELL); + if (($mask & $hitMask) == $hitMask) return [Stat::HIT_RTG]; // generic hit rating - if (($mask & 0x0700) == 0x0700) // crit done rating - all subcats (8:mle, 9:rgd, 10:spl) + $critMask = (1 << CR_CRIT_MELEE) | (1 << CR_CRIT_RANGED) | (1 << CR_CRIT_SPELL); + if (($mask & $critMask) == $critMask) return [Stat::CRIT_RTG]; // generic crit rating - if (($mask & 0x1C000) == 0x1C000) // crit taken rating - all subcats (14:mle, 15:rgd, 16:spl) + + $takentMask = (1 << CR_CRIT_TAKEN_MELEE) | (1 << CR_CRIT_TAKEN_RANGED) | (1 << CR_CRIT_TAKEN_SPELL); + if (($mask & $takentMask) == $takentMask) return [Stat::RESILIENCE_RTG]; // resilience $result = []; // there really shouldn't be multiple ratings in that mask besides the cases above, but who knows.. diff --git a/includes/types/item.class.php b/includes/types/item.class.php index 7d8afa77..a5db63cb 100644 --- a/includes/types/item.class.php +++ b/includes/types/item.class.php @@ -755,22 +755,22 @@ class ItemList extends BaseType if (!$qty || $type <= 0) continue; + $statId = Stat::getIndexFrom(Stat::IDX_ITEM_MOD, $type); + // base stat - switch ($type) + switch ($statId) { - case ITEM_MOD_MANA: - case ITEM_MOD_HEALTH: - // $type += 1; // i think i fucked up somewhere mapping item_mods: offsets may be required somewhere - case ITEM_MOD_AGILITY: - case ITEM_MOD_STRENGTH: - case ITEM_MOD_INTELLECT: - case ITEM_MOD_SPIRIT: - case ITEM_MOD_STAMINA: - $x .= ''.($qty > 0 ? '+' : '-').abs($qty).' '.Lang::item('statType', $type).'
'; + case Stat::MANA: + case Stat::HEALTH: + case Stat::AGILITY: + case Stat::STRENGTH: + case Stat::INTELLECT: + case Stat::SPIRIT: + case Stat::STAMINA: + $x .= ''.Lang::item('statType', $type, [ord($qty > 0 ? '+' : '-'), abs($qty)]).'
'; break; default: // rating with % for reqLevel - $green[] = $this->parseRating($type, $qty, $interactive, $causesScaling); - + $green[] = $this->formatRating($statId, $type, $qty, $interactive, $causesScaling); } } @@ -1439,7 +1439,7 @@ class ItemList extends BaseType return round(($dps - 54.8) * 14); } - private function parseRating($type, $value, $interactive = false, &$scaling = false) + private function formatRating(int $statId, int $itemMod, int $qty, bool $interactive = false, bool &$scaling = false) : string { // clamp level range $ssdLvl = isset($this->ssd[$this->id]) ? $this->ssd[$this->id]['maxLevel'] : 1; @@ -1447,27 +1447,27 @@ class ItemList extends BaseType $level = min(max($reqLvl, $ssdLvl), MAX_LEVEL); // unknown rating - if (!Stat::getIndexFrom(Stat::IDX_ITEM_MOD, $type)) + if (!$statId) { if (User::isInGroup(U_GROUP_EMPLOYEE)) - return sprintf(Lang::item('statType', count(Lang::item('statType')) - 1), $type, $value); + return Lang::item('statType', count(Lang::item('statType')) - 1, [$itemMod, $qty]); else - return null; + return ''; } // level independent Bonus - if (Stat::isLevelIndependent($type)) - return Lang::item('trigger', SPELL_TRIGGER_EQUIP).str_replace('%d', ''.$value, Lang::item('statType', $type)); + if (Stat::isLevelIndependent($statId)) + return Lang::item('trigger', SPELL_TRIGGER_EQUIP).str_replace('%d', ''.$qty, Lang::item('statType', $itemMod)); // rating-Bonuses $scaling = true; if ($interactive) - $js = ' ('.sprintf(Util::$changeLevelString, Util::setRatingLevel($level, $type, $value)).')'; + $js = ' ('.sprintf(Util::$changeLevelString, Util::setRatingLevel($level, $statId, $qty)).')'; else - $js = ' ('.Util::setRatingLevel($level, $type, $value).')'; + $js = ' ('.Util::setRatingLevel($level, $statId, $qty).')'; - return Lang::item('trigger', SPELL_TRIGGER_EQUIP).str_replace('%d', ''.$value.$js, Lang::item('statType', $type)); + return Lang::item('trigger', SPELL_TRIGGER_EQUIP).str_replace('%d', ''.$qty.$js, Lang::item('statType', $itemMod)); } private function getSSDMod($type) diff --git a/includes/types/spell.class.php b/includes/types/spell.class.php index e8e751d6..94801c81 100644 --- a/includes/types/spell.class.php +++ b/includes/types/spell.class.php @@ -955,18 +955,22 @@ class SpellList extends BaseType } // description-, buff-parsing component - // returns [min, max, minFulltext, maxFulltext, ratingId] - private function resolveVariableString($varParts) + private function resolveVariableString(array $varParts) : array { $signs = ['+', '-', '/', '*', '%', '^']; foreach ($varParts as $k => $v) $$k = $v; - $result = [null]; + // returns + $minPoints = null; + $maxPoints = null; + $fmtStringMin = null; + $fmtStringMax = null; + $statId = null; if (!$var) - return $result; + return [null, null, null, null, null]; if (!$effIdx) // if EffectIdx is omitted, assume EffectIdx: 1 $effIdx = 1; @@ -977,7 +981,7 @@ class SpellList extends BaseType $srcSpell = $lookup && $lookup != $this->id ? $this->refSpells[$lookup] : $this; if ($srcSpell->error) - return $result; + return [null, null, null, null, null]; switch ($var) { @@ -988,7 +992,7 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'b': // PointsPerComboPoint case 'B': @@ -997,18 +1001,18 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'd': // SpellDuration case 'D': // todo (med): min/max?; /w unit? $base = $srcSpell->getField('duration'); - $result[2] = Lang::formatTime($srcSpell->getField('duration'), 'spell', 'duration'); + $fmtStringMin = Lang::formatTime($srcSpell->getField('duration'), 'spell', 'duration'); if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base < 0 ? 0 : $base; + $minPoints = $base < 0 ? 0 : $base; break; case 'e': // EffectValueMultiplier case 'E': @@ -1017,7 +1021,7 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'f': // EffectDamageMultiplier case 'F': @@ -1026,11 +1030,11 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'g': // boolean choice with casters gender as condition $gX:Y; case 'G': - $result[2] = '<'.$switch[0].'/'.$switch[1].'>'; + $fmtStringMin = '<'.$switch[0].'/'.$switch[1].'>'; break; case 'h': // ProcChance case 'H': @@ -1039,7 +1043,7 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'i': // MaxAffectedTargets case 'I': @@ -1048,12 +1052,12 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'l': // boolean choice with last value as condition $lX:Y; case 'L': // resolve later by backtracking - $result[2] = '$l'.$switch[0].':'.$switch[1].';'; + $fmtStringMin = '$l'.$switch[0].':'.$switch[1].';'; break; case 'm': // BasePoints (minValue) case 'M': // BasePoints (maxValue) @@ -1069,25 +1073,25 @@ class SpellList extends BaseType } // Aura giving combat ratings - $rType = []; + $stats = []; if ($aura == SPELL_AURA_MOD_RATING) - if ($rType = StatsContainer::convertCombatRating($mv)) + if ($stats = StatsContainer::convertCombatRating($mv)) $this->scaling[$this->id] = true; // Aura end - if ($rType) + if ($stats) { - $result[2] = '%s (%s)'; - $result[4] = $rType[0]; // could be multiple ratings in theory, but not expected to be + $fmtStringMin = '%s (%s)'; + $statId = $stats[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)) { $this->scaling[$this->id] = true; - $result[2] = $modStrMin.'%s'; + $fmtStringMin = $modStrMin.'%s'; } */ - $result[0] = ctype_lower($var) ? $min : $max; + $minPoints = ctype_lower($var) ? $min : $max; break; case 'n': // ProcCharges case 'N': @@ -1096,7 +1100,7 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'o': // TotalAmount for periodic auras (with variance) case 'O': @@ -1125,12 +1129,12 @@ class SpellList extends BaseType { $this->scaling[$this->id] = true; - $result[2] = $modStrMin.'%s'; - $result[3] = $modStrMax.'%s'; + $fmtStringMin = $modStrMin.'%s'; + $fmtStringMax = $modStrMax.'%s'; } - $result[0] = $min; - $result[1] = $max; + $minPoints = $min; + $maxPoints = $max; break; case 'q': // EffectMiscValue case 'Q': @@ -1139,7 +1143,7 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'r': // SpellRange case 'R': @@ -1148,7 +1152,7 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 's': // BasePoints (with variance) case 'S': @@ -1162,26 +1166,26 @@ class SpellList extends BaseType eval("\$max = $max $op $oparg;"); } // Aura giving combat ratings - $rType = []; + $stats = []; if ($aura == SPELL_AURA_MOD_RATING) - if ($rType = StatsContainer::convertCombatRating($mv)) + if ($stats = StatsContainer::convertCombatRating($mv)) $this->scaling[$this->id] = true; // Aura end - if ($rType) + if ($stats) { - $result[2] = '%s (%s)'; - $result[4] = $rType[0]; // could be multiple ratings in theory, but not expected to be + $fmtStringMin = '%s (%s)'; + $statId = $stats[0]; // could be multiple ratings in theory, but not expected to be } else if (($modStrMin || $modStrMax) && $this->interactive) { $this->scaling[$this->id] = true; - $result[2] = $modStrMin.'%s'; - $result[3] = $modStrMax.'%s'; + $fmtStringMin = $modStrMin.'%s'; + $fmtStringMax = $modStrMax.'%s'; } - $result[0] = $min; - $result[1] = $max; + $minPoints = $min; + $maxPoints = $max; break; case 't': // Periode case 'T': @@ -1190,7 +1194,7 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'u': // StackCount case 'U': @@ -1199,7 +1203,7 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'v': // MaxTargetLevel case 'V': @@ -1208,7 +1212,7 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'x': // ChainTargetCount case 'X': @@ -1217,27 +1221,27 @@ class SpellList extends BaseType if (in_array($op, $signs) && is_numeric($oparg) && is_numeric($base)) eval("\$base = $base $op $oparg;"); - $result[0] = $base; + $minPoints = $base; break; case 'z': // HomeZone - $result[2] = Lang::spell('home'); + $fmtStringMin = Lang::spell('home'); break; } // handle excessively precise floats - if (is_float($result[0])) - $result[0] = round($result[0], 2); - if (isset($result[1]) && is_float($result[1])) - $result[1] = round($result[1], 2); + if (is_float($minPoints)) + $minPoints = round($minPoints, 2); + if (isset($maxPoints) && is_float($maxPoints)) + $maxPoints = round($maxPoints, 2); - return $result; // minPoints, maxPoints, fmtStringMin, fmtStringMax, combatRatingId + return [$minPoints, $maxPoints, $fmtStringMin, $fmtStringMax, $statId]; } // description-, buff-parsing component - private function resolveFormulaString($formula, $precision = 0) + private function resolveFormulaString(string $formula, int $precision = 0) { $fSuffix = '%s'; - $fRating = 0; + $fStat = 0; // step 1: formula unpacking redux while (($formStartPos = strpos($formula, '${')) !== false) @@ -1273,7 +1277,7 @@ class SpellList extends BaseType ++$formCurPos; // for some odd reason the precision decimal survives if we dont increment further.. } - [$formOutStr, $fSuffix, $fRating] = $this->resolveFormulaString($formOutStr, $formPrecision); + [$formOutStr, $fSuffix, $fStat] = $this->resolveFormulaString($formOutStr, $formPrecision); $formula = substr_replace($formula, $formOutStr, $formStartPos, ($formCurPos - $formStartPos)); } @@ -1307,34 +1311,34 @@ class SpellList extends BaseType $pos += $len; // we are resolving a formula -> omit ranges - $var = $this->resolveVariableString($varParts); + [$minPoints, , $fmtStringMin, , $statId] = $this->resolveVariableString($varParts); // time within formula -> rebase to seconds and omit timeUnit if (strtolower($varParts['var']) == 'd') { - $var[0] /= 1000; - unset($var[2]); + $minPoints /= 1000; + unset($fmtStringMin); } - $str .= $var[0]; + $str .= $minPoints; // overwrite eventually inherited strings - if (isset($var[2])) - $fSuffix = $var[2]; + if (isset($fmtStringMin)) + $fSuffix = $fmtStringMin; - // overwrite eventually inherited ratings - if (isset($var[4])) - $fRating = $var[4]; + // overwrite eventually inherited stat + if (isset($statId)) + $fStat = $statId; } $str .= substr($formula, $pos); - $str = str_replace('#', '$', $str); // reset marks + $str = str_replace('#', '$', $str); // reset markers // step 3: try to evaluate result $evaled = $this->resolveEvaluation($str); $return = is_numeric($evaled) ? round($evaled, $precision) : $evaled; - return [$return, $fSuffix, $fRating]; + return [$return, $fSuffix, $fStat]; } // should probably used only once to create ?_spell. come to think of it, it yields the same results every time.. it absolutely has to! @@ -1576,12 +1580,12 @@ class SpellList extends BaseType $formPrecision = $data[$formCurPos + 1]; $formCurPos += 2; } - [$formOutVal, $formOutStr, $ratingId] = $this->resolveFormulaString($formOutStr, $formPrecision ?: ($topLevel ? 0 : 10)); + [$formOutVal, $formOutStr, $statId] = $this->resolveFormulaString($formOutStr, $formPrecision ?: ($topLevel ? 0 : 10)); - if ($ratingId && Util::checkNumeric($formOutVal) && $this->interactive) - $resolved = sprintf($formOutStr, $ratingId, abs($formOutVal), sprintf(Util::$setRatingLevelString, $this->charLevel, $ratingId, abs($formOutVal), Util::setRatingLevel($this->charLevel, $ratingId, abs($formOutVal)))); - else if ($ratingId && Util::checkNumeric($formOutVal)) - $resolved = sprintf($formOutStr, $ratingId, abs($formOutVal), Util::setRatingLevel($this->charLevel, $ratingId, abs($formOutVal))); + if ($statId && Util::checkNumeric($formOutVal) && $this->interactive) + $resolved = sprintf($formOutStr, $statId, abs($formOutVal), sprintf(Util::$setRatingLevelString, $this->charLevel, $statId, abs($formOutVal), Util::setRatingLevel($this->charLevel, $statId, abs($formOutVal)))); + else if ($statId && Util::checkNumeric($formOutVal)) + $resolved = sprintf($formOutStr, $statId, abs($formOutVal), Util::setRatingLevel($this->charLevel, $statId, abs($formOutVal))); else $resolved = sprintf($formOutStr, Util::checkNumeric($formOutVal) ? abs($formOutVal) : $formOutVal); @@ -1614,23 +1618,23 @@ class SpellList extends BaseType $pos += $len; - $var = $this->resolveVariableString($varParts); - $resolved = is_numeric($var[0]) ? abs($var[0]) : $var[0]; - if (isset($var[2])) + [$minPoints, $maxPoints, $fmtStringMin, $fmtStringMax, $statId] = $this->resolveVariableString($varParts); + $resolved = is_numeric($minPoints) ? abs($minPoints) : $minPoints; + if (isset($fmtStringMin)) { - if (isset($var[4]) && $this->interactive) - $resolved = sprintf($var[2], $var[4], abs($var[0]), sprintf(Util::$setRatingLevelString, $this->charLevel, $var[4], abs($var[0]), Util::setRatingLevel($this->charLevel, $var[4], abs($var[0])))); - else if (isset($var[4])) - $resolved = sprintf($var[2], $var[4], abs($var[0]), Util::setRatingLevel($this->charLevel, $var[4], abs($var[0]))); + if (isset($statId) && $this->interactive) + $resolved = sprintf($fmtStringMin, $statId, abs($minPoints), sprintf(Util::$setRatingLevelString, $this->charLevel, $statId, abs($minPoints), Util::setRatingLevel($this->charLevel, $statId, abs($minPoints)))); + else if (isset($statId)) + $resolved = sprintf($fmtStringMin, $statId, abs($minPoints), Util::setRatingLevel($this->charLevel, $statId, abs($minPoints))); else - $resolved = sprintf($var[2], $resolved); + $resolved = sprintf($fmtStringMin, $resolved); } - if (isset($var[1]) && $var[0] != $var[1] && !isset($var[4])) + if (isset($maxPoints) && $minPoints != $maxPoints && !isset($statId)) { - $_ = is_numeric($var[1]) ? abs($var[1]) : $var[1]; + $_ = is_numeric($maxPoints) ? abs($maxPoints) : $maxPoints; $resolved .= Lang::game('valueDelim'); - $resolved .= isset($var[3]) ? sprintf($var[3], $_) : $_; + $resolved .= isset($fmtStringMax) ? sprintf($fmtStringMax, $_) : $_; } $str .= $resolved; diff --git a/includes/utilities.php b/includes/utilities.php index 1d7edbeb..12b1c096 100644 --- a/includes/utilities.php +++ b/includes/utilities.php @@ -520,13 +520,6 @@ abstract class Util 'us', 'eu', 'kr', 'tw', 'cn', 'dev' ); - # todo (high): find a sensible way to write data here on setup - private static $gtCombatRatings = array( - 12 => 1.5, 13 => 13.8, 14 => 13.8, 15 => 5, 16 => 10, 17 => 10, 18 => 8, 19 => 14, 20 => 14, - 21 => 14, 22 => 10, 23 => 10, 24 => 8, 25 => 0, 26 => 0, 27 => 0, 28 => 10, 29 => 10, - 30 => 10, 31 => 10, 32 => 14, 33 => 0, 34 => 0, 35 => 28.75, 36 => 10, 37 => 2.5, 44 => 4.268292513760655 - ); - public static $ssdMaskFields = array( 'shoulderMultiplier', 'trinketMultiplier', 'weaponMultiplier', 'primBudged', 'rangedMultiplier', 'clothShoulderArmor', 'leatherShoulderArmor', 'mailShoulderArmor', @@ -917,12 +910,13 @@ abstract class Util } // for item and spells - public static function setRatingLevel(int $level, int $type, int $val) : string + public static function setRatingLevel(int $level, int $statId, int $val) : string { - if (in_array($type, [ITEM_MOD_DEFENSE_SKILL_RATING, ITEM_MOD_DODGE_RATING, ITEM_MOD_PARRY_RATING, ITEM_MOD_BLOCK_RATING, ITEM_MOD_RESILIENCE_RATING]) && $level < 34) + if (in_array($statId, [Stat::DEFENSE_RTG, Stat::DODGE_RTG, Stat::PARRY_RTG, Stat::BLOCK_RTG, Stat::RESILIENCE_RTG]) && $level < 34) $level = 34; - if (!isset(self::$gtCombatRatings[$type])) + $factor = Stat::getRatingPctFactor($statId); + if (!$factor) $result = 0; else { @@ -936,13 +930,13 @@ abstract class Util $c = 2 / 52; // do not use localized number format here! - $result = number_format($val / self::$gtCombatRatings[$type] / $c, 2); + $result = number_format($val / $factor / $c, 2); } - if (!in_array($type, array(ITEM_MOD_DEFENSE_SKILL_RATING, ITEM_MOD_EXPERTISE_RATING))) + if (!in_array($statId, [Stat::DEFENSE_RTG, Stat::EXPERTISE_RTG])) $result .= '%'; - return Lang::item('ratingString', [$type, $result, $level]); + return Lang::item('ratingString', [$statId, $result, $level]); } public static function powerUseLocale($domain = 'www') diff --git a/localization/locale_dede.php b/localization/locale_dede.php index bc1ddd19..d91e314c 100644 --- a/localization/locale_dede.php +++ b/localization/locale_dede.php @@ -1801,7 +1801,7 @@ $lang = array( 'block' => "%s Blocken", 'charges' => "%d |4Aufladung:Aufladungen;", 'locked' => "Verschlossen", - 'ratingString' => '%2$.2F%% @ Lvl%3$d', + 'ratingString' => '%2$s @ Lvl%3$d', 'heroic' => "Heroisch", 'startQuest' => "Dieser Gegenstand startet eine Quest", 'bagSlotString' => '%1$d Platz %2$s', diff --git a/localization/locale_enus.php b/localization/locale_enus.php index 4db8dd5a..ec98c1b2 100644 --- a/localization/locale_enus.php +++ b/localization/locale_enus.php @@ -1801,7 +1801,7 @@ $lang = array( 'block' => "%s Block", // SHIELD_BLOCK_TEMPLATE 'charges' => "%d |4Charge:Charges;", // ITEM_SPELL_CHARGES 'locked' => "Locked", // LOCKED - 'ratingString' => '%2$.2F%% @ L%3$d', + 'ratingString' => '%2$s @ L%3$d', 'heroic' => "Heroic", // ITEM_HEROIC 'startQuest' => "This Item Begins a Quest", // ITEM_STARTS_QUEST 'bagSlotString' => "%d Slot %s", // CONTAINER_SLOTS diff --git a/localization/locale_eses.php b/localization/locale_eses.php index 2fc08ac3..92ebf053 100644 --- a/localization/locale_eses.php +++ b/localization/locale_eses.php @@ -1801,7 +1801,7 @@ $lang = array( 'block' => "%s bloqueo", 'charges' => "%d |4carga:cargas;", 'locked' => "Cerrado", - 'ratingString' => '%2$.2F%% @ L%3$d', + 'ratingString' => '%2$s @ L%3$d', 'heroic' => "Heroico", 'startQuest' => "Este objeto inicia una misión", 'bagSlotString' => '%2$s de %1$d casillas', diff --git a/localization/locale_frfr.php b/localization/locale_frfr.php index c1569001..9dfcd103 100644 --- a/localization/locale_frfr.php +++ b/localization/locale_frfr.php @@ -1801,7 +1801,7 @@ $lang = array( 'block' => "Bloquer : %s", 'charges' => "%d |4charge:charges;", 'locked' => "Verrouillé", - 'ratingString' => '%2$.2F%% au niveau %3$d', + 'ratingString' => '%2$s au niveau %3$d', 'heroic' => "Héroïque", 'startQuest' => "Cet objet permet de lancer une quête", 'bagSlotString' => '%2$s %1$d |4emplacement:emplacements;', diff --git a/localization/locale_ruru.php b/localization/locale_ruru.php index 8f3d2b1e..b47e5fab 100644 --- a/localization/locale_ruru.php +++ b/localization/locale_ruru.php @@ -1801,7 +1801,7 @@ $lang = array( 'block' => "Блок: %s", 'charges' => "%d |4заряд:заряда:зарядов;", 'locked' => "Заперто", - 'ratingString' => '%2$.2F%% на %3$d ур.', + 'ratingString' => '%2$s на %3$d ур.', 'heroic' => "Героический", 'startQuest' => "Этот предмет позволяет получить задание.", 'bagSlotString' => '%2$s (%1$d |4ячейка:ячейки:ячеек;)', diff --git a/localization/locale_zhcn.php b/localization/locale_zhcn.php index feacbc31..1653b59b 100644 --- a/localization/locale_zhcn.php +++ b/localization/locale_zhcn.php @@ -1800,7 +1800,7 @@ $lang = array( 'block' => "%d格挡", 'charges' => "%d次", 'locked' => "已锁", - 'ratingString' => '%2$.2F%% @ Lvl%3$d', + 'ratingString' => '%2$s @ Lvl%3$d', 'heroic' => "英雄级别", 'startQuest' => "该物品将触发一个任务", 'bagSlotString' => "%d格%s",