From 3ec1c894eef86f8a0e6b2304c721dc72e246851a Mon Sep 17 00:00:00 2001 From: ukmeth0d Date: Fri, 7 Mar 2014 01:34:30 +0100 Subject: [PATCH 1/6] 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 3316c3eb3..75f742983 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 2b9580452..7bd337a14 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 21bdc8c5b6bff3ede0f015c1b827c62b8d568d06 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 12 Mar 2014 05:14:19 -0400 Subject: [PATCH 2/6] 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 a291a709b..9ffcddb6d 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 4170434b960e856bb19204cbe444c64a150076c5 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 17 Mar 2014 04:53:47 -0400 Subject: [PATCH 3/6] ** 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 a84862897ac15b863ac88f6a4a789bd106452b09 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 18 Mar 2014 06:00:46 -0400 Subject: [PATCH 4/6] 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 02e291d4e878e81611b32827451f50dfa788a8d7 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 20 Mar 2014 00:53:49 -0400 Subject: [PATCH 5/6] Further refinements to root, charm, mez and fear behaviors. Updates to a few rule due to new/corrected parse data. All behaviors defined from weeks of extensive live parsing Root Break Chance from DD now will scale based on level difference. Root has a baseline aproximately 6% chance to break per check when target has at 0% chance to resist spells.(ie green cons 60 levels lower with tash). Fear has an approximately 70% chance to trigger a resist check each tick to determine if it will fade early. (no baseline break chance) Charisma less than 100, gives -20 resist mod to intial fear casts Charisma from 100 to 255 will progressively reduce this mod to 0. Charisma DOES NOT effect UNDEAD fears Charmisma less than 75 significantly increase CHARM/MEZ/LULL resist rates. Mez spells will now also use charisma resist check, as they do on live. --- changelog.txt | 6 ++ common/ruletypes.h | 7 +- common/spdat.cpp | 2 +- .../git/optional/2014_03_19_RulesUpdates.sql | 6 ++ zone/aggro.cpp | 2 +- zone/attack.cpp | 63 +++++++++-------- zone/mob.h | 4 +- zone/spell_effects.cpp | 20 +++++- zone/spells.cpp | 67 +++++++++++++++---- 9 files changed, 127 insertions(+), 50 deletions(-) create mode 100644 utils/sql/git/optional/2014_03_19_RulesUpdates.sql diff --git a/changelog.txt b/changelog.txt index 08fc5dd27..e2805115e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +Kayen: Further refinements to root, charm, mez and fear behaviors - See commit message for full details + +New rule for 'Fear' break chance, and updates to default settings of various rules. +Optional SQL: utils/sql/git/optional/2014_03_19_RulesUpdates.sql + + == 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. diff --git a/common/ruletypes.h b/common/ruletypes.h index 83db6c3f0..fd50d1514 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -290,17 +290,18 @@ RULE_BOOL ( Spells, NPCIgnoreBaseImmunity, true) // Whether or not NPCs get to i RULE_REAL ( Spells, AvgSpellProcsPerMinute, 6.0) //Adjust rate for sympathetic spell procs RULE_INT ( Spells, ResistFalloff, 67) //Max that level that will adjust our resist chance based on level modifiers RULE_INT ( Spells, CharismaEffectiveness, 10) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. -RULE_INT ( Spells, CharismaEffectivenessCap, 200) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. +RULE_INT ( Spells, CharismaEffectivenessCap, 255) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. RULE_BOOL ( Spells, CharismaCharmDuration, false) // Allow CHA resist mod to extend charm duration. RULE_INT ( Spells, CharmBreakCheckChance, 25) //Determines chance for a charm break check to occur each buff tick. RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time can be reduced by spell haste -RULE_INT ( Spells, RootBreakFromSpells, 20) //Chance for root to break when cast on. +RULE_INT ( Spells, RootBreakFromSpells, 55) //Chance for root to break when cast on. RULE_INT ( Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing. RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount. RULE_BOOL ( Spells, AdditiveBonusValues, false) //Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects) RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this? RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live -RULE_INT ( Spells, RootBreakCheckChance, 40) //Determines chance for a root break check to occur each buff tick. +RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick. +RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/common/spdat.cpp b/common/spdat.cpp index 0f4387b83..c75acc056 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -390,7 +390,7 @@ bool IsPartialCapableSpell(uint16 spell_id) if (spells[spell_id].no_partial_resist) return false; - if (IsPureNukeSpell(spell_id) || IsFearSpell(spell_id)) + if (IsPureNukeSpell(spell_id)) return true; return false; diff --git a/utils/sql/git/optional/2014_03_19_RulesUpdates.sql b/utils/sql/git/optional/2014_03_19_RulesUpdates.sql new file mode 100644 index 000000000..d360528e6 --- /dev/null +++ b/utils/sql/git/optional/2014_03_19_RulesUpdates.sql @@ -0,0 +1,6 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FearBreakCheckChance', '70', 'Chance for fear to do a resist check each tick. Decrease for longer fears.'); + +-- Updates rule value if server is using the OLD DEFAULT values +UPDATE rule_values SET rule_value = 55 WHERE rule_name LIKE 'Spells:RootBreakFromSpells' AND rule_value = 20; +UPDATE rule_values SET rule_value = 70 WHERE rule_name LIKE 'Spells:RootBreakCheckChance' AND rule_value = 40; +UPDATE rule_values SET rule_value = 255 WHERE rule_name LIKE 'Spells:CharismaResistCap' AND rule_value = 200; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 71956a535..144f7363e 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1398,7 +1398,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { /* Charm formula is correct based on over 50 hours of personal live parsing - Kayen - Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA + Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 CHA) Charisma DOES NOT extend charm durations. Base effect value of charm spells in the spell file DOES NOT effect duration OR resist rate (unclear if does anything) Charm has a lower limit of 5% chance to break per tick, regardless of resist modifiers / level difference. diff --git a/zone/attack.cpp b/zone/attack.cpp index ccbc3a8f8..be099adc6 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3680,7 +3680,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons if(spell_id != SPELL_UNKNOWN && !iBuffTic) { //see if root will break if (IsRooted() && !FromDamageShield) // neotoyko: only spells cancel root - TryRootFadeByDamage(buffslot); + TryRootFadeByDamage(buffslot, attacker); } else if(spell_id == SPELL_UNKNOWN) { @@ -4548,34 +4548,43 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, float chance) } } -bool Mob::TryRootFadeByDamage(int buffslot) -{ - /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 - The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. - There is no distinction of any kind between the caster inflicted damage, or anyone - else's damage. There is also no distinction between Direct and DOT damage in the root code. - There is however, a provision that if the damage inflicted is greater than 500 per hit, the - chance to break root is increased. My guess is when this code was put in place, the devs at - the time couldn't imagine DOT damage getting that high. - */ - - /* General Mechanics - - Check buffslot to make sure damage from a root does not cancel the root - - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. - - Only roots on determental spells can be broken by damage. - */ - - if (!spellbonuses.Root[0] || spellbonuses.Root[1] < 0) - return false; - - if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ - - int BreakChance = RuleI(Spells, RootBreakFromSpells); +bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { + + /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 + The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. + There is no distinction of any kind between the caster inflicted damage, or anyone + else's damage. There is also no distinction between Direct and DOT damage in the root code. + + /* General Mechanics + - Check buffslot to make sure damage from a root does not cancel the root + - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. + - Only roots on determental spells can be broken by damage. + - Root break chance values obtained from live parses. + */ + + if (!attacker || !spellbonuses.Root[0] || spellbonuses.Root[1] < 0) + return false; + + if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ + + int BreakChance = RuleI(Spells, RootBreakFromSpells); - BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; + BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; + int level_diff = attacker->GetLevel() - GetLevel(); - if (BreakChance < 1) - BreakChance = 1; + //Use baseline if level difference <= 1 (ie. If target is (1) level less than you, or equal or greater level) + + if (level_diff == 2) + BreakChance = (BreakChance * 80) /100; //Decrease by 20%; + + else if (level_diff >= 3 && level_diff <= 20) + BreakChance = (BreakChance * 60) /100; //Decrease by 40%; + + else if (level_diff > 21) + BreakChance = (BreakChance * 20) /100; //Decrease by 80%; + + if (BreakChance < 1) + BreakChance = 1; if (MakeRandomInt(0, 99) < BreakChance) { diff --git a/zone/mob.h b/zone/mob.h index c771d57b7..c8453d301 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -191,7 +191,7 @@ public: virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration){ return duration;} virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false, - int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false); + int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false); uint16 GetSpecializeSkillValue(uint16 spell_id) const; void SendSpellBarDisable(); void SendSpellBarEnable(uint16 spellid); @@ -593,7 +593,7 @@ public: void MeleeLifeTap(int32 damage); bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true); bool ImprovedTaunt(); - bool TryRootFadeByDamage(int buffslot); + bool TryRootFadeByDamage(int buffslot, Mob* attacker); void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index cebd548dd..7fd2f4332 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -830,14 +830,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Fear: %+i", effect_value); #endif - //use resistance value for duration... - buffs[buffslot].ticsremaining = ((buffs[buffslot].ticsremaining * partial) / 100); - if(IsClient()) { if(buffs[buffslot].ticsremaining > RuleI(Character, MaxFearDurationForPlayerCharacter)) buffs[buffslot].ticsremaining = RuleI(Character, MaxFearDurationForPlayerCharacter); } + if(RuleB(Combat, EnableFearPathing)){ if(IsClient()) @@ -3327,6 +3325,22 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){ + float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0,0,0,true); + + if(resist_check == 100) + break; + else + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + } + + break; + } + + case SE_Fear: + { + if (MakeRandomInt(0, 99) < RuleI(Spells, FearBreakCheckChance)){ + float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster); if(resist_check == 100) diff --git a/zone/spells.cpp b/zone/spells.cpp index 332906883..86d21ea7a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3409,7 +3409,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r // not all unresistable, so changing this to only check certain spells if(IsResistableSpell(spell_id)) { - if (IsCharmSpell(spell_id)) + if (IsCharmSpell(spell_id) || IsMezSpell(spell_id) || IsFearSpell(spell_id)) spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust,true); else spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust); @@ -4040,7 +4040,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) // pvp_resist_base // pvp_resist_calc // pvp_resist_cap -float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick) +float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick, bool IsRoot) { if(!caster) @@ -4079,8 +4079,10 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } //Check for fear resist + bool IsFear = false; if(IsFearSpell(spell_id)) { + IsFear = true; int fear_resist_bonuses = CalcFearResistChance(); if(MakeRandomInt(0, 99) < fear_resist_bonuses) { @@ -4089,7 +4091,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } } - if (!CharismaCheck){ + if (!CharmTick){ //Check for Spell Effect specific resistance chances (ie AA Mental Fortitude) int se_resist_bonuses = GetSpellEffectResistChance(spell_id); @@ -4232,15 +4234,38 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if (CharismaCheck) { - //Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA - //'Lull' spells only check charisma if inital cast is resisted to see if mob will aggro, same modifier/cap as above. - //Charisma DOES NOT extend charm durations. + /* + Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 cha) + Charisma less than ~ 75 gives a postive modifier to resist checks at approximate ratio of -10 CHA = +6 Resist. + Mez spells do same initial resist check as a above. + Lull spells only check charisma if inital cast is resisted to see if mob will aggro, same modifier/cap as above. + Charisma DOES NOT extend charm durations. + Fear resist chance is given a -20 resist modifier if CHA is < 100, from 100-255 it progressively reduces the negative mod to 0. + Fears verse undead DO NOT apply a charisma modifer. (Note: unknown Base1 values defined in undead fears do not effect duration). + */ int16 charisma = caster->GetCHA(); - if (charisma > RuleI(Spells, CharismaEffectivenessCap)) - charisma = RuleI(Spells, CharismaEffectivenessCap); + if (IsFear && (spells[spell_id].targettype != 10)){ - resist_modifier -= charisma/RuleI(Spells, CharismaEffectiveness); + if (charisma < 100) + resist_modifier -= 20; + + else if (charisma <= 255) + resist_modifier += (charisma - 100)/8; + } + + else { + + if (charisma >= 75){ + + if (charisma > RuleI(Spells, CharismaEffectivenessCap)) + charisma = RuleI(Spells, CharismaEffectivenessCap); + + resist_modifier -= (charisma - 75)/RuleI(Spells, CharismaEffectiveness); + } + else + resist_modifier += ((75 - charisma)/10) * 6; //Increase Resist Chance + } } //Lull spells DO NOT use regular resists on initial cast, instead they use a flat +15 modifier. Live parses confirm this. @@ -4266,10 +4291,26 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use resist_chance = spells[spell_id].MinResist; } - //Charm can not have less than 5% chance to fail. - if (CharmTick && (resist_chance < 10)) - resist_chance = 10; - + //Average charm duration agianst mobs with 0% chance to resist on LIVE is ~ 68 ticks. + //Minimum resist chance should be caclulated factoring in the RuleI(Spells, CharmBreakCheckChance) + if (CharmTick) { + + int min_charmbreakchance = ((100/RuleI(Spells, CharmBreakCheckChance))/66 * 100)*2; + + if (resist_chance < min_charmbreakchance) + resist_chance = min_charmbreakchance; + } + + //Average root duration agianst mobs with 0% chance to resist on LIVE is ~ 22 ticks (6% resist chance). + //Minimum resist chance should be caclulated factoring in the RuleI(Spells, RootBreakCheckChance) + if (IsRoot) { + + int min_rootbreakchance = ((100/RuleI(Spells, RootBreakCheckChance))/22 * 100)*2; + + if (resist_chance < min_rootbreakchance) + resist_chance = min_rootbreakchance; + } + //Finally our roll int roll = MakeRandomInt(0, 200); if(roll > resist_chance) From cfdd48b2a3cd05e2a70b4e0cb4a56697ae3a914c Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 22 Mar 2014 05:17:38 -0400 Subject: [PATCH 6/6] Fix for bot guild script failures. MOVED 'load_bots.sql' and 'drop_bots.sql' into this repository. (They are updated) --- changelog.txt | 10 + utils/sql/git/README | 5 +- utils/sql/git/bots/README | 3 + utils/sql/git/bots/drop_bots.sql | 21 ++ utils/sql/git/bots/load_bots.sql | 307 ++++++++++++++++++ ..._03_22_BotGuildMember_ScriptFailureFix.sql | 27 ++ 6 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 utils/sql/git/bots/README create mode 100644 utils/sql/git/bots/drop_bots.sql create mode 100644 utils/sql/git/bots/load_bots.sql create mode 100644 utils/sql/git/bots/required/2014_03_22_BotGuildMember_ScriptFailureFix.sql diff --git a/changelog.txt b/changelog.txt index e2805115e..0403a5c7f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,15 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/22/2014 == +Uleat: Moved the existing 'load_bots' and 'drop_bots' sqls into the emu git repository for the time being. Look to the + /utils/sql/git/bots/ folder to find them. The 'load_bots' sql has been updated to include the below fix, as well as + collecting the multiple files into one. This should allow HeidiSQL to now run it properly. (You will still need to + search for the optional scripts for the time being.) +Uleat: Fixed bot guild script failure. For existing bot databases only, use this sql file to update the affected table and view. + +Required Bot SQL: 2014_03_22_BotGuildMember_ScriptFailureFix.sql + +== 03/19/2014 == Kayen: Further refinements to root, charm, mez and fear behaviors - See commit message for full details New rule for 'Fear' break chance, and updates to default settings of various rules. diff --git a/utils/sql/git/README b/utils/sql/git/README index 12f97658a..9b433f638 100644 --- a/utils/sql/git/README +++ b/utils/sql/git/README @@ -9,4 +9,7 @@ What we'll do instead as follows: All updates will follow a specific format of YYYY_MM_DD_Desc.sql, this is so it's easy to sort. So the following is a good example of what I expect to see -2013_02_16_GitConversion.sql \ No newline at end of file +2013_02_16_GitConversion.sql + + +The new bots/ folder contains two sub-folders named optional/ and required/ for updates. \ No newline at end of file diff --git a/utils/sql/git/bots/README b/utils/sql/git/bots/README new file mode 100644 index 000000000..3f3c94cb1 --- /dev/null +++ b/utils/sql/git/bots/README @@ -0,0 +1,3 @@ +Use this new load_bots.sql to source bot information into your database. + +This file now contains all of the script information so that it may be run from inside of HeidiSQL. \ No newline at end of file diff --git a/utils/sql/git/bots/drop_bots.sql b/utils/sql/git/bots/drop_bots.sql new file mode 100644 index 000000000..d02425d73 --- /dev/null +++ b/utils/sql/git/bots/drop_bots.sql @@ -0,0 +1,21 @@ +DROP TABLE IF EXISTS `botbuffs`; +DROP TABLE IF EXISTS `botpetinventory`; +DROP TABLE IF EXISTS `botpetbuffs`; +DROP TABLE IF EXISTS `botpets`; +DROP TABLE IF EXISTS `botgroupmembers`; +DROP TABLE IF EXISTS `botgroup`; +DROP TABLE IF EXISTS `botgroups`; +DROP TABLE IF EXISTS `botinventory`; +DROP TABLE IF EXISTS `botguildmembers`; +DROP TABLE IF EXISTS `botstances`; +DROP TABLE IF EXISTS `bottimers`; +DROP TABLE IF EXISTS `bots`; +DROP VIEW IF EXISTS `vwGuildMembers`; +DROP VIEW IF EXISTS `vwBotCharacterMobs`; +DROP VIEW IF EXISTS `vwBotGroups`; + +delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1; + +delete from commands where command = 'bot'; + +update spawn2 set enabled = 0 where id in (59297,59298); \ No newline at end of file diff --git a/utils/sql/git/bots/load_bots.sql b/utils/sql/git/bots/load_bots.sql new file mode 100644 index 000000000..d5a358d69 --- /dev/null +++ b/utils/sql/git/bots/load_bots.sql @@ -0,0 +1,307 @@ +-- This is pretty much a straight copy of the original files with fixes applied. +-- HeidiSQL does not like sub-directory references, so this should now run from there. +-- The 'headers' are left in place for reference only. + +-- FILE: +-- source player_tables/botguildmembers.sql; +CREATE TABLE IF NOT EXISTS `botguildmembers` ( + `char_id` int(11) NOT NULL default '0', + `guild_id` mediumint(8) unsigned NOT NULL default '0', + `rank` tinyint(3) unsigned NOT NULL default '0', + `tribute_enable` tinyint(3) unsigned NOT NULL default '0', + `total_tribute` int(10) unsigned NOT NULL default '0', + `last_tribute` int(10) unsigned NOT NULL default '0', + `banker` tinyint(3) unsigned NOT NULL default '0', + `public_note` text NULL, + `alt` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`char_id`) +) ENGINE=InnoDB; + +-- FILE: +-- source player_tables/bots.sql; +update spawn2 set enabled = 1 where id in (59297,59298); + +INSERT INTO rule_values VALUES ('1', 'Bots:BotManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.'); +INSERT INTO rule_values VALUES ('1', 'Bots:BotFinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.'); +INSERT INTO rule_values VALUES ('1', 'Bots:CreateBotCount', '150', 'Number of bots that each account can create'); +INSERT INTO rule_values VALUES ('1', 'Bots:SpawnBotCount', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid'); +INSERT INTO rule_values VALUES ('1', 'Bots:BotQuest', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl'); +INSERT INTO rule_values VALUES ('1', 'Bots:BotGroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.'); +INSERT INTO rule_values VALUES ('1', 'Bots:BotSpellQuest', 'false', 'Anita Thrall\'s (Anita_Thrall.pl) Bot Spell Scriber quests.'); + +-- this is a hack fix to maintain the original file process +delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1; +INSERT INTO rule_values VALUES ('1', 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs'); + +-- field count has changed +-- INSERT INTO `commands` VALUES ('bot', '0', 'Type \"#bot help\" to the see the list of available commands for bots.'); +INSERT INTO `commands` VALUES ('bot', '0'); + +CREATE TABLE bots ( + `BotID` int(10) unsigned NOT NULL AUTO_INCREMENT, + `BotOwnerCharacterID` int(10) unsigned NOT NULL, + `BotSpellsID` int(10) unsigned NOT NULL DEFAULT '0', + `Name` varchar(64) NOT NULL, + `LastName` varchar(32) DEFAULT NULL, + `BotLevel` tinyint(2) unsigned NOT NULL DEFAULT '0', + `Race` smallint(5) NOT NULL DEFAULT '0', + `Class` tinyint(2) NOT NULL DEFAULT '0', + `Gender` tinyint(2) NOT NULL DEFAULT '0', + `Size` float NOT NULL DEFAULT '0', + `Face` int(10) NOT NULL DEFAULT '1', + `LuclinHairStyle` int(10) NOT NULL DEFAULT '1', + `LuclinHairColor` int(10) NOT NULL DEFAULT '1', + `LuclinEyeColor` int(10) NOT NULL DEFAULT '1', + `LuclinEyeColor2` int(10) NOT NULL DEFAULT '1', + `LuclinBeardColor` int(10) NOT NULL DEFAULT '1', + `LuclinBeard` int(10) NOT NULL DEFAULT '0', + `DrakkinHeritage` int(10) NOT NULL DEFAULT '0', + `DrakkinTattoo` int(10) NOT NULL DEFAULT '0', + `DrakkinDetails` int(10) NOT NULL DEFAULT '0', + `HP` INTEGER NOT NULL DEFAULT '0', + `Mana` INTEGER NOT NULL DEFAULT '0', + `MR` smallint(5) NOT NULL DEFAULT '0', + `CR` smallint(5) NOT NULL DEFAULT '0', + `DR` smallint(5) NOT NULL DEFAULT '0', + `FR` smallint(5) NOT NULL DEFAULT '0', + `PR` smallint(5) NOT NULL DEFAULT '0', + `Corrup` SMALLINT(5) NOT NULL DEFAULT '0', + `AC` smallint(5) NOT NULL DEFAULT '0', + `STR` mediumint(8) NOT NULL DEFAULT '75', + `STA` mediumint(8) NOT NULL DEFAULT '75', + `DEX` mediumint(8) NOT NULL DEFAULT '75', + `AGI` mediumint(8) NOT NULL DEFAULT '75', + `_INT` mediumint(8) NOT NULL DEFAULT '80', + `WIS` mediumint(8) NOT NULL DEFAULT '75', + `CHA` mediumint(8) NOT NULL DEFAULT '75', + `ATK` mediumint(9) NOT NULL DEFAULT '0', + `BotCreateDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `LastSpawnDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `TotalPlayTime` int(10) unsigned NOT NULL DEFAULT '0', + `LastZoneId` smallint(6) NOT NULL DEFAULT '0', + `BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT '', + PRIMARY KEY (`BotID`) +) ENGINE=InnoDB; + +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`); +ALTER TABLE `guild_members` DROP PRIMARY KEY; + +DROP VIEW IF EXISTS `vwGuildMembers`; +CREATE VIEW `vwGuildMembers` AS + select 'C' as mobtype, +cm.char_id, +cm.guild_id, +cm.rank, +cm.tribute_enable, +cm.total_tribute, +cm.last_tribute, +cm.banker, +cm.public_note, +cm.alt +from guild_members as cm +union all +select 'B' as mobtype, +bm.char_id, +bm.guild_id, +bm.rank, +bm.tribute_enable, +bm.total_tribute, +bm.last_tribute, +bm.banker, +bm.public_note, +bm.alt +from botguildmembers as bm; + +DROP VIEW IF EXISTS `vwBotCharacterMobs`; +CREATE VIEW `vwBotCharacterMobs` AS + select 'C' as mobtype, +c.id, +c.name, +c.class, +c.level, +c.timelaston, +c.zoneid +from character_ as c +union all +select 'B' as mobtype, +b.BotID as id, +b.Name as name, +b.Class as class, +b.BotLevel as level, +0 as timelaston, +0 as zoneid +from bots as b; + +-- FILE: +-- source player_tables/botpetstatepersists.sql; +DROP TABLE IF EXISTS `botpetinventory`; +DROP TABLE IF EXISTS `botpetbuffs`; +DROP TABLE IF EXISTS `botpets`; + +CREATE TABLE IF NOT EXISTS `botpets` ( + `BotPetsId` integer unsigned NOT NULL AUTO_INCREMENT, + `PetId` integer unsigned NOT NULL DEFAULT '0', + `BotId` integer unsigned NOT NULL DEFAULT '0', + `Name` varchar(64) NULL, + `Mana` integer NOT NULL DEFAULT '0', + `HitPoints` integer NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetsId`), + KEY `FK_botpets_1` (`BotId`), + CONSTRAINT `FK_botpets_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`), + CONSTRAINT `U_botpets_1` UNIQUE (`BotId`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `botpetbuffs` ( + `BotPetBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `BotPetsId` int(10) unsigned NOT NULL DEFAULT '0', + `SpellId` int(10) unsigned NOT NULL DEFAULT '0', + `CasterLevel` int(10) unsigned NOT NULL DEFAULT '0', + `Duration` int(11) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetBuffId`), + KEY `FK_botpetbuffs_1` (`BotPetsId`), + CONSTRAINT `FK_botpetbuffs_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `botpetinventory` ( + `BotPetInventoryId` integer unsigned NOT NULL AUTO_INCREMENT, + `BotPetsId` integer unsigned NOT NULL DEFAULT '0', + `ItemId` integer unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetInventoryId`), + KEY `FK_botpetinventory_1` (`BotPetsId`), + CONSTRAINT `FK_botpetinventory_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +-- FILE: +-- source player_tables/botinventory.sql; +CREATE TABLE IF NOT EXISTS botinventory ( + BotInventoryID integer unsigned NOT NULL auto_increment, + BotID integer unsigned NOT NULL DEFAULT '0', + SlotID integer signed NOT NULL DEFAULT '0', + ItemID integer unsigned NOT NULL DEFAULT '0', + charges tinyint(3) unsigned DEFAULT 0, + color integer unsigned NOT NULL DEFAULT 0, + augslot1 mediumint(7) unsigned NOT NULL DEFAULT 0, + augslot2 mediumint(7) unsigned NOT NULL DEFAULT 0, + augslot3 mediumint(7) unsigned NOT NULL DEFAULT 0, + augslot4 mediumint(7) unsigned NOT NULL DEFAULT 0, + augslot5 mediumint(7) unsigned DEFAULT 0, + instnodrop tinyint(1) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (BotInventoryID), + KEY FK_botinventory_1 (BotID), + CONSTRAINT FK_botinventory_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) +) ENGINE=InnoDB; + +-- FILE: +-- source player_tables/botbuffs.sql; +DROP TABLE IF EXISTS `botbuffs`; +CREATE TABLE `botbuffs` ( + `BotBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `BotId` int(10) unsigned NOT NULL DEFAULT '0', + `SpellId` int(10) unsigned NOT NULL DEFAULT '0', + `CasterLevel` int(10) unsigned NOT NULL DEFAULT '0', + `DurationFormula` int(10) unsigned NOT NULL DEFAULT '0', + `TicsRemaining` int(11) unsigned NOT NULL DEFAULT '0', + `PoisonCounters` int(11) unsigned NOT NULL DEFAULT '0', + `DiseaseCounters` int(11) unsigned NOT NULL DEFAULT '0', + `CurseCounters` int(11) unsigned NOT NULL DEFAULT '0', + `CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `HitCount` int(10) unsigned NOT NULL DEFAULT '0', + `MeleeRune` int(10) unsigned NOT NULL DEFAULT '0', + `MagicRune` int(10) unsigned NOT NULL DEFAULT '0', + `DeathSaveSuccessChance` int(10) unsigned NOT NULL DEFAULT '0', + `CasterAARank` int(10) unsigned NOT NULL DEFAULT '0', + `Persistent` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`BotBuffId`), + KEY `FK_botbuff_1` (`BotId`), + CONSTRAINT `FK_botbuff_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +-- FILE: +-- source player_tables/botadventuring.sql; +DELIMITER $$ + +DROP FUNCTION IF EXISTS `GetMobType` $$ +CREATE FUNCTION `GetMobType` (mobname VARCHAR(64)) RETURNS CHAR(1) +BEGIN + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (select count(*) from character_ where name = mobname) > 0 THEN + SET Result = 'C'; + ELSEIF (select count(*) from bots where Name = mobname) > 0 THEN + SET Result = 'B'; + END IF; + + RETURN Result; +END $$ + +DELIMITER ; + +DROP VIEW IF EXISTS `vwGroups`; +CREATE VIEW `vwGroups` AS + select g.groupid as groupid, +GetMobType(g.name) as mobtype, +g.name as name, +g.charid as mobid, +ifnull(c.level, b.BotLevel) as level +from group_id as g +left join character_ as c on g.name = c.name +left join bots as b on g.name = b.Name; + +-- FILE: +-- source player_tables/botgroups.sql; +DROP TABLE IF EXISTS `botgroupmembers`; +DROP TABLE IF EXISTS `botgroup`; + +CREATE TABLE IF NOT EXISTS `botgroup` ( + `BotGroupId` integer unsigned NOT NULL AUTO_INCREMENT, + `BotGroupLeaderBotId` integer unsigned NOT NULL DEFAULT '0', + `BotGroupName` varchar(64) NOT NULL, + PRIMARY KEY (`BotGroupId`), + KEY FK_botgroup_1 (BotGroupLeaderBotId), + CONSTRAINT FK_botgroup_1 FOREIGN KEY (BotGroupLeaderBotId) REFERENCES bots (BotID) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `botgroupmembers` ( + `BotGroupMemberId` integer unsigned NOT NULL AUTO_INCREMENT, + `BotGroupId` integer unsigned NOT NULL DEFAULT '0', + `BotId` integer unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`BotGroupMemberId`), + KEY FK_botgroupmembers_1 (BotGroupId), + CONSTRAINT FK_botgroupmembers_1 FOREIGN KEY (BotGroupId) REFERENCES botgroup (BotGroupId), + KEY FK_botgroupmembers_2 (BotId), + CONSTRAINT FK_botgroupmembers_2 FOREIGN KEY (BotId) REFERENCES bots (BotID) +) ENGINE=InnoDB; + +DROP VIEW IF EXISTS `vwBotGroups`; +CREATE VIEW `vwBotGroups` AS +select g.BotGroupId, +g.BotGroupName, +g.BotGroupLeaderBotId, +b.Name as BotGroupLeaderName, +b.BotOwnerCharacterId, +c.name as BotOwnerCharacterName +from botgroup as g +join bots as b on g.BotGroupLeaderBotId = b.BotID +join character_ as c on b.BotOwnerCharacterID = c.id +order by b.BotOwnerCharacterId, g.BotGroupName; + +-- FILE: +-- source player_tables/botstances.sql; +CREATE TABLE botstances ( + BotID int(10) unsigned NOT NULL default '0', + StanceID tinyint unsigned NOT NULL default '0', + PRIMARY KEY (BotID), + CONSTRAINT FK_botstances_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) +); + +-- FILE: +-- source player_tables/bottimers.sql; +CREATE TABLE bottimers ( +BotID int(10) unsigned NOT NULL default '0', +TimerID int(10) unsigned NOT NULL default '0', +Value int(10) unsigned NOT NULL default '0', +PRIMARY KEY (BotID), +CONSTRAINT FK_bottimers_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) +) diff --git a/utils/sql/git/bots/required/2014_03_22_BotGuildMember_ScriptFailureFix.sql b/utils/sql/git/bots/required/2014_03_22_BotGuildMember_ScriptFailureFix.sql new file mode 100644 index 000000000..47b735045 --- /dev/null +++ b/utils/sql/git/bots/required/2014_03_22_BotGuildMember_ScriptFailureFix.sql @@ -0,0 +1,27 @@ +ALTER TABLE `botguildmembers` ADD `alt` TINYINT UNSIGNED NOT NULL DEFAULT '0' AFTER `public_note`; + +DROP VIEW IF EXISTS `vwGuildMembers`; +CREATE VIEW `vwGuildMembers` AS + select 'C' as mobtype, +cm.char_id, +cm.guild_id, +cm.rank, +cm.tribute_enable, +cm.total_tribute, +cm.last_tribute, +cm.banker, +cm.public_note, +cm.alt +from guild_members as cm +union all +select 'B' as mobtype, +bm.char_id, +bm.guild_id, +bm.rank, +bm.tribute_enable, +bm.total_tribute, +bm.last_tribute, +bm.banker, +bm.public_note, +bm.alt +from botguildmembers as bm; \ No newline at end of file