From bc6199a86f6cf2f5b51a1783ec2686bb4a6c889e Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 6 Feb 2015 02:49:42 -0500 Subject: [PATCH 01/26] Implemented a better method for developers who want to have additivie worn bonuses than what was prior implemented. Removed old rule RuleB(Spells, AdditiveBonusValues) Replaced with new rule RuleI(AdditiveBonusWornType) The rule value denotes a specific 'worntype' that is to be checked on items. If the items 'worntype' matches the rules worntype then any worn effect on that item will be cacluated additively instead of taking the highest value. This will also stack with regular worn effects that take highest value. Unless the value is set to (2) which is what all live items use. If set to 2 then all worn effects will be calculated additively (same as what the old rule did). In laymans terms. You can take 3 Cleave I items and put them on a character and they will all add together if you set the worn type = 3 and the rule = 3. Which would also add to any regular cleave set to worn type = 2. Hope you enjoyed the novel. --- common/ruletypes.h | 2 +- .../2015_2_6_AdditiveBonusWornType.sql | 4 + zone/bonuses.cpp | 106 +++++++++++++----- zone/bot.cpp | 4 +- zone/client.h | 1 + zone/merc.cpp | 4 +- zone/mob.h | 2 +- zone/mob_ai.cpp | 2 +- 8 files changed, 92 insertions(+), 33 deletions(-) create mode 100644 utils/sql/git/optional/2015_2_6_AdditiveBonusWornType.sql diff --git a/common/ruletypes.h b/common/ruletypes.h index ff88115e2..12a8a0737 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -300,7 +300,7 @@ RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time 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_INT ( Spells, AdditiveBonusWornType, 0) //Calc worn bonuses to add together (instead of taking highest) if set to THIS worn type. (2=Will covert live items automatically) 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, 70) //Determines chance for a root break check to occur each buff tick. diff --git a/utils/sql/git/optional/2015_2_6_AdditiveBonusWornType.sql b/utils/sql/git/optional/2015_2_6_AdditiveBonusWornType.sql new file mode 100644 index 000000000..aab88bd9f --- /dev/null +++ b/utils/sql/git/optional/2015_2_6_AdditiveBonusWornType.sql @@ -0,0 +1,4 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AdditiveBonusWornType', '0', 'Calcs worn bonuses to add together (instead of taking highest) if item set to THIS worn type. Will stack with regular worn bonuses. (2=Will cause all live items to use this behavior)'); + +-- This is no longer used - Set the above value equal to 2 to achieve the same effect. +DELETE FROM `rule_values` WHERE rule_name LIKE "Spells:AdditiveBonusValues"; diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 4aa13519b..ea3174a54 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -169,6 +169,17 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { continue; AddItemBonuses(inst, newbon, false, true); } + + //Optional ability to have worn effects calculate as an addititive bonus instead of highest value + if (RuleI(Spells, AdditiveBonusWornType) && RuleI(Spells, AdditiveBonusWornType) != ET_WornEffect){ + for (i = MainCharm; i < MainAmmo; i++) { + const ItemInst* inst = m_inv[i]; + if(inst == 0) + continue; + AdditiveWornBonuses(inst, newbon); + } + } + // Caps if(newbon->HPRegen > CalcHPRegenCap()) newbon->HPRegen = CalcHPRegenCap(); @@ -410,12 +421,12 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu else newbon->DSMitigation += item->DSMitigation; } - if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true, true); + if (item->Worn.Effect > 0 && item->Worn.Type == ET_WornEffect) {// latent effects + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); } if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects - ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true, false); + ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0); } switch(item->BardType) @@ -537,6 +548,45 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu } +void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug) { + + /* + Powerful Non-live like option allows developers to add worn effects on items that + can stack with other worn effects of the same spell effect type, instead of only taking the highest value. + Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus. + To enable use RuleI(Spells, AdditiveBonusWornType) + Setting value = 2 Will force all live items to automatically be calculated additivily + Setting value to anything else will indicate the item 'worntype' that if set to the same, will cause the bonuses to use this calculation + which will also stack with regular (worntype 2) effects. [Ie set rule = 3 and item worntype = 3] + */ + + if(!inst || !inst->IsType(ItemClassCommon)) + return; + + if(inst->GetAugmentType()==0 && isAug == true) + return; + + const Item_Struct *item = inst->GetItem(); + + if(!inst->IsEquipable(GetBaseRace(),GetClass())) + return; + + if(GetLevel() < item->ReqLevel) + return; + + if (item->Worn.Effect > 0 && item->Worn.Type == RuleI(Spells, AdditiveBonusWornType)) + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);// Non-live like - Addititive latent effects + + + if (!isAug) + { + int i; + for (i = 0; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + AdditiveWornBonuses(inst->GetAugment(i),newbon,true); + } + } +} + void Client::CalcEdibleBonuses(StatBonuses* newbon) { uint32 i; @@ -1393,7 +1443,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid != SPELL_UNKNOWN){ - ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, false, false, buffs[i].ticsremaining,i); + ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, false,0, buffs[i].ticsremaining,i); if (buffs[i].numhits > 0) Numhits(true); @@ -1416,10 +1466,11 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells. } -void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, bool item_bonus, bool IsWornEffect, uint32 ticsremaining, int buffslot, +void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot, bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) { int i, effect_value, base2, max, effectid; + bool AdditiveWornBonus = false; Mob *caster = nullptr; if(!IsAISpellEffect && !IsValidSpell(spell_id)) @@ -1439,15 +1490,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne uint8 focus = IsFocusEffect(spell_id, i); if (focus) { - if (!IsWornEffect) - new_bonus->FocusEffects[focus] = static_cast(spells[spell_id].effectid[i]); + if (WornType){ + if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) + new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base[i]; + } - else if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) - new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base[i]; + else + new_bonus->FocusEffects[focus] = static_cast(spells[spell_id].effectid[i]); continue; } + if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType)) + AdditiveWornBonus = true; effectid = spells[spell_id].effectid[i]; effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining); @@ -1813,7 +1868,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_CriticalHitChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) { + if (AdditiveWornBonus) { if(base2 == -1) new_bonus->CriticalHitChance[HIGHEST_SKILL+1] += effect_value; else @@ -1839,7 +1894,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_CrippBlowChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->CrippBlowChance += effect_value; else if((effect_value < 0) && (new_bonus->CrippBlowChance > effect_value)) @@ -1853,7 +1908,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_AvoidMeleeChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->AvoidMeleeChanceEffect += effect_value; else if((effect_value < 0) && (new_bonus->AvoidMeleeChanceEffect > effect_value)) @@ -1866,7 +1921,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_RiposteChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->RiposteChance += effect_value; else if((effect_value < 0) && (new_bonus->RiposteChance > effect_value)) @@ -1879,7 +1934,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_DodgeChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->DodgeChance += effect_value; else if((effect_value < 0) && (new_bonus->DodgeChance > effect_value)) @@ -1892,7 +1947,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_ParryChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->ParryChance += effect_value; else if((effect_value < 0) && (new_bonus->ParryChance > effect_value)) @@ -1905,7 +1960,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_DualWieldChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->DualWieldChance += effect_value; else if((effect_value < 0) && (new_bonus->DualWieldChance > effect_value)) @@ -1919,7 +1974,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_DoubleAttackChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->DoubleAttackChance += effect_value; else if((effect_value < 0) && (new_bonus->DoubleAttackChance > effect_value)) @@ -1933,7 +1988,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_TripleAttackChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->TripleAttackChance += effect_value; else if((effect_value < 0) && (new_bonus->TripleAttackChance > effect_value)) @@ -1946,7 +2001,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_MeleeLifetap: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->MeleeLifetap += spells[spell_id].base[i]; else if((effect_value < 0) && (new_bonus->MeleeLifetap > effect_value)) @@ -1995,7 +2050,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_HundredHands: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->HundredHands += effect_value; if (effect_value > 0 && effect_value > new_bonus->HundredHands) @@ -2017,7 +2072,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_HitChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus){ + if (AdditiveWornBonus){ if(base2 == -1) new_bonus->HitChanceEffect[HIGHEST_SKILL+1] += effect_value; else @@ -2084,7 +2139,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_ProcChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->ProcChanceSPA += effect_value; else if((effect_value < 0) && (new_bonus->ProcChanceSPA > effect_value)) @@ -2122,7 +2177,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_DivineSave: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) { + if (AdditiveWornBonus) { new_bonus->DivineSaveChance[0] += effect_value; new_bonus->DivineSaveChance[1] = 0; } @@ -2131,7 +2186,6 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne { new_bonus->DivineSaveChance[0] = effect_value; new_bonus->DivineSaveChance[1] = base2; - //SetDeathSaveChance(true); } break; } @@ -3051,12 +3105,12 @@ void NPC::CalcItemBonuses(StatBonuses *newbon) newbon->ProcChance += cur->CombatEffects; } if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon); + ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, cur->Worn.Type); } if (RuleB(Spells, NPC_UseFocusFromItems)){ if (cur->Focus.Effect>0 && (cur->Focus.Type == ET_Focus)){ // focus effects - ApplySpellsBonuses(cur->Focus.Effect, cur->Focus.Level, newbon, 0, true); + ApplySpellsBonuses(cur->Focus.Effect, cur->Focus.Level, newbon); } } diff --git a/zone/bot.cpp b/zone/bot.cpp index c39696576..4b42fac37 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10959,7 +10959,7 @@ void Bot::CalcItemBonuses() } } if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses); + ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type); } } } @@ -11043,7 +11043,7 @@ void Bot::CalcItemBonuses() } } if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses); + ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type); } } } diff --git a/zone/client.h b/zone/client.h index 9248fb742..a2cb823f1 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1257,6 +1257,7 @@ protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); void AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false); + void AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false); int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); void CalcEdibleBonuses(StatBonuses* newbon); void CalcAABonuses(StatBonuses* newbon); diff --git a/zone/merc.cpp b/zone/merc.cpp index 2fea4aba4..059ccb316 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -449,11 +449,11 @@ void Merc::AddItemBonuses(const Item_Struct *item, StatBonuses* newbon) { newbon->DSMitigation += item->DSMitigation; } if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true); + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); } if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects - ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true); + ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0); } switch(item->BardType) diff --git a/zone/mob.h b/zone/mob.h index 46531b819..e7318007e 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -194,7 +194,7 @@ public: bool IsBeneficialAllowed(Mob *target); virtual int GetCasterLevel(uint16 spell_id); void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0, - bool item_bonus = false, bool IsWornEffect = false, uint32 ticsremaining = 0, int buffslot = -1, + uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1, bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0); void NegateSpellsBonuses(uint16 spell_id); virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 371a8e523..452ca71fd 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -2559,7 +2559,7 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon) for(int i=0; i < AIspellsEffects.size(); i++) { - ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1, + ApplySpellsBonuses(0, 0, newbon, 0, 0, 0,-1, true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max); } From 2bf2485b4c66ae9e6a8e7e35bf93fcdac09b3cea Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 6 Feb 2015 07:57:15 -0500 Subject: [PATCH 02/26] Enforced const_interator returns on const_iterator return types in Inventory and ItemInst --- changelog.txt | 3 +++ common/item.cpp | 22 +++++++++++----------- common/item.h | 12 ++++++------ zone/client_packet.cpp | 4 ++-- zone/command.cpp | 2 +- zone/corpse.cpp | 4 ++-- zone/inventory.cpp | 26 +++++++++++++------------- 7 files changed, 38 insertions(+), 35 deletions(-) diff --git a/changelog.txt b/changelog.txt index 9494adfce..8d28ad3de 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/06/2015 == +Uleat: Updated returns for Inventory and ItemInst const iterators. (const == const) + == 02/03/2015 == Trevius: Crashfix for TempName() when numbers are passed at the end of the name. Uleat: Tweaking of item type exclusions to alleviate strobing conditions with light sources diff --git a/common/item.cpp b/common/item.cpp index 82da305f1..4119c7058 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -1060,7 +1060,7 @@ int Inventory::GetSlotByItemInstCollection(const std::map &col } if (t_inst && !t_inst->IsType(ItemClassContainer)) { - for (auto b_iter = t_inst->_begin(); b_iter != t_inst->_end(); ++b_iter) { + for (auto b_iter = t_inst->_cbegin(); b_iter != t_inst->_cend(); ++b_iter) { if (b_iter->second == inst) { return Inventory::CalcSlotId(iter->first, b_iter->first); } @@ -1095,7 +1095,7 @@ void Inventory::dumpBagContents(ItemInst *inst, iter_inst *it) { return; // Go through bag, if bag - for (itb = inst->_begin(); itb != inst->_end(); ++itb) { + for (itb = inst->_cbegin(); itb != inst->_cend(); ++itb) { ItemInst* baginst = itb->second; if (!baginst || !baginst->GetItem()) continue; @@ -1204,7 +1204,7 @@ int16 Inventory::_HasItem(std::map& bucket, uint32 item_id, ui if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1235,7 +1235,7 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) uint8 quantity_found = 0; - for (auto iter = iqueue.begin(); iter != iqueue.end(); ++iter) { + for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { auto inst = *iter; if (inst == nullptr) { continue; } @@ -1252,7 +1252,7 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1289,7 +1289,7 @@ int16 Inventory::_HasItemByUse(std::map& bucket, uint8 use, ui if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1309,7 +1309,7 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) { uint8 quantity_found = 0; - for (auto iter = iqueue.begin(); iter != iqueue.end(); ++iter) { + for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { auto inst = *iter; if (inst == nullptr) { continue; } @@ -1321,7 +1321,7 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1355,7 +1355,7 @@ int16 Inventory::_HasItemByLoreGroup(std::map& bucket, uint32 if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1378,7 +1378,7 @@ int16 Inventory::_HasItemByLoreGroup(std::map& bucket, uint32 // Internal Method: Checks an inventory queue type bucket for a particular item int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) { - for (auto iter = iqueue.begin(); iter != iqueue.end(); ++iter) { + for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { auto inst = *iter; if (inst == nullptr) { continue; } @@ -1395,7 +1395,7 @@ int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } diff --git a/common/item.h b/common/item.h index fd30b5ea0..8554d1c55 100644 --- a/common/item.h +++ b/common/item.h @@ -86,8 +86,8 @@ public: // Public Methods ///////////////////////// - inline std::list::const_iterator begin() { return m_list.begin(); } - inline std::list::const_iterator end() { return m_list.end(); } + inline std::list::const_iterator cbegin() { return m_list.cbegin(); } + inline std::list::const_iterator cend() { return m_list.cend(); } inline int size() { return static_cast(m_list.size()); } // TODO: change to size_t inline bool empty() { return m_list.empty(); } @@ -140,8 +140,8 @@ public: ItemInst* GetItem(int16 slot_id) const; ItemInst* GetItem(int16 slot_id, uint8 bagidx) const; - inline std::list::const_iterator cursor_begin() { return m_cursor.begin(); } - inline std::list::const_iterator cursor_end() { return m_cursor.end(); } + inline std::list::const_iterator cursor_cbegin() { return m_cursor.cbegin(); } + inline std::list::const_iterator cursor_cend() { return m_cursor.cend(); } inline int CursorSize() { return m_cursor.size(); } inline bool CursorEmpty() { return m_cursor.empty(); } @@ -425,8 +425,8 @@ protected: ////////////////////////// // Protected Members ////////////////////////// - iter_contents _begin() { return m_contents.begin(); } - iter_contents _end() { return m_contents.end(); } + iter_contents _cbegin() { return m_contents.cbegin(); } + iter_contents _cend() { return m_contents.cend(); } friend class Inventory; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 331c50435..40dc0e35a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1838,9 +1838,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (loaditems) { /* Dont load if a length error occurs */ BulkSendInventoryItems(); /* Send stuff on the cursor which isnt sent in bulk */ - for (auto iter = m_inv.cursor_begin(); iter != m_inv.cursor_end(); ++iter) { + for (auto iter = m_inv.cursor_cbegin(); iter != m_inv.cursor_cend(); ++iter) { /* First item cursor is sent in bulk inventory packet */ - if (iter == m_inv.cursor_begin()) + if (iter == m_inv.cursor_cbegin()) continue; const ItemInst *inst = *iter; SendItemPacket(MainCursor, inst, ItemPacketSummonItem); diff --git a/zone/command.cpp b/zone/command.cpp index c34bc1b9d..622f1d973 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2618,7 +2618,7 @@ void command_peekinv(Client *c, const Seperator *sep) } else { int cursorDepth = 0; - for (auto it = targetClient->GetInv().cursor_begin(); (it != targetClient->GetInv().cursor_end()); ++it, ++cursorDepth) { + for (auto it = targetClient->GetInv().cursor_cbegin(); (it != targetClient->GetInv().cursor_cend()); ++it, ++cursorDepth) { inst_main = *it; item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 0dd4ed1f3..9f91b584b 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -361,8 +361,8 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( database.QueryDatabase(ss.str().c_str()); } - auto start = client->GetInv().cursor_begin(); - auto finish = client->GetInv().cursor_end(); + auto start = client->GetInv().cursor_cbegin(); + auto finish = client->GetInv().cursor_cend(); database.SaveCursor(client->CharacterID(), start, finish); client->CalcBonuses(); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 1da2fb13d..94138fbe5 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -619,7 +619,7 @@ void Client::DropItem(int16 slot_id) // Save client inventory change to database if (slot_id == MainCursor) { SendCursorBuffer(); - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(CharacterID(), s, e); } else { database.SaveInventory(CharacterID(), nullptr, slot_id); @@ -772,7 +772,7 @@ void Client::DeleteItemInInventory(int16 slot_id, int8 quantity, bool client_upd const ItemInst* inst = nullptr; if (slot_id == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); if(update_db) database.SaveCursor(character_id, s, e); } @@ -826,7 +826,7 @@ bool Client::PushItemOnCursor(const ItemInst& inst, bool client_update) SendItemPacket(MainCursor, &inst, ItemPacketSummonItem); } - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); return database.SaveCursor(CharacterID(), s, e); } @@ -851,7 +851,7 @@ bool Client::PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client } if (slot_id == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); return database.SaveCursor(this->CharacterID(), s, e); } else { @@ -870,7 +870,7 @@ void Client::PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootI SendLootItemInPacket(&inst, slot_id); if (slot_id == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(this->CharacterID(), s, e); } else { @@ -1009,7 +1009,7 @@ void Client::MoveItemCharges(ItemInst &from, int16 to_slot, uint8 type) from.SetCharges(from.GetCharges() - charges_to_move); SendLootItemInPacket(tmp_inst, to_slot); if (to_slot == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(this->CharacterID(), s, e); } else { @@ -1567,7 +1567,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { { SendCursorBuffer(); } - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(character_id, s, e); } else @@ -1726,7 +1726,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { { SendCursorBuffer(); } - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(character_id, s, e); } else { @@ -1734,7 +1734,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } if (dst_slot_id == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(character_id, s, e); } else { @@ -2170,7 +2170,7 @@ void Client::RemoveNoRent(bool client_update) } local.clear(); - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(this->CharacterID(), s, e); } } @@ -2298,7 +2298,7 @@ void Client::RemoveDuplicateLore(bool client_update) } local_2.clear(); - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(this->CharacterID(), s, e); } } @@ -2826,9 +2826,9 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool } int limbo = 0; - for (auto cursor_itr = m_inv.cursor_begin(); cursor_itr != m_inv.cursor_end(); ++cursor_itr, ++limbo) { + for (auto cursor_itr = m_inv.cursor_cbegin(); cursor_itr != m_inv.cursor_cend(); ++cursor_itr, ++limbo) { // m_inv.cursor_begin() is referenced as MainCursor in MapPossessions above - if (cursor_itr == m_inv.cursor_begin()) + if (cursor_itr == m_inv.cursor_cbegin()) continue; instmap[8000 + limbo] = *cursor_itr; From 5d64012d74df20e26ce25f481f9b3ce1055c54e6 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 6 Feb 2015 08:52:41 -0500 Subject: [PATCH 03/26] Removed iter_inst and iter_contents typedefs --- changelog.txt | 1 + common/item.cpp | 27 +++++++++++---------------- common/item.h | 9 +++------ 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/changelog.txt b/changelog.txt index 8d28ad3de..47aa7016f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 02/06/2015 == Uleat: Updated returns for Inventory and ItemInst const iterators. (const == const) +Uleat: Replaced 'iter_inst' and 'iter_contents' typedefs with their stl definitions == 02/03/2015 == Trevius: Crashfix for TempName() when numbers are passed at the end of the name. diff --git a/common/item.cpp b/common/item.cpp index 4119c7058..40fc98474 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -1071,13 +1071,10 @@ int Inventory::GetSlotByItemInstCollection(const std::map &col return -1; } -void Inventory::dumpItemCollection(const std::map &collection) { - iter_inst it; - iter_contents itb; - ItemInst* inst = nullptr; - - for (it = collection.begin(); it != collection.end(); ++it) { - inst = it->second; +void Inventory::dumpItemCollection(const std::map &collection) +{ + for (auto it = collection.cbegin(); it != collection.cend(); ++it) { + auto inst = it->second; if (!inst || !inst->GetItem()) continue; @@ -1088,14 +1085,13 @@ void Inventory::dumpItemCollection(const std::map &collection) } } -void Inventory::dumpBagContents(ItemInst *inst, iter_inst *it) { - iter_contents itb; - +void Inventory::dumpBagContents(ItemInst *inst, std::map::const_iterator *it) +{ if (!inst || !inst->IsType(ItemClassContainer)) return; // Go through bag, if bag - for (itb = inst->_cbegin(); itb != inst->_cend(); ++itb) { + for (auto itb = inst->_cbegin(); itb != inst->_cend(); ++itb) { ItemInst* baginst = itb->second; if (!baginst || !baginst->GetItem()) continue; @@ -1110,7 +1106,7 @@ void Inventory::dumpBagContents(ItemInst *inst, iter_inst *it) { // Internal Method: Retrieves item within an inventory bucket ItemInst* Inventory::_GetItem(const std::map& bucket, int16 slot_id) const { - iter_inst it = bucket.find(slot_id); + auto it = bucket.find(slot_id); if (it != bucket.end()) { return it->second; } @@ -1505,8 +1501,7 @@ ItemInst::ItemInst(const ItemInst& copy) m_attuned=copy.m_attuned; m_merchantcount=copy.m_merchantcount; // Copy container contents - iter_contents it; - for (it=copy.m_contents.begin(); it!=copy.m_contents.end(); ++it) { + for (auto it = copy.m_contents.begin(); it != copy.m_contents.end(); ++it) { ItemInst* inst_old = it->second; ItemInst* inst_new = nullptr; @@ -1676,7 +1671,7 @@ bool ItemInst::IsAugmentSlotAvailable(int32 augtype, uint8 slot) const // Retrieve item inside container ItemInst* ItemInst::GetItem(uint8 index) const { - iter_contents it = m_contents.find(index); + auto it = m_contents.find(index); if (it != m_contents.end()) { return it->second; } @@ -1739,7 +1734,7 @@ void ItemInst::ClearByFlags(byFlagSetting is_nodrop, byFlagSetting is_norent) // TODO: This needs work... // Destroy container contents - iter_contents cur, end, del; + std::map::const_iterator cur, end, del; cur = m_contents.begin(); end = m_contents.end(); for (; cur != end;) { diff --git a/common/item.h b/common/item.h index 8554d1c55..906e00313 100644 --- a/common/item.h +++ b/common/item.h @@ -33,9 +33,6 @@ class EvolveInfo; // Stores information about an evolving item family #include #include -// Helper typedefs -typedef std::map::const_iterator iter_inst; -typedef std::map::const_iterator iter_contents; namespace ItemField { @@ -227,7 +224,7 @@ protected: int GetSlotByItemInstCollection(const std::map &collection, ItemInst *inst); void dumpItemCollection(const std::map &collection); - void dumpBagContents(ItemInst *inst, iter_inst *it); + void dumpBagContents(ItemInst *inst, std::map::const_iterator *it); // Retrieves item within an inventory bucket ItemInst* _GetItem(const std::map& bucket, int16 slot_id) const; @@ -425,8 +422,8 @@ protected: ////////////////////////// // Protected Members ////////////////////////// - iter_contents _cbegin() { return m_contents.cbegin(); } - iter_contents _cend() { return m_contents.cend(); } + std::map::const_iterator _cbegin() { return m_contents.cbegin(); } + std::map::const_iterator _cend() { return m_contents.cend(); } friend class Inventory; From 5a619bddaf2b6e8033df2592c8be1e60b8ab7538 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 6 Feb 2015 09:58:57 -0500 Subject: [PATCH 04/26] Excluded limbo (cursor buffer) from HasItem checks --- changelog.txt | 1 + common/item.cpp | 14 ++++++++ zone/inventory.cpp | 80 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/changelog.txt b/changelog.txt index 47aa7016f..8dd4b77ec 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 02/06/2015 == Uleat: Updated returns for Inventory and ItemInst const iterators. (const == const) Uleat: Replaced 'iter_inst' and 'iter_contents' typedefs with their stl definitions +Uleat: Removed 'limbo' from the 'HasItem' series of checks - including lore checks. The client excludes this range and it causes issues when performing item searches - dupe lore checks were added to account for this. == 02/03/2015 == Trevius: Crashfix for TempName() when numbers are passed at the end of the name. diff --git a/common/item.cpp b/common/item.cpp index 40fc98474..0e2f0f574 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -506,6 +506,7 @@ int16 Inventory::HasItem(uint32 item_id, uint8 quantity, uint8 where) return slot_id; } + // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItem(m_cursor, item_id, quantity); @@ -552,6 +553,7 @@ int16 Inventory::HasItemByUse(uint8 use, uint8 quantity, uint8 where) return slot_id; } + // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByUse(m_cursor, use, quantity); @@ -597,6 +599,7 @@ int16 Inventory::HasItemByLoreGroup(uint32 loregroup, uint8 where) return slot_id; } + // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByLoreGroup(m_cursor, loregroup); @@ -1119,6 +1122,8 @@ ItemInst* Inventory::_GetItem(const std::map& bucket, int16 sl // Assumes item has already been allocated int16 Inventory::_PutItem(int16 slot_id, ItemInst* inst) { + // What happens here when we _PutItem(MainCursor)? Bad things..really bad things... + // // If putting a nullptr into slot, we need to remove slot without memory delete if (inst == nullptr) { //Why do we not delete the poped item here???? @@ -1263,6 +1268,9 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) return legacy::SLOT_AUGMENT; } } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; } return INVALID_INDEX; @@ -1327,6 +1335,9 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) return Inventory::CalcSlotId(MainCursor, bag_iter->first); } } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; } return INVALID_INDEX; @@ -1406,6 +1417,9 @@ int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) return legacy::SLOT_AUGMENT; } } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; } return INVALID_INDEX; diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 94138fbe5..69f1de882 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -177,16 +177,16 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) { } -bool Client::CheckLoreConflict(const Item_Struct* item) { - if (!item) - return false; - if (!(item->LoreFlag)) - return false; +bool Client::CheckLoreConflict(const Item_Struct* item) +{ + if (!item) { return false; } + if (!item->LoreFlag) { return false; } + if (item->LoreGroup == 0) { return false; } - if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result + if (item->LoreGroup == 0xFFFFFFFF) // Standard lore items; look everywhere except the shared bank, return the result return (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != INVALID_INDEX); - //If the item has a lore group, we check for other items with the same group and return the result + // If the item has a lore group, we check for other items with the same group and return the result return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != INVALID_INDEX); } @@ -680,20 +680,37 @@ int32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) { return INVALID_ID; } -void Client::SendCursorBuffer() { +void Client::SendCursorBuffer() +{ // Temporary work-around for the RoF+ Client Buffer // Instead of dealing with client moving items in cursor buffer, // we can just send the next item in the cursor buffer to the cursor. - if (GetClientVersion() >= ClientVersion::RoF) - { - if (!GetInv().CursorEmpty()) - { - const ItemInst* inst = GetInv().GetCursorItem(); - if (inst) - { - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - } - } + if (GetClientVersion() < ClientVersion::RoF) { return; } + if (GetInv().CursorEmpty()) { return; } + + auto test_inst = GetInv().GetCursorItem(); + if (test_inst == nullptr) { return; } + auto test_item = test_inst->GetItem(); + if (test_item == nullptr) { return; } + + bool lore_pass = true; + if (test_item->LoreGroup == 0xFFFFFFFF) { + lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + else if (test_item->LoreGroup != 0) { + lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + + if (!lore_pass) { + Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor", + GetName(), test_item->Name, test_item->ID); + Message_StringID(MT_LootMessages, 290); + parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0); + DeleteItemInInventory(MainCursor); + SendCursorBuffer(); + } + else { + SendItemPacket(MainCursor, test_inst, ItemPacketSummonItem); } } @@ -1320,10 +1337,33 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { return false; } - // This could be expounded upon at some point to let the server know that - // the client has moved a buffered cursor item onto the active cursor -U if (move_in->from_slot == move_in->to_slot) { // Item summon, no further processing needed if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit + if (GetClientVersion() >= ClientVersion::RoF) { return true; } // Can't do RoF+ + + if (move_in->to_slot == MainCursor) { + auto test_inst = m_inv.GetItem(MainCursor); + if (test_inst == nullptr) { return true; } + auto test_item = test_inst->GetItem(); + if (test_item == nullptr) { return true; } + if (!test_item->LoreFlag) { return true; } + + bool lore_pass = true; + if (test_item->LoreGroup == 0xFFFFFFFF) { + lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + else if (test_item->LoreGroup != 0) { + lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + + if (!lore_pass) { + Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor", + GetName(), test_item->Name, test_item->ID); + Message_StringID(MT_LootMessages, 290); + parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0); + DeleteItemInInventory(MainCursor, 0, true); + } + } return true; } From c9cd733d9a1a23ed71ce8c14a6ef3b6a8c25b521 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 6 Feb 2015 12:09:26 -0500 Subject: [PATCH 05/26] Updated command #iteminfo --- changelog.txt | 1 + zone/command.cpp | 66 ++++++++++++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/changelog.txt b/changelog.txt index 8dd4b77ec..f0d5fc722 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) Uleat: Updated returns for Inventory and ItemInst const iterators. (const == const) Uleat: Replaced 'iter_inst' and 'iter_contents' typedefs with their stl definitions Uleat: Removed 'limbo' from the 'HasItem' series of checks - including lore checks. The client excludes this range and it causes issues when performing item searches - dupe lore checks were added to account for this. +Uleat: Updated command #iteminfo to show light source information and a few other things == 02/03/2015 == Trevius: Crashfix for TempName() when numbers are passed at the end of the name. diff --git a/zone/command.cpp b/zone/command.cpp index 622f1d973..96a43d17a 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -4284,31 +4284,49 @@ void command_goto(Client *c, const Seperator *sep) void command_iteminfo(Client *c, const Seperator *sep) { - const ItemInst* inst = c->GetInv()[MainCursor]; - - if (!inst) - c->Message(13, "Error: You need an item on your cursor for this command"); - else { - const Item_Struct* item = inst->GetItem(); - c->Message(0, "ID: %i Name: %s", item->ID, item->Name); - c->Message(0, " Lore: %s ND: %i NS: %i Type: %i", (item->LoreFlag) ? "true":"false", item->NoDrop, item->NoRent, item->ItemClass); - c->Message(0, " IDF: %s Size: %i Weight: %i icon_id: %i Price: %i", item->IDFile, item->Size, item->Weight, item->Icon, item->Price); - if (c->Admin() >= 200) - c->Message(0, "MinStatus: %i", item->MinStatus); - if (item->ItemClass==ItemClassBook) - c->Message(0, " This item is a Book: %s", item->Filename); - else if (item->ItemClass==ItemClassContainer) - c->Message(0, " This item is a container with %i slots", item->BagSlots); - else { - c->Message(0, " equipableSlots: %u equipable Classes: %u", item->Slots, item->Classes); - c->Message(0, " Magic: %i SpellID: %i Proc Level: %i DBCharges: %i CurCharges: %i", item->Magic, item->Click.Effect, item->Click.Level, item->MaxCharges, inst->GetCharges()); - c->Message(0, " EffectType: 0x%02x CastTime: %.2f", (uint8) item->Click.Type, (double) item->CastTime/1000); - c->Message(0, " Material: 0x%02x Color: 0x%08x Skill: %i", item->Material, item->Color, item->ItemType); - c->Message(0, " Required level: %i Required skill: %i Recommended level:%i", item->ReqLevel, item->RecSkill, item->RecLevel); - c->Message(0, " Skill mod: %i percent: %i", item->SkillModType, item->SkillModValue); - c->Message(0, " BaneRace: %i BaneBody: %i BaneDMG: %i", item->BaneDmgRace, item->BaneDmgBody, item->BaneDmgAmt); - } + auto inst = c->GetInv()[MainCursor]; + if (!inst) { c->Message(13, "Error: You need an item on your cursor for this command"); } + auto item = inst->GetItem(); + if (!item) { + Log.Out(Logs::General, Logs::Inventory, "(%s) Command #iteminfo processed an item with no data pointer"); + c->Message(13, "Error: This item has no data reference"); } + + Client::TextLink linker; + linker.SetLinkType(linker.linkItemInst); + linker.SetItemInst(inst); + + auto item_link = linker.GenerateLink(); + + c->Message(0, "*** Item Info for [%s] ***", item_link.c_str()); + c->Message(0, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass); + c->Message(0, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon); + c->Message(0, ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", item->Size, item->Weight, item->Price, item->LDoNPrice); + c->Message(0, ">> Material: 0x%02X, Color: 0x%08X, Tint: 0x%08X, Light: 0x%02X", item->Material, item->Color, inst->GetColor(), item->Light); + c->Message(0, ">> IsLore: %s, LoreGroup: %u, Lore: '%s'", (item->LoreFlag ? "TRUE" : "FALSE"), item->LoreGroup, item->Lore); + c->Message(0, ">> NoDrop: %u, NoRent: %u, NoPet: %u, NoTransfer: %u, FVNoDrop: %u", + item->NoDrop, item->NoRent, (uint8)item->NoPet, (uint8)item->NoTransfer, item->FVNoDrop); + + if (item->ItemClass == ItemClassBook) { + c->Message(0, "*** This item is a Book (filename:'%s') ***", item->Filename); + } + else if (item->ItemClass == ItemClassContainer) { + c->Message(0, "*** This item is a Container (%u slots) ***", item->BagSlots); + } + else { + c->Message(0, "*** This item is Common ***"); + c->Message(0, ">> Classes: %u, Races: %u, Slots: %u", item->Classes, item->Races, item->Slots); + c->Message(0, ">> ReqSkill: %u, ReqLevel: %u, RecLevel: %u", item->RecSkill, item->ReqLevel, item->RecLevel); + c->Message(0, ">> SkillModType: %u, SkillModValue: %i", item->SkillModType, item->SkillModValue); + c->Message(0, ">> BaneRaceType: %u, BaneRaceDamage: %u, BaneBodyType: %u, BaneBodyDamage: %i", + item->BaneDmgRace, item->BaneDmgRaceAmt, item->BaneDmgBody, item->BaneDmgAmt); + c->Message(0, ">> Magic: %s, SpellID: %i, ProcLevel: %u, Charges: %u, MaxCharges: %u", + (item->Magic ? "TRUE" : "FALSE"), item->Click.Effect, item->Click.Level, inst->GetCharges(), item->MaxCharges); + c->Message(0, ">> EffectType: 0x%02X, CastTime: %.2f", (uint8)item->Click.Type, ((double)item->CastTime / 1000)); + } + + if (c->Admin() >= 200) + c->Message(0, ">> MinStatus: %u", item->MinStatus); } void command_uptime(Client *c, const Seperator *sep) From d5047da637e1825cd31079d1e259e30906550ff0 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 6 Feb 2015 19:55:00 -0600 Subject: [PATCH 06/26] Fix an issue that is slowing repops down and unecessarily causing more work for respawn timer code --- zone/zonedb.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 07fe67679..cfde2ff19 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -195,11 +195,9 @@ void ZoneDatabase::UpdateSpawn2Timeleft(uint32 id, uint16 instance_id, uint32 ti //if we pass timeleft as 0 that means we clear from respawn time //otherwise we update with a REPLACE INTO if(timeleft == 0) { - std::string query = StringFormat("DELETE FROM respawn_times WHERE id=%lu " - "AND instance_id = %lu",(unsigned long)id, (unsigned long)instance_id); + std::string query = StringFormat("DELETE FROM respawn_times WHERE id=%lu AND instance_id = %lu",(unsigned long)id, (unsigned long)instance_id); auto results = QueryDatabase(query); - if (!results.Success()) - + return; } From 63810d5c1b8ffdcbeff60e7981df36a708b51b9b Mon Sep 17 00:00:00 2001 From: JohnsonAskot Date: Sat, 7 Feb 2015 12:34:50 -0500 Subject: [PATCH 07/26] Exploit fixes Bind Wound was spammable via packet sending. You could buy a larger stack than the max StackSize of an item from merchants that had unlimited of those stackable items. --- zone/client.cpp | 10 +++++++--- zone/client_packet.cpp | 5 +++++ zone/client_process.cpp | 3 ++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 5cc5ae309..1889ecbf9 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2471,11 +2471,13 @@ void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 bool Client::BindWound(Mob* bindmob, bool start, bool fail){ EQApplicationPacket* outapp = 0; - if(!fail) { + if(!fail) + { outapp = new EQApplicationPacket(OP_Bind_Wound, sizeof(BindWound_Struct)); BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer; // Start bind - if(!bindwound_timer.Enabled()) { + if(!bindwound_timer.Enabled()) + { //make sure we actually have a bandage... and consume it. int16 bslot = m_inv.HasItemByUse(ItemTypeBandage, 1, invWhereWorn|invWherePersonal); if (bslot == INVALID_INDEX) { @@ -2521,7 +2523,9 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){ ; // Binding self } } - } else { + } + else if (bindwound_timer.Check()) // Did the timer finish? No? Then why the hell do they get free hpz?! -Lecht + { // finish bind // disable complete timer bindwound_timer.Disable(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 8ade0bc55..abc5734bd 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12145,6 +12145,10 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) mp->quantity = prevcharges; } + // Item's stackable, but the quantity they want to buy exceeds the max stackable quantity. -Lecht + if (item->Stackable && mp->quantity > item->StackSize) + mp->quantity = item->StackSize; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); Merchant_Sell_Struct* mpo = (Merchant_Sell_Struct*)outapp->pBuffer; mpo->quantity = mp->quantity; @@ -12171,6 +12175,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) mpo->price = SinglePrice; else mpo->price = SinglePrice * mp->quantity; + if (mpo->price < 0) { safe_delete(outapp); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index bddc874c2..4c1475c95 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -239,7 +239,8 @@ bool Client::Process() { if(IsAIControlled()) AI_Process(); - if (bindwound_timer.Check() && bindwound_target != 0) { + // Don't reset the bindwound timer so we can check it in BindWound as well. -Lecht + if (bindwound_timer.Check(false) && bindwound_target != 0) { BindWound(bindwound_target, false); } From 67ee327f5b2ffbff7accebff0473ed0c2fdb4392 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 7 Feb 2015 12:39:46 -0500 Subject: [PATCH 08/26] Better sanity checking in Client::BuyTraderItem to prevent potential exploits --- zone/trading.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/zone/trading.cpp b/zone/trading.cpp index 4d30691f5..4874716ae 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1479,8 +1479,6 @@ static void BazaarAuditTrail(const char *seller, const char *buyer, const char * database.QueryDatabase(query); } - - void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicationPacket* app){ if(!Trader) return; @@ -1509,15 +1507,15 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat BuyItem->GetItem()->Name, BuyItem->IsStackable(), tbs->Quantity, BuyItem->GetCharges()); // If the item is not stackable, then we can only be buying one of them. if(!BuyItem->IsStackable()) - outtbs->Quantity = tbs->Quantity; + outtbs->Quantity = 1; // normally you can't send more than 1 here else { // Stackable items, arrows, diamonds, etc - int ItemCharges = BuyItem->GetCharges(); + int32 ItemCharges = BuyItem->GetCharges(); // ItemCharges for stackables should not be <= 0 if(ItemCharges <= 0) outtbs->Quantity = 1; // If the purchaser requested more than is in the stack, just sell them how many are actually in the stack. - else if(ItemCharges < (int16)tbs->Quantity) + else if(static_cast(ItemCharges) < tbs->Quantity) outtbs->Quantity = ItemCharges; else outtbs->Quantity = tbs->Quantity; @@ -1609,7 +1607,6 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat safe_delete(outapp); safe_delete(outapp2); - } void Client::SendBazaarWelcome() From 7bf054bd58e8e99fb757c49502c4d752be578cea Mon Sep 17 00:00:00 2001 From: JohnsonAskot Date: Sat, 7 Feb 2015 14:35:43 -0500 Subject: [PATCH 09/26] Name removed from comments --- zone/client.cpp | 2 +- zone/client_packet.cpp | 2 +- zone/client_process.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 1889ecbf9..80942527a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2524,7 +2524,7 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){ } } } - else if (bindwound_timer.Check()) // Did the timer finish? No? Then why the hell do they get free hpz?! -Lecht + else if (bindwound_timer.Check()) // Did the timer finish? { // finish bind // disable complete timer diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index abc5734bd..55a6e9fcd 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12145,7 +12145,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) mp->quantity = prevcharges; } - // Item's stackable, but the quantity they want to buy exceeds the max stackable quantity. -Lecht + // Item's stackable, but the quantity they want to buy exceeds the max stackable quantity. if (item->Stackable && mp->quantity > item->StackSize) mp->quantity = item->StackSize; diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 4c1475c95..ef21013e8 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -239,7 +239,7 @@ bool Client::Process() { if(IsAIControlled()) AI_Process(); - // Don't reset the bindwound timer so we can check it in BindWound as well. -Lecht + // Don't reset the bindwound timer so we can check it in BindWound as well. if (bindwound_timer.Check(false) && bindwound_target != 0) { BindWound(bindwound_target, false); } From edbd0552773343fd2dc47292874e946d5133d3c8 Mon Sep 17 00:00:00 2001 From: JJ Date: Sat, 7 Feb 2015 17:27:48 -0500 Subject: [PATCH 10/26] Revert b96e5a7f4d0087d98e186c45ee05b5aa5bf4dba0 to search for better fix. --- zone/groups.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/groups.h b/zone/groups.h index 71935bf81..7dcbd848b 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -73,7 +73,7 @@ public: void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr); inline void SetLeader(Mob* newleader){ leader=newleader; }; inline Mob* GetLeader() { return leader; }; - const char* GetLeaderName() { return leader->GetName(); }; + const char* GetLeaderName() { return membername[0]; }; void SendHPPacketsTo(Mob* newmember); void SendHPPacketsFrom(Mob* newmember); bool UpdatePlayer(Mob* update); From 452b1a1eae3aa627080a4f421f4f8469ae37f398 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 7 Feb 2015 15:54:42 -0800 Subject: [PATCH 11/26] Added throttling to some appearance packets, also removed responding to client light packets. With the new light code we'll tell the client when the light has changed not the other way around. --- zone/client.cpp | 5 +++++ zone/client.h | 5 +++++ zone/client_packet.cpp | 36 +++++++++++++++++++++--------------- zone/client_process.cpp | 7 +++++++ 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 5cc5ae309..6adffd471 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -141,6 +141,11 @@ Client::Client(EQStreamInterface* ieqs) merc_timer(RuleI(Mercs, UpkeepIntervalMS)), ItemTickTimer(10000), ItemQuestTimer(500), + anim_change_timer(100), + anon_toggle_timer(250), + afk_toggle_timer(250), + helm_toggle_timer(250), + light_update_timer(250), m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f), m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), diff --git a/zone/client.h b/zone/client.h index a2cb823f1..3d5bc8e4b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1465,6 +1465,11 @@ private: Timer TrackingTimer; Timer RespawnFromHoverTimer; Timer merc_timer; + Timer anim_change_timer; + Timer anon_toggle_timer; + Timer afk_toggle_timer; + Timer helm_toggle_timer; + Timer light_update_timer; glm::vec3 m_Proximity; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 8ade0bc55..24a811016 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12640,6 +12640,10 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) else if (sa->type == AT_Anim) { if (IsAIControlled()) return; + + if(!anim_change_timer.Check()) + return; + if (sa->parameter == ANIM_STAND) { SetAppearance(eaStanding); playeraction = 0; @@ -12673,15 +12677,6 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) SetFeigned(false); } - // This is from old code - // I have no clue what it's for - /* - else if (sa->parameter == 0x05) { - // Illusion - std::cout << "Illusion packet recv'd:" << std::endl; - DumpPacket(app); - } - */ else { std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl; return; @@ -12690,6 +12685,10 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) entity_list.QueueClients(this, app, true); } else if (sa->type == AT_Anon) { + if(!anon_toggle_timer.Check()) { + return; + } + // For Anon/Roleplay if (sa->parameter == 1) { // Anon m_pp.anon = 1; @@ -12711,13 +12710,18 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) return; } else if (sa->type == AT_AFK) { - this->AFK = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); + if(afk_toggle_timer.Check()) { + AFK = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } } else if (sa->type == AT_Split) { m_pp.autosplit = (sa->parameter == 1); } else if (sa->type == AT_Sneak) { + if(sneaking == 0) + return; + if (sa->parameter != 0) { if (!HasSkill(SkillSneak)) @@ -12729,7 +12733,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) } return; } - this->sneaking = 0; + sneaking = 0; entity_list.QueueClients(this, app, true); } else if (sa->type == AT_Size) @@ -12741,7 +12745,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) } else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield) { - entity_list.QueueClients(this, app, false); + //don't do anything with this } else if (sa->type == AT_Levitate) { @@ -12750,8 +12754,10 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) } else if (sa->type == AT_ShowHelm) { - m_pp.showhelm = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); + if(helm_toggle_timer.Check()) { + m_pp.showhelm = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } } else { std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec diff --git a/zone/client_process.cpp b/zone/client_process.cpp index bddc874c2..e978552e8 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -260,6 +260,13 @@ bool Client::Process() { } } + if(light_update_timer.Check()) { + UpdateEquipLightValue(); + if(UpdateActiveLightValue()) { + SendAppearancePacket(AT_Light, GetActiveLightValue()); + } + } + bool may_use_attacks = false; /* Things which prevent us from attacking: From 9a15361e9345134c490ee954b5535872863eb582 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 7 Feb 2015 20:07:46 -0500 Subject: [PATCH 12/26] Should fix spell bonuses not being applied properly --- zone/bonuses.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index ea3174a54..9ff05fb53 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1443,7 +1443,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid != SPELL_UNKNOWN){ - ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, false,0, buffs[i].ticsremaining,i); + ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining,i); if (buffs[i].numhits > 0) Numhits(true); From 3392f4b1c3ec07f63eef7770376ad6025c2d3286 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 7 Feb 2015 20:16:27 -0500 Subject: [PATCH 13/26] bonus fix --- zone/bonuses.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 9ff05fb53..8e99f9b23 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -3105,7 +3105,7 @@ void NPC::CalcItemBonuses(StatBonuses *newbon) newbon->ProcChance += cur->CombatEffects; } if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, cur->Worn.Type); + ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, 0, cur->Worn.Type); } if (RuleB(Spells, NPC_UseFocusFromItems)){ From cb81d956f62b75cf6cfbb7e5e9e328a24ea52d65 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 8 Feb 2015 05:01:58 -0600 Subject: [PATCH 14/26] Reduced #repop time dramatically by taking down hundreds of individual SELECT/DELETE/INSERT queries in routines and bringing it down to very few See: https://www.youtube.com/watch?v=9kSFbyTBuAk --- changelog.txt | 4 ++ zone/questmgr.cpp | 4 +- zone/spawn2.cpp | 105 ++++++++++++++++++++++++++++++++++++---------- zone/zone.cpp | 3 ++ zone/zonedb.cpp | 42 ++++++++++++------- zone/zonedb.h | 2 +- 6 files changed, 119 insertions(+), 41 deletions(-) diff --git a/changelog.txt b/changelog.txt index 65941f885..38b9d5719 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/06/2015 == +Akkadius: Reduced #repop time dramatically by taking down hundreds of individual SELECT/DELETE/INSERT queries in routines and bringing it down to very few + See: https://www.youtube.com/watch?v=9kSFbyTBuAk + == 02/06/2015 == Uleat: Updated returns for Inventory and ItemInst const iterators. (const == const) Uleat: Replaced 'iter_inst' and 'iter_contents' typedefs with their stl definitions diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 6c181bf1e..7dcb4e457 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -297,7 +297,7 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id) } } - database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0); + database.UpdateRespawnTime(spawn2_id, zone->GetInstanceID(), 0); found_spawn->SetCurrentNPCID(npcid); auto position = glm::vec4(found_spawn->GetX(), found_spawn->GetY(), found_spawn->GetZ(), found_spawn->GetHeading()); @@ -2388,7 +2388,7 @@ void QuestManager::UpdateSpawnTimer(uint32 id, uint32 newTime) { bool found = false; - database.UpdateSpawn2Timeleft(id, 0, (newTime/1000)); + database.UpdateRespawnTime(id, 0, (newTime/1000)); LinkedListIterator iterator(zone->spawn2_list); iterator.Reset(); while (iterator.MoreElements()) diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index f3440f127..068774091 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -214,9 +214,6 @@ bool Spawn2::Process() { if(IsDespawned) return true; - if(spawn2_id) - database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0); - currentnpcid = npcid; NPC* npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), FlyMode3); @@ -348,7 +345,7 @@ void Spawn2::DeathReset(bool realdeath) //if we have a valid spawn id if(spawn2_id) { - database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), (cur/1000)); + database.UpdateRespawnTime(spawn2_id, zone->GetInstanceID(), (cur/1000)); Log.Out(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn reset by death, repop in %d ms", spawn2_id, timer.GetRemainingTime()); //store it to database too } @@ -356,28 +353,92 @@ void Spawn2::DeathReset(bool realdeath) bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList &spawn2_list, int16 version, uint32 repopdelay) { + std::unordered_map spawn_times; + + timeval tv; + gettimeofday(&tv, nullptr); + + std::string spawn_query = StringFormat( + "SELECT " + "respawn_times.id, " + "respawn_times.`start`, " + "respawn_times.duration " + "FROM " + "respawn_times " + "WHERE instance_id = %u", + zone->GetInstanceID() + ); + auto results = QueryDatabase(spawn_query); + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 start_duration = atoi(row[1]) > 0 ? atoi(row[1]) : 0; + uint32 end_duration = atoi(row[2]) > 0 ? atoi(row[2]) : 0; + + /* Our current time was expired */ + if ((start_duration + end_duration) <= tv.tv_sec) { + spawn_times[atoi(row[0])] = 0; + } + /* We still have time left on this timer */ + else { + spawn_times[atoi(row[0])] = ((start_duration + end_duration) - tv.tv_sec) * 1000; + } + } + const char *zone_name = database.GetZoneName(zoneid); - std::string query = StringFormat("SELECT id, spawngroupID, x, y, z, heading, " - "respawntime, variance, pathgrid, _condition, " - "cond_value, enabled, animation FROM spawn2 " - "WHERE zone = '%s' AND version = %u", - zone_name, version); - auto results = QueryDatabase(query); - if (!results.Success()) { + std::string query = StringFormat( + "SELECT " + "id, " + "spawngroupID, " + "x, " + "y, " + "z, " + "heading, " + "respawntime, " + "variance, " + "pathgrid, " + "_condition, " + "cond_value, " + "enabled, " + "animation " + "FROM " + "spawn2 " + "WHERE zone = '%s' AND version = %u", + zone_name, + version + ); + results = QueryDatabase(query); + + if (!results.Success()) { return false; - } + } - for (auto row = results.begin(); row != results.end(); ++row) { - Spawn2* newSpawn = 0; + for (auto row = results.begin(); row != results.end(); ++row) { - bool perl_enabled = atoi(row[11]) == 1? true: false; - uint32 spawnLeft = (GetSpawnTimeLeft(atoi(row[0]), zone->GetInstanceID()) * 1000); - newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]), - atof(row[5]), atoi(row[6]), atoi(row[7]), spawnLeft, atoi(row[8]), - atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12])); + uint32 spawn_time_left = 0; + Spawn2* new_spawn = 0; + bool perl_enabled = atoi(row[11]) == 1 ? true : false; - spawn2_list.Insert(newSpawn); - } + if (spawn_times.count(atoi(row[0])) != 0) + spawn_time_left = spawn_times[atoi(row[0])]; + + new_spawn = new Spawn2( // + atoi(row[0]), // uint32 in_spawn2_id + atoi(row[1]), // uint32 spawngroup_id + atof(row[2]), // float in_x + atof(row[3]), // float in_y + atof(row[4]), // float in_z + atof(row[5]), // float in_heading + atoi(row[6]), // uint32 respawn + atoi(row[7]), // uint32 variance + spawn_time_left, // uint32 timeleft + atoi(row[8]), // uint32 grid + atoi(row[9]), // uint16 in_cond_id + atoi(row[10]), // int16 in_min_value + perl_enabled, // bool in_enabled + (EmuAppearance)atoi(row[12]) // EmuAppearance anim + ); + + spawn2_list.Insert(new_spawn); + } return true; } @@ -427,8 +488,6 @@ bool ZoneDatabase::CreateSpawn2(Client *client, uint32 spawngroup, const char* z if (results.RowsAffected() != 1) return false; - if(client) - return true; } diff --git a/zone/zone.cpp b/zone/zone.cpp index 444e217da..e79a9f9af 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -931,6 +931,9 @@ bool Zone::Init(bool iStaticZone) { Log.Out(Logs::General, Logs::Error, "Loading World Objects failed. continuing."); } + Log.Out(Logs::General, Logs::Status, "Flushing old respawn timers..."); + database.QueryDatabase("DELETE FROM `respawn_times` WHERE (`start` + `duration`) < UNIX_TIMESTAMP(NOW())"); + //load up the zone's doors (prints inside) zone->LoadZoneDoors(zone->GetShortName(), zone->GetInstanceVersion()); zone->LoadBlockedSpells(zone->GetZoneID()); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index cfde2ff19..165282f3d 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -185,28 +185,40 @@ bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct return true; } -//updates or clears the respawn time in the database for the current spawn id -void ZoneDatabase::UpdateSpawn2Timeleft(uint32 id, uint16 instance_id, uint32 timeleft) +void ZoneDatabase::UpdateRespawnTime(uint32 spawn2_id, uint16 instance_id, uint32 time_left) { + timeval tv; gettimeofday(&tv, nullptr); - uint32 cur = tv.tv_sec; + uint32 current_time = tv.tv_sec; - //if we pass timeleft as 0 that means we clear from respawn time - //otherwise we update with a REPLACE INTO - if(timeleft == 0) { - std::string query = StringFormat("DELETE FROM respawn_times WHERE id=%lu AND instance_id = %lu",(unsigned long)id, (unsigned long)instance_id); - auto results = QueryDatabase(query); - + /* If we pass timeleft as 0 that means we clear from respawn time + otherwise we update with a REPLACE INTO + */ + + if(time_left == 0) { + std::string query = StringFormat("DELETE FROM `respawn_times` WHERE `id` = %u AND `instance_id` = %u", spawn2_id, instance_id); + QueryDatabase(query); return; } - std::string query = StringFormat("REPLACE INTO respawn_times (id, start, duration, instance_id) " - "VALUES (%lu, %lu, %lu, %lu)", - (unsigned long)id, (unsigned long)cur, - (unsigned long)timeleft, (unsigned long)instance_id); - auto results = QueryDatabase(query); - if (!results.Success()) + std::string query = StringFormat( + "REPLACE INTO `respawn_times` " + "(id, " + "start, " + "duration, " + "instance_id) " + "VALUES " + "(%u, " + "%u, " + "%u, " + "%u)", + spawn2_id, + current_time, + time_left, + instance_id + ); + QueryDatabase(query); return; } diff --git a/zone/zonedb.h b/zone/zonedb.h index 5e1a55248..70c732878 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -365,7 +365,7 @@ public: bool PopulateZoneSpawnList(uint32 zoneid, LinkedList &spawn2_list, int16 version, uint32 repopdelay = 0); Spawn2* LoadSpawn2(LinkedList &spawn2_list, uint32 spawn2id, uint32 timeleft); bool CreateSpawn2(Client *c, uint32 spawngroup, const char* zone, const glm::vec4& position, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value); - void UpdateSpawn2Timeleft(uint32 id, uint16 instance_id,uint32 timeleft); + void UpdateRespawnTime(uint32 id, uint16 instance_id,uint32 timeleft); uint32 GetSpawnTimeLeft(uint32 id, uint16 instance_id); void UpdateSpawn2Status(uint32 id, uint8 new_status); From 8b925f549b902dedfe9f4083b734af218129030a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 8 Feb 2015 06:19:14 -0600 Subject: [PATCH 15/26] derp [skip ci] --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 38b9d5719..0204c266b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,6 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- -== 02/06/2015 == +== 02/07/2015 == Akkadius: Reduced #repop time dramatically by taking down hundreds of individual SELECT/DELETE/INSERT queries in routines and bringing it down to very few See: https://www.youtube.com/watch?v=9kSFbyTBuAk From 0521cae8d0c6f13ba18e1e9d84fdaf4833518c0c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 8 Feb 2015 20:17:51 -0500 Subject: [PATCH 16/26] Implemented npc specialability (44) COUNTER_AVOID_DAMAGE which when applied to the ATTACKING NPC will make their attacks more difficult to be avoided by riposte/dodge/parry/block. Parama0: Negative modifer value that affects ALL avoid damage types dodge/parry/riposte/block) chance on defender. Ie (44,50 = 50 pct reduction to ALL) Parama1: Negative modifer value that affects RIPOSTE chance on defender. Ie (44,1,0,50 = 50 pct reduction to riposte chance) Parama2: Negative modifer value that affects PARRY chance on defender. Ie (44,1,0,0,50 = 50 pct reduction to parry chance) Parama3: Negative modifer value that affects BLOCK chance on defender. Ie (44,1,0,0,0,50 = 50 pct reduction to block chance) Parama4: Negative modifer value that affects DODGE chance on defender. e (44,1,0,0,0,0,50 = 50 pct reduction to dodge chance) Example of usage: Player has Improved Dodge V (+50 pct dodge chance), you want to negate this bonus you would set 44,1,0,0,0,0,50 on your NPC. Clean up and minor fixes to AvoidDamage function. Added support to a few AA bonuses there. --- changelog.txt | 9 +++++ zone/attack.cpp | 99 ++++++++++++++++++++++++++---------------------- zone/bonuses.cpp | 12 ++++-- zone/common.h | 3 +- zone/mob.cpp | 1 + zone/mob.h | 5 ++- 6 files changed, 78 insertions(+), 51 deletions(-) diff --git a/changelog.txt b/changelog.txt index 0204c266b..40a933c56 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,14 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/08/2015 == +Kayen: Implemented npc specialability (44) COUNTER_AVOID_DAMAGE which when applied to the ATTACKING NPC will make their attacks more difficult to be avoided by riposte/dodge/parry/block. +Parama0: Negative modifer value that affects ALL avoid damage types dodge/parry/riposte/block) chance on defender. Ie (44,50 = 50 pct reduction to ALL) +Parama1: Negative modifer value that affects RIPOSTE chance on defender. Ie (44,1,0,50 = 50 pct reduction to riposte chance) +Parama2: Negative modifer value that affects PARRY chance on defender. Ie (44,1,0,0,50 = 50 pct reduction to parry chance) +Parama3: Negative modifer value that affects BLOCK chance on defender. Ie (44,1,0,0,0,50 = 50 pct reduction to block chance) +Parama4: Negative modifer value that affects DODGE chance on defender. e (44,1,0,0,0,0,50 = 50 pct reduction to dodge chance) +Example of usage: Player has Improved Dodge V (+50 pct dodge chance), you want to negate this bonus you would set 44,1,0,0,0,0,50 on your NPC. + == 02/07/2015 == Akkadius: Reduced #repop time dramatically by taking down hundreds of individual SELECT/DELETE/INSERT queries in routines and bringing it down to very few See: https://www.youtube.com/watch?v=9kSFbyTBuAk diff --git a/zone/attack.cpp b/zone/attack.cpp index bfd4a5cc2..ef57b4b6b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -362,13 +362,40 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) //garunteed hit bool ghit = false; - if((attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500) + if((attacker->aabonuses.MeleeSkillCheck + attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500) ghit = true; + bool InFront = false; + + if (attacker->InFrontMob(this, attacker->GetX(), attacker->GetY())) + InFront = true; + + /* + This special ability adds a negative modifer to the defenders riposte/block/parry/chance + therefore reducing the defenders chance to successfully avoid the melee attack. At present + time this is the only way to fine tune counter these mods on players. This may + ultimately end up being more useful as fields in npc_types. + */ + + int counter_all = 0; + int counter_riposte = 0; + int counter_block = 0; + int counter_parry = 0; + int counter_dodge = 0; + + if (attacker->GetSpecialAbility(COUNTER_AVOID_DAMAGE)){ + + counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0); + counter_riposte = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE,1); + counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2); + counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3); + counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4); + } + ////////////////////////////////////////////////////////// // make enrage same as riposte ///////////////////////////////////////////////////////// - if (IsEnraged() && other->InFrontMob(this, other->GetX(), other->GetY())) { + if (IsEnraged() && InFront) { damage = -3; Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack."); } @@ -377,9 +404,10 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) // riposte ///////////////////////////////////////////////////////// float riposte_chance = 0.0f; - if (CanRiposte && damage > 0 && CanThisClassRiposte() && other->InFrontMob(this, other->GetX(), other->GetY())) + if (CanRiposte && damage > 0 && CanThisClassRiposte() && InFront) { - riposte_chance = (100.0f + (float)defender->aabonuses.RiposteChance + (float)defender->spellbonuses.RiposteChance + (float)defender->itembonuses.RiposteChance) / 100.0f; + riposte_chance = (100.0f + static_cast(aabonuses.RiposteChance + spellbonuses.RiposteChance + + itembonuses.RiposteChance - counter_riposte - counter_all)) / 100.0f; skill = GetSkill(SkillRiposte); if (IsClient()) { CastToClient()->CheckIncreaseSkill(SkillRiposte, other, -10); @@ -398,28 +426,19 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) /////////////////////////////////////////////////////// bool bBlockFromRear = false; - bool bShieldBlockFromRear = false; - if (this->IsClient()) { - int aaChance = 0; + // a successful roll on this does not mean a successful block is forthcoming. only that a chance to block + // from a direction other than the rear is granted. - // a successful roll on this does not mean a successful block is forthcoming. only that a chance to block - // from a direction other than the rear is granted. + int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind; - //Live AA - HightenedAwareness - int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind; - - if (BlockBehindChance && zone->random.Roll(BlockBehindChance)) { - bBlockFromRear = true; - - if (spellbonuses.BlockBehind || itembonuses.BlockBehind) - bShieldBlockFromRear = true; //This bonus should allow a chance to Shield Block from behind. - } - } + if (BlockBehindChance && zone->random.Roll(BlockBehindChance)) + bBlockFromRear = true; float block_chance = 0.0f; - if (damage > 0 && CanThisClassBlock() && (other->InFrontMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) { - block_chance = (100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f; + if (damage > 0 && CanThisClassBlock() && (InFront || bBlockFromRear)) { + block_chance = (100.0f + static_cast(aabonuses.IncreaseBlockChance + spellbonuses.IncreaseBlockChance + + itembonuses.IncreaseBlockChance - counter_block - counter_all)) / 100.0f; skill = CastToClient()->GetSkill(SkillBlock); if (IsClient()) { CastToClient()->CheckIncreaseSkill(SkillBlock, other, -10); @@ -435,32 +454,20 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) RollTable[1] = RollTable[0]; } - if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) - && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { - - float bonusShieldBlock = 0.0f; - bonusShieldBlock = static_cast(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock); - RollTable[1] += bonusShieldBlock; - } - - if(IsClient() && damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) - && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { - if(CastToClient()->m_inv.GetItem(MainPrimary)) { - float bonusStaffBlock = 0.0f; - if (CastToClient()->m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt){ - bonusStaffBlock = static_cast(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock); - RollTable[1] += bonusStaffBlock; - } - } - } + //Try Shield Block OR TwoHandBluntBlockCheck + if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) && (InFront || bBlockFromRear)) + RollTable[1] += static_cast(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock - counter_block - counter_all); + else if(damage > 0 && HasTwoHandBluntEquiped() && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) && (InFront || bBlockFromRear)) + RollTable[1] += static_cast(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock - counter_block - counter_all); + ////////////////////////////////////////////////////// // parry ////////////////////////////////////////////////////// float parry_chance = 0.0f; - if (damage > 0 && CanThisClassParry() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - parry_chance = (100.0f + (float)defender->spellbonuses.ParryChance + (float)defender->itembonuses.ParryChance) / 100.0f; + if (damage > 0 && CanThisClassParry() && InFront){ + parry_chance = (100.0f + static_cast(aabonuses.ParryChance + itembonuses.ParryChance + + itembonuses.ParryChance - counter_parry - counter_all)) / 100.0f; skill = CastToClient()->GetSkill(SkillParry); if (IsClient()) { CastToClient()->CheckIncreaseSkill(SkillParry, other, -10); @@ -481,9 +488,11 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) // dodge //////////////////////////////////////////////////////// float dodge_chance = 0.0f; - if (damage > 0 && CanThisClassDodge() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - dodge_chance = (100.0f + (float)defender->spellbonuses.DodgeChance + (float)defender->itembonuses.DodgeChance) / 100.0f; + if (damage > 0 && CanThisClassDodge() && InFront){ + + dodge_chance = (100.0f + static_cast(aabonuses.DodgeChance + spellbonuses.DodgeChance + + itembonuses.DodgeChance - counter_dodge - counter_all)) / 100.0f; + skill = CastToClient()->GetSkill(SkillDodge); if (IsClient()) { CastToClient()->CheckIncreaseSkill(SkillDodge, other, -10); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 8e99f9b23..a76860ce4 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -139,7 +139,8 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { // Clear item faction mods ClearItemFactionBonuses(); - ShieldEquiped(false); + SetShieldEquiped(false); + SetTwoHandBluntEquiped(false); unsigned int i; //should not include 21 (SLOT_AMMO) @@ -149,9 +150,12 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { continue; AddItemBonuses(inst, newbon); - //Check if item is secondary slot is a 'shield'. Required for multiple spelll effects. - if (i == MainSecondary && (m_inv.GetItem(MainSecondary)->GetItem()->ItemType == ItemTypeShield)) - ShieldEquiped(true); + //These are given special flags due to how often they are checked for various spell effects. + const Item_Struct *item = inst->GetItem(); + if (i == MainSecondary && (item && item->ItemType == ItemTypeShield)) + SetShieldEquiped(true); + else if (i == MainPrimary && (item && item->ItemType == ItemType2HBlunt)) + SetTwoHandBluntEquiped(true); } //Power Source Slot diff --git a/zone/common.h b/zone/common.h index 639eee91e..56ab6f819 100644 --- a/zone/common.h +++ b/zone/common.h @@ -136,7 +136,8 @@ enum { ALLOW_TO_TANK = 41, IGNORE_ROOT_AGGRO_RULES = 42, CASTING_RESIST_DIFF = 43, - MAX_SPECIAL_ATTACK = 44 + COUNTER_AVOID_DAMAGE = 44, + MAX_SPECIAL_ATTACK = 45 }; typedef enum { //fear states diff --git a/zone/mob.cpp b/zone/mob.cpp index e9cee33e4..ea86ff4dc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -170,6 +170,7 @@ Mob::Mob(const char* in_name, findable = false; trackable = true; has_shieldequiped = false; + has_twohandbluntequiped = false; has_numhits = false; has_MGB = false; has_ProjectIllusion = false; diff --git a/zone/mob.h b/zone/mob.h index e7318007e..fb8148713 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -308,7 +308,9 @@ public: void SetTargetable(bool on); bool IsTargetable() const { return m_targetable; } bool HasShieldEquiped() const { return has_shieldequiped; } - inline void ShieldEquiped(bool val) { has_shieldequiped = val; } + inline void SetShieldEquiped(bool val) { has_shieldequiped = val; } + bool HasTwoHandBluntEquiped() const { return has_twohandbluntequiped; } + inline void SetTwoHandBluntEquiped(bool val) { has_twohandbluntequiped = val; } virtual uint16 GetSkill(SkillUseTypes skill_num) const { return 0; } virtual uint32 GetEquipment(uint8 material_slot) const { return(0); } virtual int32 GetEquipmentMaterial(uint8 material_slot) const; @@ -1150,6 +1152,7 @@ protected: uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids bool offhand; bool has_shieldequiped; + bool has_twohandbluntequiped; bool has_numhits; bool has_MGB; bool has_ProjectIllusion; From 45e7ff91935de1eece1b6cfceb8931a6606b0f54 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 9 Feb 2015 03:02:25 -0500 Subject: [PATCH 17/26] Fix RoF2 item hotkeys This string thing needs to be longer! Still no idea what it is --- common/patches/rof2.cpp | 2 +- common/patches/rof2_structs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 6947ded15..d0189e56a 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -5053,7 +5053,7 @@ namespace RoF2 //sprintf(hdr.unknown000, "06e0002Y1W00"); - snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%012d", item->ID); + snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%016d", item->ID); hdr.stacksize = stackable ? charges : 1; hdr.unknown004 = 0; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 9ef524f55..95f5c31e6 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4353,7 +4353,7 @@ struct RoF2SlotStruct struct ItemSerializationHeader { -/*000*/ char unknown000[13]; // New for HoT. Looks like a string. +/*000*/ char unknown000[17]; // New for HoT. Looks like a string. /*017*/ uint32 stacksize; /*021*/ uint32 unknown004; /*025*/ uint8 slot_type; // 0 = normal, 1 = bank, 2 = shared bank, 9 = merchant, 20 = ? From e6eb4e16d1c9a7c5fa04161f1455f3ca0585226c Mon Sep 17 00:00:00 2001 From: Trevius Date: Mon, 9 Feb 2015 17:23:43 -0600 Subject: [PATCH 18/26] (SoF+) Removed duplicate packets being sent to client on zone. --- zone/client_packet.cpp | 66 ++---------------------------------------- 1 file changed, 2 insertions(+), 64 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 41c707287..fc05605c5 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1216,73 +1216,11 @@ void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) { - //This is a copy of SendExpZonein created for SoF+ due to packet order change - - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; + // New for Secrets of Faydwer+ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); QueuePacket(outapp); safe_delete(outapp); - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if (GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if (GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); - } - safe_delete(outapp); - - SendAATimers(); - - // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein - outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name, m_pp.name); - strcpy(zonesendname->name2, m_pp.name); - zonesendname->unknown0 = 0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - if (IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); - if (RuleB(Mercs, AllowMercs)) { SpawnMercOnZone(); From c4eb6e2881c7ed9238a2fdf3efa9e1963b1071e9 Mon Sep 17 00:00:00 2001 From: Trevius Date: Mon, 9 Feb 2015 18:52:17 -0600 Subject: [PATCH 19/26] (RoF+) Setting Alt flag on characters in the Guild Management Window is now saved and functional for filtering. --- changelog.txt | 3 +++ utils/patches/patch_RoF.conf | 2 +- utils/patches/patch_RoF2.conf | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 40a933c56..fdfc74944 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/09/2015 == +Trevius: (RoF+) Setting Alt flag on characters in the Guild Management Window is now saved and functional for filtering. + == 02/08/2015 == Kayen: Implemented npc specialability (44) COUNTER_AVOID_DAMAGE which when applied to the ATTACKING NPC will make their attacks more difficult to be avoided by riposte/dodge/parry/block. Parama0: Negative modifer value that affects ALL avoid damage types dodge/parry/riposte/block) chance on defender. Ie (44,50 = 50 pct reduction to ALL) diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 05a3a4a76..d144b040e 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -129,7 +129,7 @@ OP_GuildInviteAccept=0x78a5 OP_GuildDemote=0x3100 OP_GuildPromote=0x2945 OP_GuildPublicNote=0x3c2c -OP_GuildManageBanker=0x096d # Was 0x0737 +OP_GuildManageBanker=0x389c # Was 0x096d OP_GuildBank=0x2ab0 # Was 0x10c3 OP_SetGuildRank=0x3599 OP_GuildUpdateURLAndChannel=0x7851 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 7159dcce5..e032d917d 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -128,7 +128,7 @@ OP_GuildInviteAccept=0x7053 OP_GuildDemote=0x2d4e OP_GuildPromote=0x6a98 OP_GuildPublicNote=0x5053 -OP_GuildManageBanker=0x748f +OP_GuildManageBanker=0x3f35 OP_GuildBank=0x5134 OP_SetGuildRank=0x0b9c OP_GuildUpdateURLAndChannel=0x2958 From 543e265b0a91c8c172abe01615ee286a46684a6c Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 9 Feb 2015 17:48:07 -0800 Subject: [PATCH 20/26] Adjustments to OP_SpawnAppearance, as well as updating import client files to handle newer spell files --- client_files/import/main.cpp | 32 ++++++++++++++++++++++++++++++-- zone/client.cpp | 3 +-- zone/client.h | 1 - zone/client_packet.cpp | 3 --- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index a8477f77a..3a2f4154f 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -76,6 +76,24 @@ int GetSpellColumns(SharedDatabase *db) { return results.RowCount(); } +bool IsStringField(int i) { + switch(i) + { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + return true; + break; + default: + return false; + } +} + void ImportSpells(SharedDatabase *db) { Log.Out(Logs::General, Logs::Status, "Importing Spells..."); FILE *f = fopen("import/spells_us.txt", "r"); @@ -113,7 +131,12 @@ void ImportSpells(SharedDatabase *db) { sql += "'"; } - sql += split[i]; + if(split[i].compare("") == 0 && !IsStringField(i)) { + sql += "0"; + } + else { + sql += split[i]; + } sql += "'"; } @@ -128,7 +151,12 @@ void ImportSpells(SharedDatabase *db) { sql += "'"; } - sql += split[i]; + if(split[i].compare("") == 0 && !IsStringField(i)) { + sql += "0"; + } else { + sql += split[i]; + } + sql += "'"; } diff --git a/zone/client.cpp b/zone/client.cpp index e9983ad48..e2158cd54 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -141,11 +141,10 @@ Client::Client(EQStreamInterface* ieqs) merc_timer(RuleI(Mercs, UpkeepIntervalMS)), ItemTickTimer(10000), ItemQuestTimer(500), - anim_change_timer(100), anon_toggle_timer(250), afk_toggle_timer(250), helm_toggle_timer(250), - light_update_timer(250), + light_update_timer(600), m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f), m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), diff --git a/zone/client.h b/zone/client.h index 3d5bc8e4b..263aecf3e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1465,7 +1465,6 @@ private: Timer TrackingTimer; Timer RespawnFromHoverTimer; Timer merc_timer; - Timer anim_change_timer; Timer anon_toggle_timer; Timer afk_toggle_timer; Timer helm_toggle_timer; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fc05605c5..71c7ca7e4 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12584,9 +12584,6 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) if (IsAIControlled()) return; - if(!anim_change_timer.Check()) - return; - if (sa->parameter == ANIM_STAND) { SetAppearance(eaStanding); playeraction = 0; From 82b9af395618808f7d0f4ce154ed45f06d65a370 Mon Sep 17 00:00:00 2001 From: Trevius Date: Mon, 9 Feb 2015 19:54:14 -0600 Subject: [PATCH 21/26] (RoF+) Guild Invites between RoF+ and previous Clients is now functional. --- changelog.txt | 1 + zone/client_packet.cpp | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index fdfc74944..a7a1127dd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 02/09/2015 == Trevius: (RoF+) Setting Alt flag on characters in the Guild Management Window is now saved and functional for filtering. +Trevius: (RoF+) Guild Invites between RoF+ and previous Clients is now functional. == 02/08/2015 == Kayen: Implemented npc specialability (44) COUNTER_AVOID_DAMAGE which when applied to the ATTACKING NPC will make their attacks more difficult to be avoided by riposte/dodge/parry/block. diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 71c7ca7e4..f0fefaf9e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -7298,6 +7298,16 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) if (gc->guildeqid == 0) gc->guildeqid = GuildID(); + // Convert Membership Level between RoF and previous clients. + if (client->GetClientVersion() < ClientVersion::RoF && GetClientVersion() >= ClientVersion::RoF) + { + gc->officer = 0; + } + if (client->GetClientVersion() >= ClientVersion::RoF && GetClientVersion() < ClientVersion::RoF) + { + gc->officer = 8; + } + Log.Out(Logs::Detail, Logs::Guilds, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); client->SetPendingGuildInvitation(true); client->QueuePacket(app); @@ -7332,6 +7342,8 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*)app->pBuffer; + uint32 guildrank = gj->response; + if (GetClientVersion() >= ClientVersion::RoF) { if (gj->response > 9) @@ -7360,9 +7372,25 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) Log.Out(Logs::Detail, Logs::Guilds, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", gj->guildeqid, gj->response, gj->inviter, gj->newmember); + //ok, the invite is also used for changing rank as well. + Mob* inviter = entity_list.GetMob(gj->inviter); + + if (inviter && inviter->IsClient()) + { + Client* client = inviter->CastToClient(); + // Convert Membership Level between RoF and previous clients. + if (client->GetClientVersion() < ClientVersion::RoF && GetClientVersion() >= ClientVersion::RoF) + { + guildrank = 0; + } + if (client->GetClientVersion() >= ClientVersion::RoF && GetClientVersion() < ClientVersion::RoF) + { + guildrank = 8; + } + } //we dont really care a lot about what this packet means, as long as //it has been authorized with the guild manager - if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) { + if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, guildrank)) { worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); Message(13, "Invalid invite response packet!"); return; @@ -7390,7 +7418,7 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) //change guild and rank - uint32 guildrank = gj->response; + guildrank = gj->response; if (GetClientVersion() >= ClientVersion::RoF) { From 927e4f83ff2094482f6c966f0f3b6ac40047c181 Mon Sep 17 00:00:00 2001 From: Trevius Date: Mon, 9 Feb 2015 20:07:04 -0600 Subject: [PATCH 22/26] (RoF2) Updated Expedition Opcodes --- utils/patches/patch_RoF2.conf | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index e032d917d..189edc8b3 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -353,21 +353,22 @@ OP_Marquee=0x502e OP_ItemRecastDelay=0x15a9 #OP_OpenInventory=0x0000 # Likely does not exist in RoF -U -OP_DzQuit=0x205f -OP_DzListTimers=0x0398 -OP_DzAddPlayer=0x59ca -OP_DzRemovePlayer=0x4701 -OP_DzSwapPlayer=0x1abc -OP_DzMakeLeader=0x405b -OP_DzPlayerList=0x543d -OP_DzJoinExpeditionConfirm=0x14c6 -OP_DzJoinExpeditionReply=0x7f4b -OP_DzExpeditionInfo=0x4f7e -OP_DzExpeditionList=0x9119 -OP_DzMemberStatus=0xb2e3 -OP_DzLeaderStatus=0x32f0 +# Expeditions +OP_DzAddPlayer=0x4701 +OP_DzRemovePlayer=0x1abc +OP_DzSwapPlayer=0x405b +OP_DzMakeLeader=0x543d +OP_DzPlayerList=0x14c6 +OP_DzJoinExpeditionConfirm=0x7f4b +OP_DzJoinExpeditionReply=0x1950 +OP_DzListTimers=0x7b68 +OP_DzExpeditionInfo=0x9119 +OP_DzExpeditionList=0x205f +OP_DzQuit=0xb2e3 +OP_DzMemberStatus=0x32f0 +OP_DzLeaderStatus=0x3de9 +OP_DzMemberList=0x5ae4 OP_DzExpeditionEndsWarning=0x383c -OP_DzMemberList=0x3de9 OP_DzCompass=0x3e0e OP_DzChooseZone=0x0b7d From f613d12c619249ce990f6976186624edb13ed34d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 10 Feb 2015 12:49:32 -0500 Subject: [PATCH 23/26] Revert "(SoF+) Removed duplicate packets being sent to client on zone." This reverts commit e6eb4e16d1c9a7c5fa04161f1455f3ca0585226c. --- zone/client_packet.cpp | 66 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f0fefaf9e..c37e6dc86 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1216,11 +1216,73 @@ void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) { - // New for Secrets of Faydwer+ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); + //This is a copy of SendExpZonein created for SoF+ due to packet order change + + ////////////////////////////////////////////////////// + // Spawn Appearance Packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; + sa->type = AT_SpawnID; // Is 0x10 used to set the player id? + sa->parameter = GetID(); // Four bytes for this parameter... + outapp->priority = 6; QueuePacket(outapp); safe_delete(outapp); + // Inform the world about the client + outapp = new EQApplicationPacket(); + + CreateSpawnPacket(outapp); + outapp->priority = 6; + if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetPVP()) //force a PVP update until we fix the spawn struct + SendAppearancePacket(AT_PVP, GetPVP(), true, false); + + //Send AA Exp packet: + if (GetLevel() >= 51) + SendAAStats(); + + // Send exp packets + outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); + ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); + uint32 tmpxp2 = GetEXPForLevel(GetLevel()); + + // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) + if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { + float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); + eu->exp = (uint32)(330.0f * tmpxp); + outapp->priority = 6; + QueuePacket(outapp); + } + safe_delete(outapp); + + SendAATimers(); + + // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein + outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); + QueuePacket(outapp); + safe_delete(outapp); + + outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); + ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; + strcpy(zonesendname->name, m_pp.name); + strcpy(zonesendname->name2, m_pp.name); + zonesendname->unknown0 = 0x0A; + QueuePacket(outapp); + safe_delete(outapp); + + if (IsInAGuild()) { + SendGuildMembers(); + SendGuildURL(); + SendGuildChannel(); + SendGuildLFGuildStatus(); + } + SendLFGuildStatus(); + + //No idea why live sends this if even were not in a guild + SendGuildMOTD(); + if (RuleB(Mercs, AllowMercs)) { SpawnMercOnZone(); From 9daf572ea78394e6f909031030c159dd628508cb Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 10 Feb 2015 11:53:16 -0800 Subject: [PATCH 24/26] Fix for no factions in database crashing the server and shared memory --- common/shareddb.cpp | 9 +++------ shared_memory/npc_faction.cpp | 3 --- zone/zonedb.cpp | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 13ae065be..0e057812d 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1158,7 +1158,7 @@ void SharedDatabase::GetFactionListInfo(uint32 &list_count, uint32 &max_lists) { auto row = results.begin(); list_count = static_cast(atoul(row[0])); - max_lists = static_cast(atoul(row[1])); + max_lists = static_cast(atoul(row[1] ? row[1] : "0")); } const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) { @@ -1235,9 +1235,6 @@ bool SharedDatabase::LoadNPCFactionLists() { uint32 list_count = 0; uint32 max_lists = 0; GetFactionListInfo(list_count, max_lists); - if(list_count == 0) { - EQ_EXCEPT("SharedDatabase", "Database returned no result"); - } uint32 size = static_cast(EQEmu::FixedMemoryHashSet::estimated_size( list_count, max_lists)); @@ -1837,7 +1834,7 @@ void SharedDatabase::GetLootTableInfo(uint32 &loot_table_count, uint32 &max_loot auto row = results.begin(); loot_table_count = static_cast(atoul(row[0])); - max_loot_table = static_cast(atoul(row[1])); + max_loot_table = static_cast(atoul(row[1] ? row[1] : "0")); loot_table_entries = static_cast(atoul(row[2])); } @@ -1858,7 +1855,7 @@ void SharedDatabase::GetLootDropInfo(uint32 &loot_drop_count, uint32 &max_loot_d auto row =results.begin(); loot_drop_count = static_cast(atoul(row[0])); - max_loot_drop = static_cast(atoul(row[1])); + max_loot_drop = static_cast(atoul(row[1] ? row[1] : "0")); loot_drop_entries = static_cast(atoul(row[2])); } diff --git a/shared_memory/npc_faction.cpp b/shared_memory/npc_faction.cpp index df33b7368..ebaeb3457 100644 --- a/shared_memory/npc_faction.cpp +++ b/shared_memory/npc_faction.cpp @@ -31,9 +31,6 @@ void LoadFactions(SharedDatabase *database) { uint32 lists = 0; uint32 max_list = 0; database->GetFactionListInfo(lists, max_list); - if(lists == 0) { - EQ_EXCEPT("Shared Memory", "Unable to get any factions from the database."); - } uint32 size = static_cast(EQEmu::FixedMemoryHashSet::estimated_size(lists, max_list)); EQEmu::MemoryMappedFile mmf("shared/faction", size); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 165282f3d..6cb15d16e 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3256,7 +3256,7 @@ bool ZoneDatabase::LoadFactionData() auto row = results.begin(); - max_faction = atoi(row[0]); + max_faction = row[0] ? atoi(row[0]) : 0; faction_array = new Faction*[max_faction+1]; for(unsigned int index=0; index Date: Tue, 10 Feb 2015 19:22:42 -0600 Subject: [PATCH 25/26] (SoF+) Removed duplicate packets being sent to client on zone (Take #2)! --- zone/client.cpp | 63 +++++++++++++++++++ zone/client.h | 1 + zone/client_packet.cpp | 138 +++-------------------------------------- 3 files changed, 73 insertions(+), 129 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index e2158cd54..b5575a307 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -413,6 +413,69 @@ Client::~Client() { UninitializeBuffSlots(); } +void Client::SendZoneInPackets() +{ + ////////////////////////////////////////////////////// + // Spawn Appearance Packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; + sa->type = AT_SpawnID; // Is 0x10 used to set the player id? + sa->parameter = GetID(); // Four bytes for this parameter... + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + // Inform the world about the client + outapp = new EQApplicationPacket(); + + CreateSpawnPacket(outapp); + outapp->priority = 6; + if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetPVP()) //force a PVP update until we fix the spawn struct + SendAppearancePacket(AT_PVP, GetPVP(), true, false); + + //Send AA Exp packet: + if (GetLevel() >= 51) + SendAAStats(); + + // Send exp packets + outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); + ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); + uint32 tmpxp2 = GetEXPForLevel(GetLevel()); + + // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) + if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { + float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); + eu->exp = (uint32)(330.0f * tmpxp); + outapp->priority = 6; + QueuePacket(outapp); + } + safe_delete(outapp); + + SendAATimers(); + + outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); + ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; + strcpy(zonesendname->name, m_pp.name); + strcpy(zonesendname->name2, m_pp.name); + zonesendname->unknown0 = 0x0A; + QueuePacket(outapp); + safe_delete(outapp); + + if (IsInAGuild()) { + SendGuildMembers(); + SendGuildURL(); + SendGuildChannel(); + SendGuildLFGuildStatus(); + } + SendLFGuildStatus(); + + //No idea why live sends this if even were not in a guild + SendGuildMOTD(); +} + void Client::SendLogoutPackets() { EQApplicationPacket* outapp = new EQApplicationPacket(OP_CancelTrade, sizeof(CancelTrade_Struct)); diff --git a/zone/client.h b/zone/client.h index 263aecf3e..53d3c0428 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1415,6 +1415,7 @@ private: bool CanBeInZone(); void SendLogoutPackets(); + void SendZoneInPackets(); bool AddPacket(const EQApplicationPacket *, bool); bool AddPacket(EQApplicationPacket**, bool); bool SendAllPackets(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c37e6dc86..db7f65781 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1089,76 +1089,15 @@ void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app) void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) { - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendExpZonein, 0); QueuePacket(outapp); safe_delete(outapp); - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if (GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if (GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); + // SoF+ Gets Zone-In packets after sending OP_WorldObjectsSent + if (GetClientVersion() < ClientVersion::SoF) + { + SendZoneInPackets(); } - safe_delete(outapp); - - SendAATimers(); - - outapp = new EQApplicationPacket(OP_SendExpZonein, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name, m_pp.name); - strcpy(zonesendname->name2, m_pp.name); - zonesendname->unknown0 = 0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - /* this is actually the guild MOTD - outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2)); - ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer; - strcpy(zonesendname2->name,m_pp.name); - QueuePacket(outapp); - safe_delete(outapp);*/ - - if (IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); return; } @@ -1216,72 +1155,13 @@ void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) { - //This is a copy of SendExpZonein created for SoF+ due to packet order change - - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; + // New for SoF+ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); QueuePacket(outapp); safe_delete(outapp); - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if (GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if (GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); - } - safe_delete(outapp); - - SendAATimers(); - - // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein - outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name, m_pp.name); - strcpy(zonesendname->name2, m_pp.name); - zonesendname->unknown0 = 0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - if (IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); + // Packet order changed for SoF+, so below is sent here instead of OP_SendExpLogin + SendZoneInPackets(); if (RuleB(Mercs, AllowMercs)) { From c8acb7bd434edafae8711e26812dc5a74359bcfa Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 10 Feb 2015 23:24:41 -0500 Subject: [PATCH 26/26] numhits issue fix --- zone/attack.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index ef57b4b6b..b98fa1c3e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3530,6 +3530,10 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons Log.Out(Logs::Detail, Logs::Combat, "Melee Damage reduced to %d", damage); damage = ReduceAllDamage(damage); TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker); + + if (skill_used) + CheckNumHitsRemaining(NumHit::IncomingHitSuccess); + } else { int32 origdmg = damage; damage = AffectMagicalDamage(damage, spell_id, iBuffTic, attacker); @@ -3545,9 +3549,6 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker); } - if (skill_used) - CheckNumHitsRemaining(NumHit::IncomingHitSuccess); - if(IsClient() && CastToClient()->sneaking){ CastToClient()->sneaking = false; SendAppearancePacket(AT_Sneak, 0);