From 8571c35e77458cfedda5ddd8f5ccb7ae8618f220 Mon Sep 17 00:00:00 2001 From: ukmeth0d Date: Fri, 7 Mar 2014 01:34:30 +0100 Subject: [PATCH 01/21] Fixed 2H Blunt Animation to match Live Melee bots (with no mana) will now sit when not full HP and out of combat. --- zone/attack.cpp | 4 ++-- zone/bot.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index a291a709b..798c61b11 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -91,7 +91,7 @@ bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* w case ItemType2HBlunt: // 2H Blunt { skillinuse = Skill2HBlunt; - type = anim2HWeapon; + type = anim2HSlashing; //anim2HWeapon break; } case ItemType2HPiercing: // 2H Piercing @@ -140,7 +140,7 @@ bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* w } case Skill2HBlunt: // 2H Blunt { - type = anim2HWeapon; + type = anim2HSlashing; //anim2HWeapon break; } case 99: // 2H Piercing // change to Skill2HPiercing once activated diff --git a/zone/bot.cpp b/zone/bot.cpp index 1ec736ecf..425402b65 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3151,9 +3151,9 @@ void Bot::SpellProcess() void Bot::BotMeditate(bool isSitting) { if(isSitting) { // If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate - if(GetManaRatio() < 99.0f) + if(GetManaRatio() < 99.0f || GetHPRatio() < 99.0f) { - if(!IsSitting()) + if (!IsEngaged() && !IsSitting()) Sit(); } else From 95176fc8137aa920f81625f1038e75dc86cc8af3 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 12 Mar 2014 05:14:19 -0400 Subject: [PATCH 02/21] Coverted melee and magic runes to use bonuses. Removed all the old rune flags now that none of them are used. Fixed issues where runes would not fade properly if damage = remaing rune amount Fixed issue where runes would stop absorbing damage if you had multiple runes. --- changelog.txt | 4 ++++ zone/attack.cpp | 31 +++++++++++------------------ zone/bonuses.cpp | 38 ++++++++++++++++++++++++++++++++++- zone/client.cpp | 4 ++-- zone/common.h | 4 ++-- zone/mob.cpp | 6 ------ zone/mob.h | 14 ------------- zone/pets.cpp | 1 - zone/spell_effects.cpp | 14 +++++-------- zone/spells.cpp | 45 ------------------------------------------ zone/zonedb.cpp | 6 ------ 11 files changed, 62 insertions(+), 105 deletions(-) diff --git a/changelog.txt b/changelog.txt index df6cc662d..e7973ed6d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- + +== 03/12/2014 == +Kayen: Melee/Magic runes are now calculated as bonuses. Resolved issues with runes not working and not fading properly. + == 03/08/2014 == Kayen: Revision to lull/harmony/pacification code to be consistent with live based on extensive personal parsing. *Lulls on initial cast do not check regular resists (MR ect) but only apply a flat ~7.5 % resist chance + level modifier diff --git a/zone/attack.cpp b/zone/attack.cpp index 798c61b11..ccbc3a8f8 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3188,7 +3188,6 @@ int32 Mob::ReduceDamage(int32 damage) damage -= damage_to_reduce; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - //UpdateRuneFlags(); } else { @@ -3213,7 +3212,6 @@ int32 Mob::ReduceDamage(int32 damage) damage -= damage_to_reduce; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - UpdateRuneFlags(); } else { @@ -3221,7 +3219,6 @@ int32 Mob::ReduceDamage(int32 damage) " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); damage -= damage_to_reduce; - UpdateRuneFlags(); } } } @@ -3243,7 +3240,7 @@ int32 Mob::ReduceDamage(int32 damage) if(damage < 1) return -6; - if (HasRune()) + if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0) damage = RuneAbsorb(damage, SE_Rune); if(damage < 1) @@ -3317,7 +3314,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi damage -= damage_to_reduce; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - //UpdateRuneFlags(); } else { @@ -3341,7 +3337,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi damage -= damage_to_reduce; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - UpdateRuneFlags(); } else { @@ -3349,7 +3344,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi " damage remaining.", damage_to_reduce, buffs[slot].magic_rune); buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce); damage -= damage_to_reduce; - UpdateRuneFlags(); } } } @@ -3372,7 +3366,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi return 0; - if (HasSpellRune()) + if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0) damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); if(damage < 1) @@ -4602,9 +4596,10 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) uint32 buff_max = GetMaxTotalSlots(); if (type == SE_Rune){ for(uint32 slot = 0; slot < buff_max; slot++) { - if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].melee_rune) && IsEffectInSpell(buffs[slot].spellid, type)){ + if(slot == spellbonuses.MeleeRune[1] && spellbonuses.MeleeRune[0] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)){ uint32 melee_rune_left = buffs[slot].melee_rune; - if(melee_rune_left >= damage) + + if(melee_rune_left > damage) { melee_rune_left -= damage; buffs[slot].melee_rune = melee_rune_left; @@ -4615,22 +4610,20 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) { if(melee_rune_left > 0) damage -= melee_rune_left; + if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - UpdateRuneFlags(); - continue; } } } - return damage; } - + else{ for(uint32 slot = 0; slot < buff_max; slot++) { - if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].magic_rune) && IsEffectInSpell(buffs[slot].spellid, type)){ + if(slot == spellbonuses.AbsorbMagicAtt[1] && spellbonuses.AbsorbMagicAtt[0] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)){ uint32 magic_rune_left = buffs[slot].magic_rune; - if(magic_rune_left >= damage) + if(magic_rune_left > damage) { magic_rune_left -= damage; buffs[slot].magic_rune = magic_rune_left; @@ -4641,14 +4634,14 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) { if(magic_rune_left > 0) damage -= magic_rune_left; + if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - UpdateRuneFlags(); - continue; } } } - return damage; } + + return damage; } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 49cfe8a08..99941b893 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2538,11 +2538,36 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->Root[0] = 1; newbon->Root[1] = buffslot; } - else { + else if (!newbon->Root[0]){ newbon->Root[0] = 1; newbon->Root[1] = buffslot; } break; + + case SE_Rune: + + if (newbon->MeleeRune[0] && (newbon->MeleeRune[1] > buffslot)){ + + newbon->MeleeRune[0] = effect_value; + newbon->MeleeRune[1] = buffslot; + } + else if (!newbon->MeleeRune[0]){ + newbon->MeleeRune[0] = effect_value; + newbon->MeleeRune[1] = buffslot; + } + + break; + + case SE_AbsorbMagicAtt: + if (newbon->AbsorbMagicAtt[0] && (newbon->AbsorbMagicAtt[1] > buffslot)){ + newbon->AbsorbMagicAtt[0] = effect_value; + newbon->AbsorbMagicAtt[1] = buffslot; + } + else if (!newbon->AbsorbMagicAtt[0]){ + newbon->AbsorbMagicAtt[0] = effect_value; + newbon->AbsorbMagicAtt[1] = buffslot; + } + break; } } } @@ -3896,7 +3921,18 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) case SE_Root: spellbonuses.Root[0] = effect_value; spellbonuses.Root[1] = -1; + break; + case SE_Rune: + spellbonuses.MeleeRune[0] = effect_value; + spellbonuses.MeleeRune[1] = -1; + break; + + case SE_AbsorbMagicAtt: + spellbonuses.AbsorbMagicAtt[0] = effect_value; + spellbonuses.AbsorbMagicAtt[1] = -1; + break; + } } } diff --git a/zone/client.cpp b/zone/client.cpp index e8af229ae..ebf5d806c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6710,9 +6710,9 @@ void Client::SendStatsWindow(Client* client, bool use_window) uint32 buff_count = GetMaxTotalSlots(); for (int i=0; i < buff_count; i++) { if (buffs[i].spellid != SPELL_UNKNOWN) { - if ((HasRune() || HasPartialMeleeRune()) && buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; } + if (buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; } - if ((HasSpellRune() || HasPartialSpellRune()) && buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; } + if (buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; } } } diff --git a/zone/common.h b/zone/common.h index bd744316f..581d11430 100644 --- a/zone/common.h +++ b/zone/common.h @@ -342,8 +342,8 @@ struct StatBonuses { int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid int8 Root[2]; // The lowest buff slot a root can be found. [0] = Bool if has root [1] = buff slot int16 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana. - //bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect - //bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect + uint16 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot + uint16 MeleeRune[2]; // 0 = rune value 1 = buff slot // AAs int8 Packrat; //weight reduction for items, 1 point = 10% diff --git a/zone/mob.cpp b/zone/mob.cpp index d7b37d996..3c1500d19 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -349,12 +349,6 @@ Mob::Mob(const char* in_name, nextinchpevent = -1; TempPets(false); - SetHasRune(false); - SetHasSpellRune(false); - SetHasPartialMeleeRune(false); - SetHasPartialSpellRune(false); - - m_hasDeathSaveChance = false; m_is_running = false; diff --git a/zone/mob.h b/zone/mob.h index a98c125c4..c771d57b7 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -248,14 +248,6 @@ public: virtual int GetMaxTotalSlots() const { return 0; } virtual void InitializeBuffSlots() { buffs = nullptr; current_buff_count = 0; } virtual void UninitializeBuffSlots() { } - inline bool HasRune() const { return m_hasRune; } - inline bool HasSpellRune() const { return m_hasSpellRune; } - inline bool HasPartialMeleeRune() const { return m_hasPartialMeleeRune; } - inline bool HasPartialSpellRune() const { return m_hasPartialSpellRune; } - inline void SetHasRune(bool hasRune) { m_hasRune = hasRune; } - inline void SetHasSpellRune(bool hasSpellRune) { m_hasSpellRune = hasSpellRune; } - inline void SetHasPartialMeleeRune(bool hasPartialMeleeRune) { m_hasPartialMeleeRune = hasPartialMeleeRune; } - inline void SetHasPartialSpellRune(bool hasPartialSpellRune) { m_hasPartialSpellRune = hasPartialSpellRune; } EQApplicationPacket *MakeBuffsPacket(bool for_target = true); void SendBuffsToClient(Client *c); inline Buffs_Struct* GetBuffs() { return buffs; } @@ -993,7 +985,6 @@ protected: float FindGroundZ(float new_x, float new_y, float z_offset=0.0); VERTEX UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached); void PrintRoute(); - void UpdateRuneFlags(); virtual float GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod); @@ -1192,11 +1183,6 @@ protected: float tar_vz; float test_vector; - bool m_hasRune; - bool m_hasSpellRune; - bool m_hasPartialMeleeRune; - bool m_hasPartialSpellRune; - bool m_hasDeathSaveChance; uint32 m_spellHitsLeft[38]; // Used to track which spells will have their numhits incremented when spell finishes casting, 38 Buffslots int flymode; bool m_targetable; diff --git a/zone/pets.cpp b/zone/pets.cpp index 5e81c3f84..d5946ce58 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -627,7 +627,6 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) { } } } - UpdateRuneFlags(); //restore their equipment... for(i = 0; i < MAX_WORN_INVENTORY; i++) { diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4361761de..cebd548dd 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1242,7 +1242,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #endif effect_value = ApplySpellEffectiveness(caster, spell_id, effect_value); buffs[buffslot].melee_rune = effect_value; - SetHasRune(true); break; } @@ -1251,17 +1250,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Spell Absorb Rune: %+i", effect_value); #endif - if(effect_value > 0) { - buffs[buffslot].magic_rune = effect_value; - SetHasSpellRune(true); - } + if(effect_value > 0) + buffs[buffslot].magic_rune = effect_value; + break; } case SE_MitigateMeleeDamage: { - buffs[buffslot].melee_rune = GetPartialMeleeRuneAmount(spell_id); - SetHasPartialMeleeRune(true); + buffs[buffslot].melee_rune = spells[spell_id].max[i]; break; } @@ -1279,8 +1276,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_MitigateSpellDamage: { - buffs[buffslot].magic_rune = GetPartialMagicRuneAmount(spell_id); - SetHasPartialSpellRune(true); + buffs[buffslot].magic_rune = spells[spell_id].max[i]; break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 8769e16a2..332906883 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5133,51 +5133,6 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration) } } } -void Mob::UpdateRuneFlags() -{ - bool Has_SE_Rune = false, Has_SE_AbsorbMagicAtt = false, Has_SE_MitigateMeleeDamage = false, Has_SE_MitigateSpellDamage = false; - uint32 buff_count = GetMaxTotalSlots(); - for (unsigned int i = 0; i < buff_count; ++i) - { - if (buffs[i].spellid != SPELL_UNKNOWN) - { - for (int j = 0; j < EFFECT_COUNT; ++j) - { - switch(spells[buffs[i].spellid].effectid[j]) - { - case SE_Rune: - { - Has_SE_Rune = true; - break; - } - case SE_AbsorbMagicAtt: - { - Has_SE_AbsorbMagicAtt = true; - break; - } - case SE_MitigateMeleeDamage: - { - Has_SE_MitigateMeleeDamage = true; - break; - } - case SE_MitigateSpellDamage: - { - Has_SE_MitigateSpellDamage = true; - break; - } - - default: - break; - } - } - } - } - - SetHasRune(Has_SE_Rune); - SetHasSpellRune(Has_SE_AbsorbMagicAtt); - SetHasPartialMeleeRune(Has_SE_MitigateMeleeDamage); - SetHasPartialSpellRune(Has_SE_MitigateSpellDamage); -} int Client::GetCurrentBuffSlots() const { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 9457a5f8d..6c3b8ce6e 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2656,12 +2656,6 @@ void ZoneDatabase::LoadBuffs(Client *c) { buffs[slot_id].ExtraDIChance = ExtraDIChance; buffs[slot_id].RootBreakChance = 0; buffs[slot_id].UpdateClient = false; - if(IsRuneSpell(spell_id)) { - c->SetHasRune(true); - } - else if(IsMagicRuneSpell(spell_id)) { - c->SetHasSpellRune(true); - } } mysql_free_result(result); From 02633d4b01e52564663b486e4a4c5a3043cd28a0 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 17 Mar 2014 04:53:47 -0400 Subject: [PATCH 03/21] ** Fix for RoF clients not displaying Augment Restrictions in the Item Info window. ** Change to Client::SummonItem() to enforce valid item/augment combinations. (Run the optional sql file first, before posting any SummonItem() failure issues in the forums.) --- changelog.txt | 10 + common/eq_constants.h | 77 +++ common/patches/RoF.cpp | 3 +- common/patches/RoF_structs.h | 5 +- common/ruletypes.h | 6 + .../2014_03_17_EnforceAugmentRules.sql | 3 + zone/client.h | 2 +- zone/inventory.cpp | 461 ++++++++++++++---- 8 files changed, 460 insertions(+), 107 deletions(-) create mode 100644 utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql diff --git a/changelog.txt b/changelog.txt index e7973ed6d..23374f83c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,15 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/17/2014 == +Uleat: Updated Client::SummonItem() to check for valid item combinations when augmentations are passed. +Uleat: Changed the return type of Client::SummonItem() from void to bool. Calling methods and APIs have not been update as yet. +Uleat: Fixed the RoF Item structure to properly pass the 'augrestrict' variable. RoF clients now show restrictions in the Item Information window. + +Optional SQL: 2014/03/17_EnforceAugmentRules.sql +Note: This adds the rules Inventory:EnforceAugmentRestriction, Inventory:EnforceAugmentUsability and Inventory:EnforceAugmentWear. + If you run into script/recipe issues, running this sql file will set the default enforcement rules to false. + If you still run into issues, you may want to check that your scripts are not trying to augment non-common items. + Please post any failures as bugs and be sure to include the base item ID, as well as any augment IDs that you are using. == 03/12/2014 == Kayen: Melee/Magic runes are now calculated as bonuses. Resolved issues with runes not working and not fading properly. diff --git a/common/eq_constants.h b/common/eq_constants.h index d612017ad..8b4db6fc3 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -137,6 +137,83 @@ enum ItemUseTypes : uint8 */ }; +/* +** Augmentation use types (in-work) +** +** (ref: dbstr_us.txt) +** +*/ +enum AugmentationUseTypes : uint32 { + AugTypeNone = 0, // not 100% sure on this... + AugTypeGeneralSingleStat, /*1^16^1 (General: Single Stat)^0*/ + AugTypeGeneralMultipleStat, /*2^16^2 (General: Multiple Stat)^0*/ + AugTypeGeneralSpellEffect, /*3^16^3 (General: Spell Effect)^0*/ + AugTypeWeaponGeneral, /*4^16^4 (Weapon: General)^0*/ + AugTypeWeaponElemDamage, /*5^16^5 (Weapon: Elem Damage)^0*/ + AugTypeWeaponBaseDamage, /*6^16^6 (Weapon: Base Damage)^0*/ + AugTypeGeneralGroup, /*7^16^7 (General: Group)^0*/ + AugTypeGeneralRaid, /*8^16^8 (General: Raid)^0*/ + AugTypeGeneralDragonsPoints, /*9^16^9 (General: Dragons Points)^0*/ + AugTypeCraftedCommon, /*10^16^10 (Crafted: Common)^0*/ + AugTypeCraftedGroup1, /*11^16^11 (Crafted: Group)^0*/ + AugTypeCraftedRaid1, /*12^16^12 (Crafted: Raid)^0*/ + AugTypeEnergeiacGroup, /*13^16^13 (Energeiac: Group)^0*/ + AugTypeEnergeiacRaid, /*14^16^14 (Energeiac: Raid)^0*/ + AugTypeEmblem, /*15^16^15 (Emblem)^0*/ + AugTypeCraftedGroup2, /*16^16^16 (Crafted: Group)^0*/ + AugTypeCraftedRaid2, /*17^16^17 (Crafted: Raid)^0*/ + AugTypeUnknown1, /*18^16^18^0*/ + AugTypeUnknown2, /*19^16^19^0*/ + AugTypeOrnamentation, /*20^16^20 (Ornamentation)^0*/ + AugTypeSpecialOrnamentation, /*21^16^21 (Special Ornamentation)^0*/ + AugTypeUnknown3, /*22^16^22^0*/ + AugTypeUnknown4, /*23^16^23^0*/ + AugTypeUnknown5, /*24^16^24^0*/ + AugTypeUnknown6, /*25^16^25^0*/ + AugTypeUnknown7, /*26^16^26^0*/ + AugTypeUnknown8, /*27^16^27^0*/ + AugTypeUnknown9, /*28^16^28^0*/ + AugTypeUnknown10, /*29^16^29^0*/ + AugTypeEpic25, /*30^16^30^0*/ + AugTypeTest, /*31^16^Test^0*/ // listed as 31^16^31^0 in 5-10 client + _AugTypeCount +}; + +/* +** Augmentation restriction types (in-work) +** +** (ref: eqstr_us.txt) +** +*/ +enum AugmentationRestrictionTypes : uint8 { +/*4690*/ AugRestrAny = 0, +/*9134*/ AugRestrArmor, +/*9135*/ AugRestrWeapons, +/*9136*/ AugRestr1HWeapons, +/*9137*/ AugRestr2HWeapons, +/*9138*/ AugRestr1HSlash, +/*9139*/ AugRestr1HBlunt, +/*9140*/ AugRestrPiercing, +/*9148*/ AugRestrHandToHand, +/*9141*/ AugRestr2HSlash, +/*9142*/ AugRestr2HBlunt, +/*9143*/ AugRestr2HPierce, +/*9144*/ AugRestrBows, +/*9145*/ AugRestrShields, +/*8052*/ AugRestr1HSlash1HBluntOrHandToHand, +/*9200*/ AugRestr1HBluntOrHandToHand, // no listed peq entries + +// these three appear to be post-RoF (12-10-2012) and can not be verified until RoF (05-10-2013) is supported +/*????*/ AugRestrUnknown1, +/*????*/ AugRestrUnknown2, +/*????*/ AugRestrUnknown3, // last value in peq entries + _AugRestrCount + +/*4687*/ //AugTypeAllItems, // ?? unknown atm +/*4688*/ //AugTypePrestige, // ?? unknown atm +/*4689*/ //AugTypeNonPrestige, // ?? unknown atm +}; + /* ** Container use types ** diff --git a/common/patches/RoF.cpp b/common/patches/RoF.cpp index 07cea6dfb..cbe228c92 100644 --- a/common/patches/RoF.cpp +++ b/common/patches/RoF.cpp @@ -5070,8 +5070,9 @@ char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint memset(&isbs, 0, sizeof(RoF::structs::ItemSecondaryBodyStruct)); isbs.augtype = item->AugType; - isbs.augrestrict = item->AugRestrict; isbs.augdistiller = 0; + isbs.augrestrict = item->AugRestrict; + for(int x = 0; x < 5; ++x) { diff --git a/common/patches/RoF_structs.h b/common/patches/RoF_structs.h index a2635ceae..918bbe68d 100644 --- a/common/patches/RoF_structs.h +++ b/common/patches/RoF_structs.h @@ -4427,8 +4427,11 @@ struct AugSlotStruct struct ItemSecondaryBodyStruct { uint32 augtype; - uint32 augrestrict; + // swapped augrestrict and augdistiller positions + // (this swap does show the proper augment restrictions in Item Information window now) + // unsure what the purpose of augdistiller is at this time -U 3/17/2014 uint32 augdistiller; // New to December 10th 2012 client - NEW + uint32 augrestrict; AugSlotStruct augslots[6]; uint32 ldonpoint_type; diff --git a/common/ruletypes.h b/common/ruletypes.h index 83324d533..83db6c3f0 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -537,6 +537,12 @@ RULE_BOOL( QueryServ, MerchantLogTransactions, false) // Logs Merchant Transacti RULE_BOOL( QueryServ, PlayerLogPCCoordinates, false) // Logs Player Coordinates with certain events RULE_CATEGORY_END() +RULE_CATEGORY( Inventory ) +RULE_BOOL ( Inventory, EnforceAugmentRestriction, true) // Forces augment slot restrictions +RULE_BOOL ( Inventory, EnforceAugmentUsability, true) // Forces augmented item usability +RULE_BOOL ( Inventory, EnforceAugmentWear, true) // Forces augment wear slot validation +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql b/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql new file mode 100644 index 000000000..e89d4a437 --- /dev/null +++ b/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql @@ -0,0 +1,3 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentRestriction', 'false', 'Forces augment slot restrictions.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentUsability', 'false', 'Forces augmented item usability.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentWear', 'false', 'Forces augment wear slot validation.'); \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index 10465dbc6..97ee1b32f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -794,7 +794,7 @@ public: void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false); void PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data = 0); bool AutoPutLootInInventory(ItemInst& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0); - void SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR); + bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR); void SetStats(uint8 type,int16 set_val); void IncStats(uint8 type,int16 increase_val); void DropItem(int16 slot_id); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 865eaa8e5..c6f1663da 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -200,124 +200,377 @@ bool Client::CheckLoreConflict(const Item_Struct* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != SLOT_INVALID); } -void Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { +bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { + // I have 'over-logged' failure messages to aid in any troubleshooting of issues that arise from this change. + // The 'LogFile' code may be taken out once after a period of time with no script/recipe issues. + // + // I have also incorporated a bool return type..but, have not updated any calling methods to process failures. + // Once the APIs are updated, scripts may also be updated. + const Item_Struct* item = database.GetItem(item_id); - if (item == nullptr) { - Message(0, "No such item: %i", item_id); - return; - } else { + if(item == nullptr) { + Message(13, "Item %u does not exist.", item_id); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + else { // if the item is stackable and the charge amount is -1 or 0 then set to 1 charge. // removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant - if (charges <= 0 && item->Stackable) { + if(charges <= 0 && item->Stackable) charges = 1; - // if the charges is -1, then no charge value was passed in set to max charges - } else if(charges == -1) { - charges = item->MaxCharges; - // in any other situation just use charges as passed - } - } - // Checking to see if the Item is lore or not. - bool foundlore = CheckLoreConflict(item); - //TODO: check for lore conflict on augments + // if the charges is -1, then no charge value was passed in set to max charges + else if(charges == -1) + charges = item->MaxCharges; + + // in any other situation just use charges as passed + } + + // Base item is lore..so, cancel summon + if(CheckLoreConflict(item)) { + // this is the 'you can not pickup another...' message. I don't feel this is appropriate + // for a summoning failure response + // DuplicateLoreMessage(item_id); + Message(13, "You already have a lore %s (%i) in your inventory.", item->Name, item_id); + + return false; + } // Checking to see if it is a GM only Item or not. - //bool foundgm = (item->gm && (this->Admin() < 100)); - bool foundgm = false; + /* + if(item->gm && (this->Admin() < 100)) + Message(0, "You are not a GM and can not summon this item!"); - if (!foundlore && !foundgm) { // Okay, It isn't LORE, or if it is, it is not in player's inventory. - ItemInst* inst = database.CreateItem(item, charges); - if (inst) { - // Corrected the augment references to reflect augment name/id instead of base item name/id - if (aug1) { - const Item_Struct* augitem1 = database.GetItem(aug1); - if (augitem1) { - if (!CheckLoreConflict(augitem1)) { - inst->PutAugment(&database, 0, aug1); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem1->Name, aug1); - } - } - } - if (aug2) { - const Item_Struct* augitem2 = database.GetItem(aug2); - if (augitem2) { - if (!CheckLoreConflict(augitem2)) { - inst->PutAugment(&database, 1, aug2); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem2->Name, aug2); - } - } - } - if (aug3) { - const Item_Struct* augitem3 = database.GetItem(aug3); - if (augitem3) { - if (!CheckLoreConflict(augitem3)) { - inst->PutAugment(&database, 2, aug3); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem3->Name, aug3); - } - } - } - if (aug4) { - const Item_Struct* augitem4 = database.GetItem(aug4); - if (augitem4) { - if (!CheckLoreConflict(augitem4)) { - inst->PutAugment(&database, 3, aug4); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem4->Name, aug4); - } - } - } - if (aug5) { - const Item_Struct* augitem5 = database.GetItem(aug5); - if (augitem5) { - if (!CheckLoreConflict(augitem5)) { - inst->PutAugment(&database, 4, aug5); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem5->Name, aug5); - } - } - } - if (attuned) { - if (inst->GetItem()->Attuneable) { - inst->SetInstNoDrop(true); - } - } - if (to_slot == SLOT_CURSOR) - { - //inst->SetCharges( - PushItemOnCursor(*inst); - // Send item packet to user - SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem); - } - else - { - PutItemInInventory(to_slot, *inst, true); - } - safe_delete(inst); + return false; + */ - if ((RuleB(Character, EnableDiscoveredItems))) - { - if(!GetGM() && !IsDiscovered(item_id)) - DiscoverItem(item_id); + if(((item->ItemClass != ItemClassCommon) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5)) { + Message(13, "You can not augment an augment or a non-common class item."); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an augment or a non-common class item.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + uint32 augments[MAX_AUGMENT_SLOTS] = { aug1, aug2, aug3, aug4, aug5 }; + + for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { + if(augments[iter] && (database.GetItem(augments[iter]) == nullptr)) { + Message(13, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an augment (Aug%i) with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + } + + uint32 classes = item->Classes; + uint32 races = item->Races; + uint32 slots = item->Slots; + + bool enforcewear = RuleB(Inventory, EnforceAugmentWear); + bool enforcerestr = RuleB(Inventory, EnforceAugmentRestriction); + bool enforceusable = RuleB(Inventory, EnforceAugmentUsability); + + for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { + const Item_Struct* augtest = database.GetItem(augments[iter]); + + if(augtest) { + if(CheckLoreConflict(augtest)) { + // ditto + // DuplicateLoreMessage(augtest->ID); + Message(13, "You already have a lore %s (%u) in your inventory.", augtest->Name, augtest->ID); + + return false; + } + + /* + if(augtest->gm && (this->Admin() < 100)) { + Message(0, "You are not a GM and can not summon this augment!"); + + return false; + } + */ + + if(augtest->AugType == 0) { + Message(13, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to use a non-augment item (Aug%i) as an augment.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, iter + 1, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + if(enforcewear) { + if((item->AugSlotType[iter] == AugTypeNone) || !(((uint32)1 << (item->AugSlotType[iter] - 1)) & augtest->AugType)) { + Message(13, "Augment %u (Aug%i) is not allowed wear on Item %u.", augments[iter], iter + 1, item->ID); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an item with a disallowed augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + } + + if(enforcerestr) { + bool restrictfail = false; + uint8 it = item->ItemType; + + switch(augtest->AugRestrict) { + case AugRestrAny: + break; + case AugRestrArmor: + switch(it) { + case ItemTypeArmor: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrWeapons: + switch(it) { + case ItemType1HSlash: + case ItemType1HBlunt: + case ItemType1HPiercing: + case ItemTypeMartial: + case ItemType2HSlash: + case ItemType2HBlunt: + case ItemType2HPiercing: + case ItemTypeBow: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HWeapons: + switch(it) { + case ItemType1HSlash: + case ItemType1HBlunt: + case ItemType1HPiercing: + case ItemTypeMartial: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr2HWeapons: + switch(it) { + case ItemType2HSlash: + case ItemType2HBlunt: + case ItemType2HPiercing: + case ItemTypeBow: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HSlash: + switch(it) { + case ItemType1HSlash: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HBlunt: + switch(it) { + case ItemType1HBlunt: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrPiercing: + switch(it) { + case ItemType1HPiercing: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrHandToHand: + switch(it) { + case ItemTypeMartial: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr2HSlash: + switch(it) { + case ItemType2HSlash: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr2HBlunt: + switch(it) { + case ItemType2HBlunt: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr2HPierce: + switch(it) { + case ItemType2HPiercing: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrBows: + switch(it) { + case ItemTypeBow: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrShields: + switch(it) { + case ItemTypeShield: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HSlash1HBluntOrHandToHand: + switch(it) { + case ItemType1HSlash: + case ItemType1HBlunt: + case ItemTypeMartial: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HBluntOrHandToHand: + switch(it) { + case ItemType1HBlunt: + case ItemTypeMartial: + break; + default: + restrictfail = true; + break; + } + break; + // These 3 are in-work + case AugRestrUnknown1: + case AugRestrUnknown2: + case AugRestrUnknown3: + default: + restrictfail = true; + break; + } + + if(restrictfail) { + Message(13, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], iter + 1, item->ID); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an item with a restricted augment (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + } + + if(enforceusable) { + classes &= augtest->Classes; + + if(item->Classes && !classes) { + Message(13, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable by any class.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + races &= augtest->Races; + + if(item->Races && !races) { + Message(13, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable by any race.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + slots &= augtest->Slots; + + if(item->Slots && !slots) { + Message(13, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable in any slot.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } } } } - else { // Item was already in inventory & is a LORE item or was a GM only item. Give them a message about it. - if (foundlore){ - DuplicateLoreMessage(item_id); - //Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id); - } - else if (foundgm) - Message(0, "You are not a GM to summon this item"); + + // Item is not lore.. + // Item is not GM-Only.. + // Augments are valid and match allowed slots.. + // ..or Augments are valid and do not match allowed slots and.. + // ..EnforceAugmentWear=false and EnforceAugmentRestriction=false and EnforceAugmentUsability=false + + ItemInst* inst = database.CreateItem(item, charges); + + if(inst == nullptr) { + Message(13, "An unknown server error has occurred and your item was not created."); + LogFile->write(EQEMuLog::Error, "Player %s on account %s encountered an unknown item creation error.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; } + + for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { + if(augments[iter]) + inst->PutAugment(&database, iter, augments[iter]); + } + + // This may need augment checks as well..left out for now + if(attuned && inst->GetItem()->Attuneable) + inst->SetInstNoDrop(true); + + if(to_slot == SLOT_CURSOR) { + //inst->SetCharges( + PushItemOnCursor(*inst); + // Send item packet to user + SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem); + } + else { + PutItemInInventory(to_slot, *inst, true); + } + + safe_delete(inst); + + if((RuleB(Character, EnableDiscoveredItems)) && !GetGM()) { + if(!IsDiscovered(item_id)) + DiscoverItem(item_id); + + for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { + if(augments[iter] && !IsDiscovered(augments[iter])) + DiscoverItem(augments[iter]); + } + } + + return true; } // Drop item from inventory to ground (generally only dropped from SLOT_CURSOR) From 02d6471c888e1bd726a8ab2685a1832c73fb3b1e Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 18 Mar 2014 06:00:46 -0400 Subject: [PATCH 04/21] Fix for name/account discrepancy in \\zone\inventory.cpp. Rearranged/condensed code snippets in Client::SummonItem(). Added 'augslotvisible' check to augment validation in C::SI(). --- changelog.txt | 5 ++ zone/inventory.cpp | 199 ++++++++++++++++++++++++--------------------- 2 files changed, 111 insertions(+), 93 deletions(-) diff --git a/changelog.txt b/changelog.txt index 23374f83c..08fc5dd27 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/18/2014 == +Uleat: Fixed the name/account discrepancy in the Client::SummonItem() code as well as the origin of the mistake (thanks K_K!) +Uleat: Condensed and rearranged certain snippets of code in SummonItem(). Added a 'augslotvisible' check to validation check. +Note: If you are experiencing issues with SummonItem, please enable 'INVENTORY_ERROR' logging if it not active on your server. + == 03/17/2014 == Uleat: Updated Client::SummonItem() to check for valid item combinations when augmentations are passed. Uleat: Changed the return type of Client::SummonItem() from void to bool. Calling methods and APIs have not been update as yet. diff --git a/zone/inventory.cpp b/zone/inventory.cpp index c6f1663da..b18514056 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -201,72 +201,45 @@ bool Client::CheckLoreConflict(const Item_Struct* item) { } bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { - // I have 'over-logged' failure messages to aid in any troubleshooting of issues that arise from this change. - // The 'LogFile' code may be taken out once after a period of time with no script/recipe issues. - // - // I have also incorporated a bool return type..but, have not updated any calling methods to process failures. - // Once the APIs are updated, scripts may also be updated. + // TODO: update calling methods and script apis to handle a failure return const Item_Struct* item = database.GetItem(item_id); + // make sure the item exists if(item == nullptr) { Message(13, "Item %u does not exist.", item_id); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } - else { - // if the item is stackable and the charge amount is -1 or 0 then set to 1 charge. - // removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant - if(charges <= 0 && item->Stackable) - charges = 1; - - // if the charges is -1, then no charge value was passed in set to max charges - else if(charges == -1) - charges = item->MaxCharges; - - // in any other situation just use charges as passed - } - - // Base item is lore..so, cancel summon - if(CheckLoreConflict(item)) { - // this is the 'you can not pickup another...' message. I don't feel this is appropriate - // for a summoning failure response + // check that there is not a lore conflict between base item and existing inventory + else if(CheckLoreConflict(item)) { // DuplicateLoreMessage(item_id); Message(13, "You already have a lore %s (%i) in your inventory.", item->Name, item_id); return false; } + // check to make sure we are augmenting an augmentable item + else if(((item->ItemClass != ItemClassCommon) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5)) { + Message(13, "You can not augment an augment or a non-common class item."); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an augment or a non-common class item.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); - // Checking to see if it is a GM only Item or not. + return false; + } + // check to make sure we are a GM if the item is GM-only /* - if(item->gm && (this->Admin() < 100)) - Message(0, "You are not a GM and can not summon this item!"); + else if(item->gm && (this->Admin() < 100)) + Message(13, "You are not a GM and can not summon this item."); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only item with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5); return false; */ - if(((item->ItemClass != ItemClassCommon) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5)) { - Message(13, "You can not augment an augment or a non-common class item."); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an augment or a non-common class item.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); - - return false; - } - uint32 augments[MAX_AUGMENT_SLOTS] = { aug1, aug2, aug3, aug4, aug5 }; - for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { - if(augments[iter] && (database.GetItem(augments[iter]) == nullptr)) { - Message(13, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an augment (Aug%i) with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); - - return false; - } - } - uint32 classes = item->Classes; uint32 races = item->Races; uint32 slots = item->Slots; @@ -278,41 +251,62 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { const Item_Struct* augtest = database.GetItem(augments[iter]); - if(augtest) { + if(augtest == nullptr) { + if(augments[iter]) { + Message(13, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an augment (Aug%i) with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + } + else { + // check that there is not a lore conflict between augment and existing inventory if(CheckLoreConflict(augtest)) { - // ditto // DuplicateLoreMessage(augtest->ID); Message(13, "You already have a lore %s (%u) in your inventory.", augtest->Name, augtest->ID); return false; } - + // check that augment is an actual augment + else if(augtest->AugType == 0) { + Message(13, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to use a non-augment item (Aug%i) as an augment.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, (iter + 1), aug1, aug2, aug3, aug4, aug5); + + return false; + } + // check to make sure we are a GM if the augment is GM-only /* - if(augtest->gm && (this->Admin() < 100)) { - Message(0, "You are not a GM and can not summon this augment!"); + else if(augtest->gm && (this->Admin() < 100)) { + Message(13, "You are not a GM and can not summon this augment."); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only augment (Aug%i) with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5); return false; } */ - if(augtest->AugType == 0) { - Message(13, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to use a non-augment item (Aug%i) as an augment.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, iter + 1, aug1, aug2, aug3, aug4, aug5); - - return false; - } - + // check for augment type allowance if(enforcewear) { if((item->AugSlotType[iter] == AugTypeNone) || !(((uint32)1 << (item->AugSlotType[iter] - 1)) & augtest->AugType)) { - Message(13, "Augment %u (Aug%i) is not allowed wear on Item %u.", augments[iter], iter + 1, item->ID); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an item with a disallowed augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + Message(13, "Augment %u (Aug%i) is not acceptable wear on Item %u.", augments[iter], iter + 1, item->ID); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an item with an unacceptable augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + if(item->AugSlotVisible[iter] == 0) { + Message(13, "Item %u has not evolved enough to accept Augment %u (Aug%i).", item->ID, augments[iter], iter + 1); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an unevolved item with augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5); return false; } } + // check for augment to item restriction if(enforcerestr) { bool restrictfail = false; uint8 it = item->ItemType; @@ -481,41 +475,38 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, } if(restrictfail) { - Message(13, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], iter + 1, item->ID); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an item with a restricted augment (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + Message(13, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], (iter + 1), item->ID); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an item with a restricted augment (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5); return false; } } if(enforceusable) { - classes &= augtest->Classes; - - if(item->Classes && !classes) { - Message(13, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable by any class.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + // check for class usability + if(item->Classes && !(classes &= augtest->Classes)) { + Message(13, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], (iter + 1)); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable by any class.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } - races &= augtest->Races; - - if(item->Races && !races) { - Message(13, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable by any race.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + // check for race usability + if(item->Races && !(races &= augtest->Races)) { + Message(13, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], (iter + 1)); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable by any race.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } - slots &= augtest->Slots; - - if(item->Slots && !slots) { - Message(13, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable in any slot.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + // check for slot usability + if(item->Slots && !(slots &= augtest->Slots)) { + Message(13, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], (iter + 1)); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable in any slot.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } @@ -523,35 +514,56 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, } } - // Item is not lore.. - // Item is not GM-Only.. - // Augments are valid and match allowed slots.. - // ..or Augments are valid and do not match allowed slots and.. - // ..EnforceAugmentWear=false and EnforceAugmentRestriction=false and EnforceAugmentUsability=false + // validation passed..so, set the charges and create the actual item + + // if the item is stackable and the charge amount is -1 or 0 then set to 1 charge. + // removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant + if(charges <= 0 && item->Stackable) + charges = 1; + + // if the charges is -1, then no charge value was passed in set to max charges + else if(charges == -1) + charges = item->MaxCharges; + + // in any other situation just use charges as passed ItemInst* inst = database.CreateItem(item, charges); if(inst == nullptr) { Message(13, "An unknown server error has occurred and your item was not created."); + // this goes to logfile since this is a major error LogFile->write(EQEMuLog::Error, "Player %s on account %s encountered an unknown item creation error.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } + // add any validated augments for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { if(augments[iter]) inst->PutAugment(&database, iter, augments[iter]); } - // This may need augment checks as well..left out for now + // attune item if(attuned && inst->GetItem()->Attuneable) inst->SetInstNoDrop(true); + // check to see if item is usable in requested slot + if(enforceusable && (((to_slot >= 0) && (to_slot <= 21)) || (to_slot == 9999))) { + uint32 slottest = (to_slot == 9999) ? 22 : to_slot; + + if(!(slots & ((uint32)1 << slottest))) { + Message(0, "This item is not equipable at slot %u - moving to cursor.", to_slot); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to equip an item unusable in slot %u - moved to cursor.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, to_slot, item->ID, aug1, aug2, aug3, aug4, aug5); + + to_slot = SLOT_CURSOR; + } + } + + // put item into inventory if(to_slot == SLOT_CURSOR) { - //inst->SetCharges( PushItemOnCursor(*inst); - // Send item packet to user SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem); } else { @@ -560,6 +572,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, safe_delete(inst); + // discover item and any augments if((RuleB(Character, EnableDiscoveredItems)) && !GetGM()) { if(!IsDiscovered(item_id)) DiscoverItem(item_id); @@ -1373,7 +1386,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { //verify shared bank transactions in the database if(src_inst && src_slot_id >= 2500 && src_slot_id <= 2550) { if(!database.VerifyInventory(account_id, src_slot_id, src_inst)) { - LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", account_name, GetName()); + LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(dst_slot_id,0,true); return(false); } @@ -1388,7 +1401,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } if(dst_inst && dst_slot_id >= 2500 && dst_slot_id <= 2550) { if(!database.VerifyInventory(account_id, dst_slot_id, dst_inst)) { - LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", account_name, GetName()); + LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(src_slot_id,0,true); return(false); } From 3054a4c307a110ddee17d359c23d61d1c0b96f35 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 18 Jun 2014 15:58:26 -0700 Subject: [PATCH 05/21] Database interface improvement with stl style iterators --- common/CMakeLists.txt | 4 ++ common/MySQLRequestResult.cpp | 103 ++++++++++++++++++++++++++++++++++ common/MySQLRequestResult.h | 54 ++++++++++++++++++ common/MySQLRequestRow.cpp | 54 ++++++++++++++++++ common/MySQLRequestRow.h | 37 ++++++++++++ common/dbcore.cpp | 79 +++++++++++++++++++++++++- common/dbcore.h | 4 +- 7 files changed, 333 insertions(+), 2 deletions(-) create mode 100644 common/MySQLRequestResult.cpp create mode 100644 common/MySQLRequestResult.h create mode 100644 common/MySQLRequestRow.cpp create mode 100644 common/MySQLRequestRow.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a63563983..35cb7eb7f 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -39,6 +39,8 @@ SET(common_sources MiscFunctions.cpp moremath.cpp Mutex.cpp + MySQLRequestResult.cpp + MySQLRequestRow.cpp opcode_map.cpp opcodemgr.cpp packet_dump.cpp @@ -149,6 +151,8 @@ SET(common_headers MiscFunctions.h moremath.h Mutex.h + MySQLRequestResult.h + MySQLRequestRow.h op_codes.h opcode_dispatch.h opcodemgr.h diff --git a/common/MySQLRequestResult.cpp b/common/MySQLRequestResult.cpp new file mode 100644 index 000000000..16d7fe9a0 --- /dev/null +++ b/common/MySQLRequestResult.cpp @@ -0,0 +1,103 @@ +#include "MySQLRequestResult.h" + + +MySQLRequestResult::MySQLRequestResult() + : m_CurrentRow(), m_OneBeyondRow() +{ + ZeroOut(); +} + +MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, uint32 rowCount, uint32 columnCount, uint32 lastInsertedID, uint32 errorNumber, char *errorBuffer) + : m_CurrentRow(result), m_OneBeyondRow() +{ + if (errorBuffer != nullptr) + m_Success = false; + else if (errorBuffer == nullptr && result == nullptr) + { + m_Success = false; + +#ifdef _EQDEBUG + std::cout << "DB Query Error: No Result" << std::endl; +#endif + m_ErrorNumber = UINT_MAX; + m_ErrorBuffer = new char[MYSQL_ERRMSG_SIZE]; + + strcpy(m_ErrorBuffer, "DBcore::RunQuery: No Result"); + } + else + m_Success = true; + + m_Result = result; + m_ErrorBuffer = errorBuffer; + m_RowsAffected = rowsAffected; + m_RowCount = rowCount; + m_ColumnCount = columnCount; + m_LastInsertedID = lastInsertedID; + m_ErrorNumber = errorNumber; +} + +void MySQLRequestResult::FreeInternals() +{ + safe_delete_array(m_ErrorBuffer); + + if (m_Result != nullptr) + mysql_free_result(m_Result); + + ZeroOut(); +} + +void MySQLRequestResult::ZeroOut() +{ + m_Success = false; + m_Result = nullptr; + m_ErrorBuffer = nullptr; + m_RowCount = 0; + m_RowsAffected = 0; + m_LastInsertedID = 0; +} + +MySQLRequestResult::~MySQLRequestResult() +{ + FreeInternals(); +} + +MySQLRequestResult::MySQLRequestResult(MySQLRequestResult&& moveItem) + : m_CurrentRow(moveItem.m_CurrentRow), m_OneBeyondRow() +{ + m_Result = moveItem.m_Result; + m_ErrorBuffer = moveItem.m_ErrorBuffer; + m_Success = moveItem.m_Success; + m_RowCount = moveItem.m_RowCount; + m_RowsAffected = moveItem.m_RowsAffected; + m_LastInsertedID = moveItem.m_LastInsertedID; + + // Keeps deconstructor from double freeing + // pre move instance. + moveItem.ZeroOut(); +} + +MySQLRequestResult& MySQLRequestResult::operator=(MySQLRequestResult&& other) +{ + // Assigning something to itself? + // Silly! (but happens) + if (this == &other) + return *this; + + FreeInternals(); + + m_Success = other.m_Success; + + m_Result = other.m_Result; + m_ErrorBuffer = other.m_ErrorBuffer; + + m_RowCount = other.m_RowCount; + m_RowsAffected = other.m_RowsAffected; + m_LastInsertedID = other.m_LastInsertedID; + m_CurrentRow = other.m_CurrentRow; + m_OneBeyondRow = other.m_OneBeyondRow; + + // Keeps deconstructor from double freeing + // pre move instance. + other.ZeroOut(); + return *this; +} \ No newline at end of file diff --git a/common/MySQLRequestResult.h b/common/MySQLRequestResult.h new file mode 100644 index 000000000..807efdfc0 --- /dev/null +++ b/common/MySQLRequestResult.h @@ -0,0 +1,54 @@ +#ifndef MYSQL_REQUEST_RESULT_H +#define MYSQL_REQUEST_RESULT_H + +#ifdef _WINDOWS + #include + #include +#endif + +#include +#include <../common/types.h> +#include <../common/MySQLRequestRow.h> +#include + +class MySQLRequestResult { +private: + MYSQL_RES* m_Result; + char* m_ErrorBuffer; + MySQLRequestRow m_CurrentRow; + MySQLRequestRow m_OneBeyondRow; + + bool m_Success; + uint32 m_RowsAffected; + uint32 m_RowCount; + uint32 m_ColumnCount; + uint32 m_LastInsertedID; + uint32 m_ErrorNumber; + + +public: + + MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected = 0, uint32 rowCount = 0, uint32 columnCount = 0, uint32 lastInsertedID = 0, uint32 errorNumber = 0, char *errorBuffer = nullptr); + MySQLRequestResult(); + MySQLRequestResult(MySQLRequestResult&& moveItem); + ~MySQLRequestResult(); + + MySQLRequestResult& operator=(MySQLRequestResult&& other); + + bool Success() const { return m_Success;} + std::string ErrorMessage() const {return std::string(m_ErrorBuffer);} + uint32 ErrorNumber() const {return m_ErrorNumber;} + uint32 RowsAffected() const {return m_RowsAffected;} + uint32 RowCount() const {return m_RowCount;} + uint32 ColumnCount() const {return m_ColumnCount;} + + MySQLRequestRow& begin() { return m_CurrentRow; } + MySQLRequestRow& end() { return m_OneBeyondRow;} + +private: + void FreeInternals(); + void ZeroOut(); +}; + + +#endif \ No newline at end of file diff --git a/common/MySQLRequestRow.cpp b/common/MySQLRequestRow.cpp new file mode 100644 index 000000000..5fc4a40e8 --- /dev/null +++ b/common/MySQLRequestRow.cpp @@ -0,0 +1,54 @@ +#include "MySQLRequestRow.h" + +MySQLRequestRow::MySQLRequestRow(const MySQLRequestRow& row) + : m_Result(row.m_Result), m_MySQLRow(row.m_MySQLRow) +{ +} + +MySQLRequestRow::MySQLRequestRow() + : m_Result(nullptr), m_MySQLRow(nullptr) +{ +} + +MySQLRequestRow::MySQLRequestRow(MySQLRequestRow&& moveItem) +{ + m_Result = moveItem.m_Result; + m_MySQLRow = moveItem.m_MySQLRow; + + moveItem.m_Result = nullptr; + moveItem.m_MySQLRow = nullptr; +} + +MySQLRequestRow::MySQLRequestRow(MYSQL_RES *result) + : m_Result(result), m_MySQLRow(mysql_fetch_row(m_Result)) +{ +} + +MySQLRequestRow& MySQLRequestRow::operator++() +{ + m_MySQLRow = mysql_fetch_row(m_Result); + return *this; +} + +MySQLRequestRow MySQLRequestRow::operator++(int) +{ + MySQLRequestRow tmp(*this); + operator++(); + return tmp; +} + +bool MySQLRequestRow::operator==(const MySQLRequestRow& rhs) +{ + return m_MySQLRow == rhs.m_MySQLRow; +} + +bool MySQLRequestRow::operator!=(const MySQLRequestRow& rhs) +{ + return m_MySQLRow != rhs.m_MySQLRow; +} + +char* MySQLRequestRow::operator[](int index) +{ + + return m_MySQLRow[index]; +} \ No newline at end of file diff --git a/common/MySQLRequestRow.h b/common/MySQLRequestRow.h new file mode 100644 index 000000000..615a75371 --- /dev/null +++ b/common/MySQLRequestRow.h @@ -0,0 +1,37 @@ +#ifndef MYSQL_REQUEST_ROW_H +#define MYSQL_REQUEST_ROW_H + +#ifdef _WINDOWS + #include + #include +#endif + +#include +#include +#include <../common/types.h> + +class MySQLRequestRow : public std::iterator +{ + +private: + MYSQL_RES* m_Result; + MYSQL_ROW m_MySQLRow; + +public: + + MySQLRequestRow(); + MySQLRequestRow(MYSQL_RES *result); + MySQLRequestRow(const MySQLRequestRow& row); + MySQLRequestRow(MySQLRequestRow&& moveItem); + MySQLRequestRow& operator++(); + MySQLRequestRow operator++(int); + bool operator==(const MySQLRequestRow& rhs); + bool operator!=(const MySQLRequestRow& rhs); + + char* operator[](int index); + +}; + + + +#endif \ No newline at end of file diff --git a/common/dbcore.cpp b/common/dbcore.cpp index a2c6bc7f8..ce1b41bdc 100644 --- a/common/dbcore.cpp +++ b/common/dbcore.cpp @@ -58,6 +58,83 @@ void DBcore::ping() { MDatabase.unlock(); } +MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, bool retryOnFailureOnce) +{ + LockMutex lock(&MDatabase); + + // Reconnect if we are not connected before hand. + if (pStatus != Connected) + Open(); + + // request query. != 0 indicates some kind of error. + if (mysql_real_query(&mysql, query, querylen) != 0) + { + unsigned int errorNumber = mysql_errno(&mysql); + + if (errorNumber == CR_SERVER_GONE_ERROR) + pStatus = Error; + + // error appears to be a disconnect error, may need to try again. + if (errorNumber == CR_SERVER_LOST || errorNumber == CR_SERVER_GONE_ERROR) + { + + if (retryOnFailureOnce) + { + std::cout << "Database Error: Lost connection, attempting to recover...." << std::endl; + MySQLRequestResult requestResult = QueryDatabase(query, querylen, false); + + if (requestResult.Success()) + { + std::cout << "Reconnection to database successful." << std::endl; + return requestResult; + } + + } + + pStatus = Error; + + char *errorBuffer = new char[MYSQL_ERRMSG_SIZE]; + + snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + + std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl; + + return MySQLRequestResult(nullptr, 0, 0, 0, 0, (uint32)mysql_errno(&mysql), errorBuffer); + } + + char *errorBuffer = new char[MYSQL_ERRMSG_SIZE]; + snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + +#ifdef _EQDEBUG + std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl; +#endif + return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql),errorBuffer); + + } + + // successful query. get results. + MYSQL_RES* res = mysql_store_result(&mysql); + MySQLRequestResult requestResult(res, (uint32)mysql_affected_rows(&mysql), (uint32)mysql_num_rows(res), (uint32)mysql_field_count(&mysql), (uint32)mysql_insert_id(&mysql)); + + +#if DEBUG_MYSQL_QUERIES >= 1 + if (requestResult.Success()) + { + std::cout << "query successful"; + if (requestResult.Result()) + std::cout << ", " << (int) mysql_num_rows(requestResult.Result()) << " rows returned"; + + std::cout << ", " << requestResult.RowCount() << " rows affected"; + std::cout<< std::endl; + } + else { + std::cout << "QUERY: query FAILED" << std::endl; + } +#endif + + return requestResult; +} + bool DBcore::RunQuery(const char* query, uint32 querylen, char* errbuf, MYSQL_RES** result, uint32* affected_rows, uint32* last_insert_id, uint32* errnum, bool retry) { if (errnum) *errnum = 0; @@ -111,7 +188,7 @@ bool DBcore::RunQuery(const char* query, uint32 querylen, char* errbuf, MYSQL_RE if (affected_rows) *affected_rows = mysql_affected_rows(&mysql); if (last_insert_id) - *last_insert_id = mysql_insert_id(&mysql); + *last_insert_id = (uint32)mysql_insert_id(&mysql); if (result) { if (*result) { ret = true; diff --git a/common/dbcore.h b/common/dbcore.h index ed5e62133..13a395eac 100644 --- a/common/dbcore.h +++ b/common/dbcore.h @@ -4,8 +4,8 @@ #ifdef _WINDOWS #include #include - //#include #endif + #include #include "../common/types.h" #include "../common/Mutex.h" @@ -13,6 +13,7 @@ #include "../common/queue.h" #include "../common/timer.h" #include "../common/Condition.h" +#include "../common/MySQLRequestResult.h" class DBcore { public: @@ -22,6 +23,7 @@ public: ~DBcore(); eStatus GetStatus() { return pStatus; } bool RunQuery(const char* query, uint32 querylen, char* errbuf = 0, MYSQL_RES** result = 0, uint32* affected_rows = 0, uint32* last_insert_id = 0, uint32* errnum = 0, bool retry = true); + MySQLRequestResult QueryDatabase(const char* query, uint32 querylen, bool retryOnFailureOnce = true); uint32 DoEscapeString(char* tobuf, const char* frombuf, uint32 fromlen); void ping(); MYSQL* getMySQL(){ return &mysql; } From ee6d7ae6bae35974f553bd23cc86e66189c18de4 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 2 Jul 2014 08:42:18 -0400 Subject: [PATCH 06/21] Implemented SE_Sanctuary - Places caster at bottom hate list, effect fades if caster cast spell on targets other than self. --- changelog.txt | 3 +++ common/spdat.h | 4 ++-- zone/bonuses.cpp | 8 ++++++++ zone/common.h | 1 + zone/spell_effects.cpp | 36 ++++++++++++++++++++++++++++++++++++ zone/spells.cpp | 3 +++ 6 files changed, 53 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 0a22ccf8d..08527447b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 07/2/2014 == +Kayen: Implemented SE_Sanctuary - Places caster at bottom hate list, effect fades if caster cast spell on targets other than self. + == 06/25/2014 == Kayen: Updated SE_Hate (Renamed from SE_Hate2) to now properly work for instant +/- hate spells. diff --git a/common/spdat.h b/common/spdat.h index 75d351fc2..0c2cf4b1f 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -455,11 +455,11 @@ typedef enum { #define SE_MitigateDamageShield 305 // implemented - off hand attacks only (Shielding Resistance) //#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds //#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor. -#define SE_SuspendMinion 308 // not implemented as bonus +#define SE_SuspendMinion 308 // implemented #define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point #define SE_ReduceReuseTimer 310 // implemented #define SE_LimitCombatSkills 311 // implemented - Excludes focus from procs (except if proc is a memorizable spell) -//#define SE_Sanctuary 312 // *not implemented +#define SE_Sanctuary 312 // implemented - Places caster at bottom hate list, effect fades if cast cast spell on targets other than self. #define SE_ForageAdditionalItems 313 // implemented[AA] - chance to forage additional items #define SE_Invisibility2 314 // implemented - fixed duration invisible #define SE_InvisVsUndead2 315 // implemented - fixed duration ITU diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index a316c4b3b..0e521a6db 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2856,6 +2856,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->PetMeleeMitigation += effect_value; break; + case SE_Sanctuary: + newbon->Sanctuary = true; + break; + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { @@ -4330,6 +4334,10 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.FinishingBlowLvl[1] = effect_value; break; + case SE_Sanctuary: + spellbonuses.Sanctuary = effect_value; + break; + } } } diff --git a/zone/common.h b/zone/common.h index 88600492e..0768a0835 100644 --- a/zone/common.h +++ b/zone/common.h @@ -372,6 +372,7 @@ struct StatBonuses { int16 DStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value bool BerserkSPA; // berserk effect int16 Metabolism; // Food/drink consumption rates. + bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others. // AAs int8 Packrat; //weight reduction for items, 1 point = 10% diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 090fb4b75..7bb227bd0 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2732,6 +2732,22 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } + case SE_Sanctuary: + { + std::list npc_list; + entity_list.GetNPCList(npc_list); + + for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { + + NPC* npc = *itr; + + if (npc && npc->CheckAggro(this)) + npc->SetHate(caster, 1); + + } + break; + } + // Handled Elsewhere case SE_ImmuneFleeing: case SE_NegateSpellEffect: @@ -2960,6 +2976,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_Berserk: case SE_Vampirism: case SE_Metabolism: + case SE_FinishingBlow: + case SE_FinishingBlowLvl: + case SE_Assassinate: + case SE_AssassinateLevel: { break; } @@ -3604,6 +3624,22 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste break; } + case SE_Sanctuary: + { + std::list npc_list; + entity_list.GetNPCList(npc_list); + + for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { + + NPC* npc = *itr; + + if (npc && npc->CheckAggro(this)) + npc->SetHate(caster, 1); + + } + break; + } + default: { // do we need to do anyting here? diff --git a/zone/spells.cpp b/zone/spells.cpp index 59dbb6625..6a87f2409 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -183,6 +183,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot); return(false); } + //It appears that the Sanctuary effect is removed by a check on the client side (keep this however for redundancy) + if (spellbonuses.Sanctuary && (spells[spell_id].targettype != ST_Self && GetTarget() != this) || IsDetrimentalSpell(spell_id)) + BuffFadeByEffect(SE_Sanctuary); if(IsClient()){ int chance = CastToClient()->GetFocusEffect(focusFcMute, spell_id); From 4a49a11e73e495bd3c27813dd904710069eefa89 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 2 Jul 2014 11:18:50 -0400 Subject: [PATCH 07/21] Implemented SE_ResourceTap - Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. --- changelog.txt | 2 +- common/spdat.h | 2 +- zone/mob.h | 1 + zone/spell_effects.cpp | 32 ++++++++++++++++++++++++++++++-- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/changelog.txt b/changelog.txt index 08527447b..ac53a761b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,7 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 07/2/2014 == Kayen: Implemented SE_Sanctuary - Places caster at bottom hate list, effect fades if caster cast spell on targets other than self. - +Kayen: Implemented SE_ResourceTap - Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. == 06/25/2014 == Kayen: Updated SE_Hate (Renamed from SE_Hate2) to now properly work for instant +/- hate spells. diff --git a/common/spdat.h b/common/spdat.h index 0c2cf4b1f..3b9d88995 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -604,7 +604,7 @@ typedef enum { #define SE_TriggerSpellThreshold 454 // implemented Trigger effect on X amount of spell damage taken #define SE_AddHatePct 455 // implemented Modify total hate by % #define SE_AddHateOverTimePct 456 // implemented Modify total hate by % over time. -//#define SE_ResourceTap 457 // not used +#define SE_ResourceTap 457 // implemented Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. //#define SE_FactionModPct 458 // not used #define SE_DamageModifier2 459 // implemented - Modifies melee damage by skill type diff --git a/zone/mob.h b/zone/mob.h index 5c1240e83..4f7b47c03 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -229,6 +229,7 @@ public: bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier); void SpellProjectileEffect(); bool TrySpellProjectile(Mob* spell_target, uint16 spell_id); + void ResourceTap(int32 damage, uint16 spell_id); //Buff void BuffProcess(); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 7bb227bd0..498e5d7ec 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -224,9 +224,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) dmg = (int32) (dmg * partial / 100); //handles AAs and what not... - if(caster) + if(caster) { dmg = caster->GetActSpellDamage(spell_id, dmg, this); - + caster->ResourceTap(-dmg, spell_id); + } + dmg = -dmg; Damage(caster, dmg, spell_id, spell.skill, false, buffslot, false); } @@ -3367,6 +3369,8 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste if(caster->IsNPC()) effect_value = caster->CastToNPC()->GetActSpellDamage(spell_id, effect_value, this); + + caster->ResourceTap(-effect_value, spell_id); } effect_value = -effect_value; @@ -6283,4 +6287,28 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){ return true; } +void Mob::ResourceTap(int32 damage, uint16 spellid){ + //'this' = caster + if (!IsValidSpell(spellid)) + return; + for (int i = 0; i <= EFFECT_COUNT; i++) + { + if (spells[spellid].effectid[i] == SE_ResourceTap){ + + damage += (damage * spells[spellid].base[i])/100; + + if (spells[spellid].max[i] && (damage > spells[spellid].max[i])) + damage = spells[spellid].max[i]; + + if (spells[spellid].base2[i] == 0) //HP Tap + SetHP((GetHP()+ damage)); + + if (spells[spellid].base2[i] == 1) //Mana Tap + SetMana(GetMana() + damage); + + if (spells[spellid].base2[i] == 2 && IsClient()) //Endurance Tap + CastToClient()->SetEndurance(CastToClient()->GetEndurance() + damage); + } + } +} From 35e72692c1a6fd415c93284bff9218d309e34e3e Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 2 Jul 2014 11:54:59 -0400 Subject: [PATCH 08/21] Implemented SE_FactionModPct - Modifies faction gains and losses by percent. --- changelog.txt | 1 + common/spdat.h | 2 +- zone/bonuses.cpp | 26 ++++++++++++++++++++++++++ zone/client.cpp | 3 +++ zone/common.h | 1 + 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index ac53a761b..26da2b13c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 07/2/2014 == Kayen: Implemented SE_Sanctuary - Places caster at bottom hate list, effect fades if caster cast spell on targets other than self. Kayen: Implemented SE_ResourceTap - Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. +Kayen: Implemented SE_FactionModPct - Modifies faction gains and losses by percent. == 06/25/2014 == Kayen: Updated SE_Hate (Renamed from SE_Hate2) to now properly work for instant +/- hate spells. diff --git a/common/spdat.h b/common/spdat.h index 3b9d88995..c579a1af7 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -605,7 +605,7 @@ typedef enum { #define SE_AddHatePct 455 // implemented Modify total hate by % #define SE_AddHateOverTimePct 456 // implemented Modify total hate by % over time. #define SE_ResourceTap 457 // implemented Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. -//#define SE_FactionModPct 458 // not used +#define SE_FactionModPct 458 // implemented Modifies faction gains and losses by percent. #define SE_DamageModifier2 459 // implemented - Modifies melee damage by skill type // LAST diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 0e521a6db..e27378207 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1315,6 +1315,16 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) newbon->PetMeleeMitigation += base1; break; + case SE_FactionModPct: + { + if((base1 < 0) && (newbon->FactionModPct > base1)) + newbon->FactionModPct = base1; + + else if(newbon->FactionModPct < base1) + newbon->FactionModPct = base1; + break; + } + } } } @@ -2860,6 +2870,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->Sanctuary = true; break; + case SE_FactionModPct: + { + if((effect_value < 0) && (newbon->FactionModPct > effect_value)) + newbon->FactionModPct = effect_value; + + else if(newbon->FactionModPct < effect_value) + newbon->FactionModPct = effect_value; + break; + } + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { @@ -4338,6 +4358,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) spellbonuses.Sanctuary = effect_value; break; + case SE_FactionModPct: + spellbonuses.FactionModPct = effect_value; + itembonuses.FactionModPct = effect_value; + aabonuses.FactionModPct = effect_value; + break; + } } } diff --git a/zone/client.cpp b/zone/client.cpp index 4ec3ce2d7..12a9ada67 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7629,6 +7629,9 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui if(npc_value[i] != 0) { tmpValue = current_value + mod + npc_value[i]; + int16 FactionModPct = spellbonuses.FactionModPct + itembonuses.FactionModPct + aabonuses.FactionModPct; + tmpValue += (tmpValue * FactionModPct) / 100; + // Make sure faction hits don't go to GMs... if (m_pp.gm==1 && (tmpValue < current_value)) { tmpValue = current_value; diff --git a/zone/common.h b/zone/common.h index 0768a0835..9cb80211a 100644 --- a/zone/common.h +++ b/zone/common.h @@ -373,6 +373,7 @@ struct StatBonuses { bool BerserkSPA; // berserk effect int16 Metabolism; // Food/drink consumption rates. bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others. + int16 FactionModPct; // Modifies amount of faction gained. // AAs int8 Packrat; //weight reduction for items, 1 point = 10% From 542c0913d6bf2560eb3067dcf58243a0865a4e23 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 2 Jul 2014 14:00:50 -0400 Subject: [PATCH 09/21] Re-Implemented SE_TriggerMeleeThreshold and SE_TriggerSpellThreshold correctly - Trigger spell if owner of buff takes more than the specified damage amount in a SINGLE hit, then fade the buff. --- changelog.txt | 2 ++ common/spdat.h | 4 ++-- zone/attack.cpp | 37 +++++-------------------------------- zone/bonuses.cpp | 24 ++++-------------------- zone/mob.cpp | 3 +-- zone/mob.h | 1 + 6 files changed, 15 insertions(+), 56 deletions(-) diff --git a/changelog.txt b/changelog.txt index 26da2b13c..e6d342c55 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) Kayen: Implemented SE_Sanctuary - Places caster at bottom hate list, effect fades if caster cast spell on targets other than self. Kayen: Implemented SE_ResourceTap - Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. Kayen: Implemented SE_FactionModPct - Modifies faction gains and losses by percent. +Kayen: Re-Implemented SE_TriggerMeleeThreshold and SE_TriggerSpellThreshold correctly - Trigger spell if owner of buff +takes more than the specified damage amount in a SINGLE hit, then fade the buff. == 06/25/2014 == Kayen: Updated SE_Hate (Renamed from SE_Hate2) to now properly work for instant +/- hate spells. diff --git a/common/spdat.h b/common/spdat.h index c579a1af7..1b202f19a 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -600,8 +600,8 @@ typedef enum { #define SE_MitigateDotDamage 450 // implemented DOT spell mitigation rune with max value #define SE_MeleeThresholdGuard 451 // implemented Partial Melee Rune that only is lowered if melee hits are over X amount of damage #define SE_SpellThresholdGuard 452 // implemented Partial Spell Rune that only is lowered if spell hits are over X amount of damage -#define SE_TriggerMeleeThreshold 453 // implemented Trigger effect on X amount of melee damage taken -#define SE_TriggerSpellThreshold 454 // implemented Trigger effect on X amount of spell damage taken +#define SE_TriggerMeleeThreshold 453 // implemented Trigger effect on X amount of melee damage taken in a single hit +#define SE_TriggerSpellThreshold 454 // implemented Trigger effect on X amount of spell damage taken in a single hit #define SE_AddHatePct 455 // implemented Modify total hate by % #define SE_AddHateOverTimePct 456 // implemented Modify total hate by % over time. #define SE_ResourceTap 457 // implemented Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. diff --git a/zone/attack.cpp b/zone/attack.cpp index 7b641bd96..fdbc46d24 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3251,20 +3251,6 @@ int32 Mob::ReduceDamage(int32 damage) } } - if (spellbonuses.TriggerMeleeThreshold[2]){ - slot = spellbonuses.TriggerMeleeThreshold[1]; - - if (slot >= 0) { - if(damage > buffs[slot].melee_rune) { - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else{ - buffs[slot].melee_rune = (buffs[slot].melee_rune - damage); - } - } - } - if(damage < 1) return -6; @@ -3393,27 +3379,13 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi } } - if (spellbonuses.TriggerSpellThreshold[2]){ - slot = spellbonuses.TriggerSpellThreshold[1]; - - if (slot >= 0) { - if(damage > buffs[slot].magic_rune) { - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else{ - buffs[slot].magic_rune = (buffs[slot].magic_rune - damage); - } - } - } - if(damage < 1) return 0; if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0) damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); - + if(damage < 1) return 0; } @@ -3599,6 +3571,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons if(spell_id == SPELL_UNKNOWN) { damage = ReduceDamage(damage); mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage); + ReduceAllDamage(damage); + TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker); } else { int32 origdmg = damage; damage = AffectMagicalDamage(damage, spell_id, iBuffTic, attacker); @@ -3610,14 +3584,13 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons //Kayen: Probably need to add a filter for this - Not sure if this msg is correct but there should be a message for spell negate/runes. Message(263, "%s tries to cast on YOU, but YOUR magical skin absorbs the spell.",attacker->GetCleanName()); } - + ReduceAllDamage(damage); + TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker); } if (skill_used) CheckNumHitsRemaining(NUMHIT_IncomingHitSuccess); - ReduceAllDamage(damage); - if(IsClient() && CastToClient()->sneaking){ CastToClient()->sneaking = false; SendAppearancePacket(AT_Sneak, 0); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index e27378207..01e2e4f91 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2425,24 +2425,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } case SE_TriggerMeleeThreshold: - { - if (newbon->TriggerMeleeThreshold[2] < base2){ - newbon->TriggerMeleeThreshold[0] = effect_value; - newbon->TriggerMeleeThreshold[1] = buffslot; - newbon->TriggerMeleeThreshold[2] = base2; - } + newbon->TriggerMeleeThreshold = true; break; - } case SE_TriggerSpellThreshold: - { - if (newbon->TriggerSpellThreshold[2] < base2){ - newbon->TriggerSpellThreshold[0] = effect_value; - newbon->TriggerSpellThreshold[1] = buffslot; - newbon->TriggerSpellThreshold[2] = base2; - } + newbon->TriggerSpellThreshold = true; break; - } case SE_ShieldBlock: newbon->ShieldBlock += effect_value; @@ -4218,15 +4206,11 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_TriggerMeleeThreshold: - spellbonuses.TriggerMeleeThreshold[0] = effect_value; - spellbonuses.TriggerMeleeThreshold[1] = effect_value; - spellbonuses.TriggerMeleeThreshold[2] = effect_value; + spellbonuses.TriggerMeleeThreshold = effect_value; break; case SE_TriggerSpellThreshold: - spellbonuses.TriggerSpellThreshold[0] = effect_value; - spellbonuses.TriggerSpellThreshold[1] = effect_value; - spellbonuses.TriggerSpellThreshold[2] = effect_value; + spellbonuses.TriggerSpellThreshold = effect_value; break; case SE_DivineAura: diff --git a/zone/mob.cpp b/zone/mob.cpp index ee468a0e6..5272b373f 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3452,8 +3452,7 @@ bool Mob::TryFadeEffect(int slot) { for(int i = 0; i < EFFECT_COUNT; i++) { - if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnWearoff || spells[buffs[slot].spellid].effectid[i] == SE_EffectOnFade - || spells[buffs[slot].spellid].effectid[i] == SE_TriggerMeleeThreshold || spells[buffs[slot].spellid].effectid[i] == SE_TriggerSpellThreshold) + if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnWearoff || spells[buffs[slot].spellid].effectid[i] == SE_EffectOnFade) { uint16 spell_id = spells[buffs[slot].spellid].base[i]; BuffFadeBySlot(slot); diff --git a/zone/mob.h b/zone/mob.h index 4f7b47c03..4d6983643 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -230,6 +230,7 @@ public: void SpellProjectileEffect(); bool TrySpellProjectile(Mob* spell_target, uint16 spell_id); void ResourceTap(int32 damage, uint16 spell_id); + void TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker); //Buff void BuffProcess(); From 8453d5bc4889d8505915f14fc3e91bdaf93b572f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 2 Jul 2014 21:38:26 -0400 Subject: [PATCH 10/21] Renamed various Cast on Fade spell effects to more accurately describe their functions. Missing code from prior commit. --- common/spdat.cpp | 8 ++--- zone/common.h | 4 +-- zone/mob.cpp | 2 +- zone/spell_effects.cpp | 74 ++++++++++++++++++++++++++++++------------ 4 files changed, 61 insertions(+), 27 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 3c4989a67..49cb8fe16 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1003,7 +1003,7 @@ bool IsSuspendableSpell(uint16 spell_id) uint32 GetMorphTrigger(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) - if (spells[spell_id].effectid[i] == SE_ImprovedSpellEffect) + if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect) return spells[spell_id].base[i]; return 0; @@ -1012,9 +1012,9 @@ uint32 GetMorphTrigger(uint32 spell_id) bool IsCastonFadeDurationSpell(uint16 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) { - if (spells[spell_id].effectid[i] == SE_ImprovedSpellEffect - || spells[spell_id].effectid[i] == SE_BossSpellTrigger - || spells[spell_id].effectid[i] == SE_CastOnWearoff){ + if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect + || spells[spell_id].effectid[i] == SE_CastOnFadeEffectNPC + || spells[spell_id].effectid[i] == SE_CastOnFadeEffectAlways){ return true; } diff --git a/zone/common.h b/zone/common.h index 9cb80211a..10b3d2200 100644 --- a/zone/common.h +++ b/zone/common.h @@ -348,8 +348,8 @@ struct StatBonuses { uint16 SpellThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. uint16 MitigateSpellRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per spell 3 = Rune Amt uint16 MitigateDotRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per tick 3 = Rune Amt - uint32 TriggerMeleeThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger - uint32 TriggerSpellThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger + bool TriggerMeleeThreshold; // Has Melee Threshhold + bool TriggerSpellThreshold; // Has Spell Threshhold uint16 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot int16 ShieldBlock; // Chance to Shield Block int16 BlockBehind; // Chance to Block Behind (with our without shield) diff --git a/zone/mob.cpp b/zone/mob.cpp index 5272b373f..cf7b2d243 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3452,7 +3452,7 @@ bool Mob::TryFadeEffect(int slot) { for(int i = 0; i < EFFECT_COUNT; i++) { - if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnWearoff || spells[buffs[slot].spellid].effectid[i] == SE_EffectOnFade) + if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways || spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect) { uint16 spell_id = spells[buffs[slot].spellid].base[i]; BuffFadeBySlot(slot); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 498e5d7ec..c30d03432 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1317,18 +1317,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - case SE_TriggerMeleeThreshold: - { - buffs[buffslot].melee_rune = spells[spell_id].base2[i]; - break; - } - - case SE_TriggerSpellThreshold: - { - buffs[buffslot].magic_rune = spells[spell_id].base2[i]; - break; - } - case SE_DistanceRemoval: { buffs[buffslot].caston_x = int(GetX()); @@ -2864,10 +2852,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_FcTwincast: case SE_DelayDeath: case SE_InterruptCasting: - case SE_ImprovedSpellEffect: - case SE_BossSpellTrigger: - case SE_CastOnWearoff: - case SE_EffectOnFade: + case SE_CastOnFadeEffect: + case SE_CastOnFadeEffectNPC: + case SE_CastOnFadeEffectAlways: + case SE_CastOnRuneFadeEffect: case SE_MaxHPChange: case SE_SympatheticProc: case SE_FcDamageAmt: @@ -2982,6 +2970,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_FinishingBlowLvl: case SE_Assassinate: case SE_AssassinateLevel: + case SE_FactionModPct: { break; } @@ -3569,9 +3558,9 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste break; } // These effects always trigger when they fade. - case SE_ImprovedSpellEffect: - case SE_BossSpellTrigger: - case SE_CastOnWearoff: + case SE_CastOnFadeEffect: + case SE_CastOnFadeEffectNPC: + case SE_CastOnFadeEffectAlways: { if (ticsremaining == 1) { @@ -5378,7 +5367,7 @@ void Mob::CheckNumHitsRemaining(uint8 type, uint32 buff_slot, uint16 spell_id) 2: [Outgoing Hit Attempts] (185=SE_DamageModifer, 184=SE_HitChance) 3: [Incoming Spells] (180=SE_ResistSpellChance, 296=SE_FcSpellVulnerability) //Note: Determinetal spells only unless proven otherwise 4: [Outgoing Spells] - 5: [Outgoing Hit Successes] (220=SE_SkillDamageAmount, 178=SE_MeleeLifetap, 121=SE_ReverseDS, ?373=SE_CastOnWearoff) + 5: [Outgoing Hit Successes] (220=SE_SkillDamageAmount, 178=SE_MeleeLifetap, 121=SE_ReverseDS, ?373=SE_CastOnFadeEffectAlways) 6: [Incoming Hit Successes] (59=SE_DamageShield, 197=SE_SkillDamageTaken, 162=define SE_MitigateMeleeDamage) 7: [Matching Spells] *When focus is triggered (focus effects) 8: [Incoming Hits or Spells] (329=SE_ManaAbsorbPercentDamage) @@ -6312,3 +6301,48 @@ void Mob::ResourceTap(int32 damage, uint16 spellid){ } } } + +void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){ + + if (damage <= 0) + return; + + if ((SE_TriggerMeleeThreshold == effect_id) && !spellbonuses.TriggerMeleeThreshold ) + return; + else if ((SE_TriggerSpellThreshold == effect_id) && !spellbonuses.TriggerSpellThreshold) + return; + + int buff_count = GetMaxTotalSlots(); + + for(int slot = 0; slot < buff_count; slot++) { + + if(IsValidSpell(buffs[slot].spellid)){ + + for(int i = 0; i < EFFECT_COUNT; i++){ + + if (spells[buffs[slot].spellid].effectid[i] == effect_id){ + + uint16 spell_id = spells[buffs[slot].spellid].base[i]; + + if (damage > spells[buffs[slot].spellid].base2[i]){ + + BuffFadeBySlot(slot); + + if (IsValidSpell(spell_id)) { + + if (IsBeneficialSpell(spell_id)) + SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff); + + else if(attacker) + SpellFinished(spell_id, attacker, 10, 0, -1, spells[spell_id].ResistDiff); + } + } + } + } + } + } +} + + + + \ No newline at end of file From ee741048e960b9ee13a49d10ffe00e3e9b6852b6 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 3 Jul 2014 08:48:27 -0400 Subject: [PATCH 11/21] Implemented SE_LimitSpellClass - Focus Limits spell to pre defined categories. (3=Cures,3=Offensive, 6=Lifetap) --- common/spdat.cpp | 18 ++++++ common/spdat.h | 15 ++--- zone/mob.h | 2 + zone/spell_effects.cpp | 121 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 139 insertions(+), 17 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 49cb8fe16..75f1e9b88 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -157,6 +157,24 @@ bool IsFearSpell(uint16 spell_id) return IsEffectInSpell(spell_id, SE_Fear); } +bool IsCureSpell(uint16 spell_id) +{ + const SPDat_Spell_Struct &sp = spells[spell_id]; + + bool CureEffect = false; + + for(int i = 0; i < EFFECT_COUNT; i++){ + if (sp.effectid[i] == SE_DiseaseCounter || sp.effectid[i] == SE_PoisonCounter + || sp.effectid[i] == SE_CurseCounter || sp.effectid[i] == SE_CorruptionCounter) + CureEffect = true; + } + + if (CureEffect && IsBeneficialSpell(spell_id)) + return true; + + return false; +} + bool IsSlowSpell(uint16 spell_id) { const SPDat_Spell_Struct &sp = spells[spell_id]; diff --git a/common/spdat.h b/common/spdat.h index 1b202f19a..69b6573b0 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -36,7 +36,7 @@ #define EFFECT_COUNT 12 #define MAX_SPELL_TRIGGER 12 // One for each slot(only 6 for AA since AA use 2) #define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists. -#define MaxLimitInclude 12 //Number(x 0.5) of focus Limiters that have inclusive checksm used when calcing focus effects +#define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects const int Z_AGGRO=10; @@ -436,7 +436,7 @@ typedef enum { #define SE_FcDamageAmt 286 // implemented - adds direct spell damage #define SE_SpellDurationIncByTic 287 // implemented #define SE_SpecialAttackKBProc 288 // implemented[AA] - Chance to to do a knockback from special attacks [AA Dragon Punch]. -#define SE_ImprovedSpellEffect 289 // implemented - Triggers only if fades after natural duration. +#define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration. #define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap #define SE_Purify 291 // implemented - Removes determental effects #define SE_StrikeThrough2 292 // implemented[AA] - increasing chance of bypassing an opponent's special defenses, such as dodge, block, parry, and riposte. @@ -480,7 +480,7 @@ typedef enum { #define SE_CriticalDamageMob 330 // implemented #define SE_Salvage 331 // implemented - chance to recover items that would be destroyed in failed tradeskill combine //#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp) -#define SE_EffectOnFade 333 // implemented +#define SE_CastOnRuneFadeEffect 333 // implemented #define SE_BardAEDot 334 // implemented #define SE_BlockNextSpellFocus 335 // implemented - base1 chance to block next spell ie Puratus (8494) //#define SE_IllusionaryTarget 336 // not used @@ -520,11 +520,11 @@ typedef enum { #define SE_ResistCorruption 370 // implemented #define SE_AttackSpeed4 371 // implemented - stackable slow effect 'Inhibit Melee' //#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not. -#define SE_CastOnWearoff 373 // implemented - Triggers only if fades after natural duration. +#define SE_CastOnFadeEffectAlways 373 // implemented - Triggers if fades after natural duration OR from rune/numhits fades. #define SE_ApplyEffect 374 // implemented #define SE_DotCritDmgIncrease 375 // implemented - Increase damage of DoT critical amount //#define SE_Fling 376 // *not implemented - used in 2 test spells (12945 | Movement Test Spell 1) -#define SE_BossSpellTrigger 377 // implemented - Triggers only if fades after natural duration. +#define SE_CastOnFadeEffectNPC 377 // implemented - Triggers only if fades after natural duration (On live these are usually players spells that effect an NPC). #define SE_SpellEffectResistChance 378 // implemented - Increase chance to resist specific spell effect (base1=value, base2=spell effect id) #define SE_ShadowStepDirectional 379 // implemented - handled by client #define SE_Knockdown 380 // implemented - small knock back(handled by client) @@ -550,8 +550,8 @@ typedef enum { #define SE_HealGroupFromMana 400 // implemented - Drains mana and heals for each point of mana drained #define SE_ManaDrainWithDmg 401 // implemented - Deals damage based on the amount of mana drained #define SE_EndDrainWithDmg 402 // implemented - Deals damage for the amount of endurance drained -//#define SE_LimitSpellClass 403 // *not implemented - unclear what this refers too (not 'right click' spell bar) -//#define SE_LimitSpellSubclass 404 // *not implemented - unclear what this refers too (not 'right click' spell bar) +#define SE_LimitSpellClass 403 // implemented - Limits to specific types of spells (see CheckSpellCategory) +#define SE_LimitSpellSubclass 404 // *not implemented - Limits to specific types of spells (see CheckSpellCategory) [Categories NOT defined yet] #define SE_TwoHandBluntBlock 405 // implemented - chance to block attacks when using two hand blunt weapons (similiar to shield block) #define SE_CastonNumHitFade 406 // implemented - casts a spell when a buff fades due to its numhits being depleted #define SE_CastonFocusEffect 407 // implemented - casts a spell if focus limits are met (ie triggers when a focus effects is applied) @@ -787,6 +787,7 @@ bool IsSummonSpell(uint16 spellid); bool IsEvacSpell(uint16 spellid); bool IsDamageSpell(uint16 spellid); bool IsFearSpell(uint16 spellid); +bool IsCureSpell(uint16 spellid); bool BeneficialSpell(uint16 spell_id); bool GroupOnlySpell(uint16 spell_id); int GetSpellEffectIndex(uint16 spell_id, int effect); diff --git a/zone/mob.h b/zone/mob.h index 4d6983643..0476a20dc 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -231,6 +231,8 @@ public: bool TrySpellProjectile(Mob* spell_target, uint16 spell_id); void ResourceTap(int32 damage, uint16 spell_id); void TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker); + bool CheckSpellCategory(uint16 spell_id, int category_id, int effect_id); + //Buff void BuffProcess(); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index c30d03432..dcc6b6e1b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2971,6 +2971,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_Assassinate: case SE_AssassinateLevel: case SE_FactionModPct: + case SE_LimitSpellClass: { break; } @@ -4128,6 +4129,8 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) 6/7 SE_LimitTarget 8/9 SE_LimitSpellGroup: 10/11 SE_LimitCastingSkill: + 12/13 SE_LimitSpellClass: + 14/15 SE_LimitSpellSubClass: Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes */ int FocusCount = 0; @@ -4322,16 +4325,40 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) break; case SE_LimitCastingSkill: - if(base1 < 0) { - if(-base1 == spell.skill) - LimitFailure = true; - } - else { - LimitInclude[10] = true; - if(base1 == spell.skill) - LimitInclude[11] = true; - } - break; + if(base1 < 0) { + if(-base1 == spell.skill) + LimitFailure = true; + } + else { + LimitInclude[10] = true; + if(base1 == spell.skill) + LimitInclude[11] = true; + } + break; + + case SE_LimitSpellClass: + if(base1 < 0) { //Exclude + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)); + return(0); + } + else { + LimitInclude[12] = true; + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)); //Include + LimitInclude[13] = true; + } + break; + + case SE_LimitSpellSubclass: + if(base1 < 0) { //Exclude + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)); + return(0); + } + else { + LimitInclude[14] = true; + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)); //Include + LimitInclude[15] = true; + } + break; case SE_LimitClass: //Do not use this limit more then once per spell. If multiple class, treat value like items would. @@ -4575,6 +4602,8 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo 6/7 SE_LimitTarget 8/9 SE_LimitSpellGroup: 10/11 SE_LimitCastingSkill: + 12/13 SE_LimitSpellClass: + 14/15 SE_LimitSpellSubClass: Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes */ @@ -4764,6 +4793,30 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo Caston_spell_id = focus_spell.base[i]; break; + case SE_LimitSpellClass: + if(focus_spell.base[i] < 0) { //Exclude + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)); + return(0); + } + else { + LimitInclude[12] = true; + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)); //Include + LimitInclude[13] = true; + } + break; + + case SE_LimitSpellSubclass: + if(focus_spell.base[i] < 0) { //Exclude + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)); + return(0); + } + else { + LimitInclude[14] = true; + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)); //Include + LimitInclude[15] = true; + } + break; + //handle effects case SE_ImprovedDamage: @@ -6343,6 +6396,54 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){ } } +bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){ + if (!IsValidSpell(spell_id) || !category_id) + return false; + int effectid = 0; + int category = 0; + + /*Category ID SE_LimitSpellClass [(+) Include (-) Exclude] + 1 = UNK + 2 = Cures + 3 = Offensive Spells + 4 = UNK + 5 = UNK + 6 = Lifetap + */ + + /*Category ID SE_LimitSpellSubClass [(+) Include (-) Exclude] + 5 = UNK + 8 = UNK + */ + + if (effect_id == SE_LimitSpellClass) { + + switch(category_id) + { + case 2: + if (IsCureSpell(spell_id)) + return true; + break; + + case 3: + if (IsDetrimentalSpell(spell_id)) + return true; + break; + + case 6: + if (spells[spell_id].targettype == ST_Tap || spells[spell_id].targettype == ST_TargetAETap) + return true; + break; + } + } + + else if (effect_id == SE_LimitSpellSubclass) { + //Pending Implementation when category types are figured out. + return false; + } + + return false; +} \ No newline at end of file From 0d2127f874f389fc7144d25385e8c9eeb5a935d0 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 3 Jul 2014 10:55:59 -0400 Subject: [PATCH 12/21] Changed SE_LimitMaxMana to SE_MeleeVulnerability - Weakness/Mitigation verse melee damage (Despite lives SPA lable as the former it clearly is not what the effect does from all spell examples) --- changelog.txt | 3 +++ common/spdat.h | 2 +- zone/bonuses.cpp | 8 ++++++++ zone/common.h | 1 + zone/mob.cpp | 7 ++++++- zone/spell_effects.cpp | 12 +----------- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/changelog.txt b/changelog.txt index e6d342c55..56307b361 100644 --- a/changelog.txt +++ b/changelog.txt @@ -6,6 +6,9 @@ Kayen: Implemented SE_ResourceTap - Coverts a percent of dmg from dmg spells(DD/ Kayen: Implemented SE_FactionModPct - Modifies faction gains and losses by percent. Kayen: Re-Implemented SE_TriggerMeleeThreshold and SE_TriggerSpellThreshold correctly - Trigger spell if owner of buff takes more than the specified damage amount in a SINGLE hit, then fade the buff. +Kayen: Implemented SE_LimitSpellClass - Focus Limits spell to pre defined categories. (3=Cures,3=Offensive, 6=Lifetap) +Kayen: Changed SE_LimitMaxMana to SE_MeleeVulnerability - Weakness/Mitigation verse melee damage +(Despite lives SPA lable as the former it clearly is not what the effect does from all spell examples) == 06/25/2014 == Kayen: Updated SE_Hate (Renamed from SE_Hate2) to now properly work for instant +/- hate spells. diff --git a/common/spdat.h b/common/spdat.h index 69b6573b0..02dca8ff7 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -538,7 +538,7 @@ typedef enum { //#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA) #define SE_FcTimerRefresh 389 // implemented - Refresh spell icons //#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited. -#define SE_LimitManaMax 391 // implemented +#define SE_MeleeVulnerability 391 // implemented [Live SPA has this as LimitManaMax however that is clearly not the effect used] #define SE_FcHealAmt 392 // implemented - Adds or removes healing from spells #define SE_FcHealPctIncoming 393 // implemented - HealRate with focus restrictions. #define SE_FcHealAmtIncoming 394 // implemented - Adds/Removes amount of healing on target by X value with foucs restrictions. diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 01e2e4f91..48a3d95e1 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1315,6 +1315,10 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) newbon->PetMeleeMitigation += base1; break; + case SE_MeleeVulnerability: + newbon->MeleeVulnerability += base1; + break; + case SE_FactionModPct: { if((base1 < 0) && (newbon->FactionModPct > base1)) @@ -2854,6 +2858,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->PetMeleeMitigation += effect_value; break; + case SE_MeleeVulnerability: + newbon->MeleeVulnerability += effect_value; + break; + case SE_Sanctuary: newbon->Sanctuary = true; break; diff --git a/zone/common.h b/zone/common.h index 10b3d2200..e3a9dfb09 100644 --- a/zone/common.h +++ b/zone/common.h @@ -374,6 +374,7 @@ struct StatBonuses { int16 Metabolism; // Food/drink consumption rates. bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others. int16 FactionModPct; // Modifies amount of faction gained. + int16 MeleeVulnerability; // Weakness/mitigation to melee damage // AAs int8 Packrat; //weight reduction for items, 1 point = 10% diff --git a/zone/mob.cpp b/zone/mob.cpp index cf7b2d243..59e02568b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3419,6 +3419,8 @@ int16 Mob::GetSkillDmgTaken(const SkillUseTypes skill_used) { int skilldmg_mod = 0; + int16 MeleeVuln = spellbonuses.MeleeVulnerability + itembonuses.MeleeVulnerability + aabonuses.MeleeVulnerability; + // All skill dmg mod + Skill specific skilldmg_mod += itembonuses.SkillDmgTaken[HIGHEST_SKILL+1] + spellbonuses.SkillDmgTaken[HIGHEST_SKILL+1] + itembonuses.SkillDmgTaken[skill_used] + spellbonuses.SkillDmgTaken[skill_used]; @@ -3427,6 +3429,8 @@ int16 Mob::GetSkillDmgTaken(const SkillUseTypes skill_used) if ((SkillDmgTaken_Mod[skill_used]) || (SkillDmgTaken_Mod[HIGHEST_SKILL+1])) skilldmg_mod += SkillDmgTaken_Mod[skill_used] + SkillDmgTaken_Mod[HIGHEST_SKILL+1]; + skilldmg_mod += MeleeVuln; + if(skilldmg_mod < -100) skilldmg_mod = -100; @@ -3452,7 +3456,8 @@ bool Mob::TryFadeEffect(int slot) { for(int i = 0; i < EFFECT_COUNT; i++) { - if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways || spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect) + if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways || + spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect) { uint16 spell_id = spells[buffs[slot].spellid].base[i]; BuffFadeBySlot(slot); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index dcc6b6e1b..8c49414d7 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2942,7 +2942,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_FcIncreaseNumHits: case SE_CastonFocusEffect: case SE_FcHealAmtIncoming: - case SE_LimitManaMax: + case SE_MeleeVulnerability: case SE_DoubleRangedAttack: case SE_ShieldEquipHateMod: case SE_ShieldEquipDmgMod: @@ -4287,11 +4287,6 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) LimitFailure = true; break; - case SE_LimitManaMax: - if(spell.mana > base1) - LimitFailure = true; - break; - case SE_LimitTarget: if (base1 < 0) { if (-base1 == spell.targettype) //Exclude @@ -4718,11 +4713,6 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo return 0; break; - case SE_LimitManaMax: - if(spell.mana > focus_spell.base[i]) - return 0; - break; - case SE_LimitTarget: if (focus_spell.base[i] < 0) { if (-focus_spell.base[i] == spell.targettype) //Exclude From 28493488acd3c81ed2b8f3b34de6f1f0151e8f33 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 3 Jul 2014 11:00:23 -0400 Subject: [PATCH 13/21] Updated SE_BardAEDot to no longer damage target while target is moving (consistent with live) --- changelog.txt | 1 + zone/spell_effects.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 56307b361..c86b926fa 100644 --- a/changelog.txt +++ b/changelog.txt @@ -9,6 +9,7 @@ takes more than the specified damage amount in a SINGLE hit, then fade the buff. Kayen: Implemented SE_LimitSpellClass - Focus Limits spell to pre defined categories. (3=Cures,3=Offensive, 6=Lifetap) Kayen: Changed SE_LimitMaxMana to SE_MeleeVulnerability - Weakness/Mitigation verse melee damage (Despite lives SPA lable as the former it clearly is not what the effect does from all spell examples) +Kayen: Updated SE_BardAEDot to no longer damage target while target is moving (consistent with live) == 06/25/2014 == Kayen: Updated SE_Hate (Renamed from SE_Hate2) to now properly work for instant +/- hate spells. diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 8c49414d7..dfe8489c0 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2328,6 +2328,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) // SE_CurrentHP is calculated at first tick if its a dot/buff if (buffslot >= 0) break; + //This effect does no damage if target is moving. + if (IsMoving()) + break; // for offensive spells check if we have a spell rune on int32 dmg = effect_value; @@ -3391,7 +3394,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste { effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster); - if (invulnerable || /*effect_value > 0 ||*/ DivineAura()) + if (IsMoving() || invulnerable || /*effect_value > 0 ||*/ DivineAura()) break; if(effect_value < 0) { From 5a30d3ed034e0ea848674190fe32967196a12f38 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 3 Jul 2014 11:37:38 -0400 Subject: [PATCH 14/21] Update SE_InterruptCasting: Will now work for instant spells (as well as over time). --- changelog.txt | 1 + zone/spell_effects.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index c86b926fa..1e371109d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -10,6 +10,7 @@ Kayen: Implemented SE_LimitSpellClass - Focus Limits spell to pre defined catego Kayen: Changed SE_LimitMaxMana to SE_MeleeVulnerability - Weakness/Mitigation verse melee damage (Despite lives SPA lable as the former it clearly is not what the effect does from all spell examples) Kayen: Updated SE_BardAEDot to no longer damage target while target is moving (consistent with live) +Kayen: Update SE_InterruptCasting: Will now work for instant spells (as well as over time). == 06/25/2014 == Kayen: Updated SE_Hate (Renamed from SE_Hate2) to now properly work for instant +/- hate spells. diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index dfe8489c0..61333edfd 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2712,6 +2712,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } + case SE_InterruptCasting:{ + if (buffslot >= 0) + break; + + if(IsCasting() && MakeRandomInt(0, 100) <= spells[spell_id].base[i]) + InterruptSpell(); + + break; + } + case SE_MassGroupBuff:{ SetMGB(true); @@ -2854,7 +2864,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_ApplyEffect: case SE_FcTwincast: case SE_DelayDeath: - case SE_InterruptCasting: case SE_CastOnFadeEffect: case SE_CastOnFadeEffectNPC: case SE_CastOnFadeEffectAlways: From 2734307ba270424f17bd119c6c4a70da0cedc053 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 3 Jul 2014 15:48:17 -0700 Subject: [PATCH 15/21] Added LastInsertID() to MySQLRequestResult --- common/MySQLRequestResult.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/MySQLRequestResult.h b/common/MySQLRequestResult.h index 807efdfc0..ef2dcd104 100644 --- a/common/MySQLRequestResult.h +++ b/common/MySQLRequestResult.h @@ -41,6 +41,7 @@ public: uint32 RowsAffected() const {return m_RowsAffected;} uint32 RowCount() const {return m_RowCount;} uint32 ColumnCount() const {return m_ColumnCount;} + uint32 LastInsertedID() const {return m_LastInsertedID;} MySQLRequestRow& begin() { return m_CurrentRow; } MySQLRequestRow& end() { return m_OneBeyondRow;} From c645b81890fb434a2e8fd2f32a4426edc03be657 Mon Sep 17 00:00:00 2001 From: Chris M Date: Thu, 3 Jul 2014 18:16:43 -0500 Subject: [PATCH 16/21] Removing socket server from cmake for now and maintaining it in its own branch until push to master --- CMakeLists.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55d9a4cfb..a9dd5a96b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -298,13 +298,6 @@ IF(EQEMU_BUILD_LUA) ENDIF(EQEMU_SANITIZE_LUA_LIBS) ENDIF(EQEMU_BUILD_LUA) -IF(EQEMU_BUILD_SOCKET_SERVER) - FIND_PACKAGE(Boost COMPONENTS system filesystem thread REQUIRED) - INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/dependencies/websocketpp") - ADD_DEFINITIONS(-D_WEBSOCKETPP_CPP11_STL_) - -ENDIF(EQEMU_BUILD_SOCKET_SERVER) - INCLUDE_DIRECTORIES("${ZLIB_INCLUDE_DIRS}" "${MySQL_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/common/glm/glm") IF(EQEMU_BUILD_LUA) @@ -320,7 +313,6 @@ IF(EQEMU_BUILD_SERVER) ADD_SUBDIRECTORY(zone) ADD_SUBDIRECTORY(ucs) ADD_SUBDIRECTORY(queryserv) - ADD_SUBDIRECTORY(socket_server) ADD_SUBDIRECTORY(eqlaunch) ENDIF(EQEMU_BUILD_SERVER) IF(EQEMU_BUILD_LOGIN) From e599555ddbacd04c9586ff41766425efc8b6be33 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 4 Jul 2014 00:46:53 -0400 Subject: [PATCH 17/21] Fix case-sensitive issue --- world/socket_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world/socket_server.cpp b/world/socket_server.cpp index 826a95620..3c6631c49 100644 --- a/world/socket_server.cpp +++ b/world/socket_server.cpp @@ -1,5 +1,5 @@ #include "../common/debug.h" -#include "Socket_Server.h" +#include "socket_server.h" #include "WorldConfig.h" #include "clientlist.h" #include "zonelist.h" From 192f9f0cb5ef3cfecb8c3a99d9f02e0c6188ae07 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 3 Jul 2014 21:49:14 -0700 Subject: [PATCH 18/21] Fix for GCC+11 compile --- common/MySQLRequestResult.h | 5 ++++- common/MySQLRequestRow.cpp | 11 ++++++++++- common/MySQLRequestRow.h | 3 ++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/common/MySQLRequestResult.h b/common/MySQLRequestResult.h index ef2dcd104..05f363722 100644 --- a/common/MySQLRequestResult.h +++ b/common/MySQLRequestResult.h @@ -10,6 +10,8 @@ #include <../common/types.h> #include <../common/MySQLRequestRow.h> #include +#include +#include class MySQLRequestResult { private: @@ -52,4 +54,5 @@ private: }; -#endif \ No newline at end of file +#endif + diff --git a/common/MySQLRequestRow.cpp b/common/MySQLRequestRow.cpp index 5fc4a40e8..d00cbc835 100644 --- a/common/MySQLRequestRow.cpp +++ b/common/MySQLRequestRow.cpp @@ -19,6 +19,15 @@ MySQLRequestRow::MySQLRequestRow(MySQLRequestRow&& moveItem) moveItem.m_MySQLRow = nullptr; } +MySQLRequestRow& MySQLRequestRow::operator=(MySQLRequestRow& moveItem) +{ + m_Result = moveItem.m_Result; + m_MySQLRow = moveItem.m_MySQLRow; + + moveItem.m_Result = nullptr; + moveItem.m_MySQLRow = nullptr; +} + MySQLRequestRow::MySQLRequestRow(MYSQL_RES *result) : m_Result(result), m_MySQLRow(mysql_fetch_row(m_Result)) { @@ -51,4 +60,4 @@ char* MySQLRequestRow::operator[](int index) { return m_MySQLRow[index]; -} \ No newline at end of file +} diff --git a/common/MySQLRequestRow.h b/common/MySQLRequestRow.h index 615a75371..8e979acab 100644 --- a/common/MySQLRequestRow.h +++ b/common/MySQLRequestRow.h @@ -23,6 +23,7 @@ public: MySQLRequestRow(MYSQL_RES *result); MySQLRequestRow(const MySQLRequestRow& row); MySQLRequestRow(MySQLRequestRow&& moveItem); + MySQLRequestRow& operator=(MySQLRequestRow& moveItem); MySQLRequestRow& operator++(); MySQLRequestRow operator++(int); bool operator==(const MySQLRequestRow& rhs); @@ -34,4 +35,4 @@ public: -#endif \ No newline at end of file +#endif From eb6248176690c5b728a233e2f63f4329965a637c Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 3 Jul 2014 23:35:19 -0700 Subject: [PATCH 19/21] Missing return value --- common/MySQLRequestRow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/MySQLRequestRow.cpp b/common/MySQLRequestRow.cpp index d00cbc835..46bdaffd9 100644 --- a/common/MySQLRequestRow.cpp +++ b/common/MySQLRequestRow.cpp @@ -26,6 +26,8 @@ MySQLRequestRow& MySQLRequestRow::operator=(MySQLRequestRow& moveItem) moveItem.m_Result = nullptr; moveItem.m_MySQLRow = nullptr; + + return *this; } MySQLRequestRow::MySQLRequestRow(MYSQL_RES *result) From 3b9e9ae0fde8acdcc1c88cb3b98e87eba5956dcd Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 4 Jul 2014 13:57:09 -0400 Subject: [PATCH 20/21] Hopefully fix compile issue with some VS versions --- common/MySQLRequestResult.h | 4 ++-- common/MySQLRequestRow.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/MySQLRequestResult.h b/common/MySQLRequestResult.h index 05f363722..0bf644c98 100644 --- a/common/MySQLRequestResult.h +++ b/common/MySQLRequestResult.h @@ -7,8 +7,8 @@ #endif #include -#include <../common/types.h> -#include <../common/MySQLRequestRow.h> +#include "../common/types.h" +#include "../common/MySQLRequestRow.h" #include #include #include diff --git a/common/MySQLRequestRow.h b/common/MySQLRequestRow.h index 8e979acab..535699fea 100644 --- a/common/MySQLRequestRow.h +++ b/common/MySQLRequestRow.h @@ -8,7 +8,7 @@ #include #include -#include <../common/types.h> +#include "../common/types.h" class MySQLRequestRow : public std::iterator { From 122d180f02676fc26a83752f471b758eb4dd8a64 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 6 Jul 2014 15:22:58 -0700 Subject: [PATCH 21/21] Changed some headers that seemed to conflict on slightly older versions of msvc --- common/MySQLRequestResult.h | 4 ++-- common/MySQLRequestRow.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/MySQLRequestResult.h b/common/MySQLRequestResult.h index 05f363722..3e017557b 100644 --- a/common/MySQLRequestResult.h +++ b/common/MySQLRequestResult.h @@ -7,8 +7,8 @@ #endif #include -#include <../common/types.h> -#include <../common/MySQLRequestRow.h> +#include "types.h" +#include "MySQLRequestRow.h" #include #include #include diff --git a/common/MySQLRequestRow.h b/common/MySQLRequestRow.h index 8e979acab..85fe7cb4f 100644 --- a/common/MySQLRequestRow.h +++ b/common/MySQLRequestRow.h @@ -8,7 +8,7 @@ #include #include -#include <../common/types.h> +#include "types.h" class MySQLRequestRow : public std::iterator {