From eaf5cea90893eb44e5b5e5caec2d487d07e61531 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sat, 14 Feb 2015 10:29:43 -0500 Subject: [PATCH 01/11] Fixed a comment --- zone/command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/command.cpp b/zone/command.cpp index f54db9a75..f175ce1c1 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -28,7 +28,7 @@ set to nullptr and 0 respectively since they aren't used when adding an alias. The function pointers being equal is makes it an alias. The access level you set with command_add is only a default if - the command isn't listed in the addon.ini file. + the command isn't listed in the commands db table. */ From f9dbea531c937e380b9241fbd63190fcf0cd7a38 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 16 Feb 2015 17:14:29 -0500 Subject: [PATCH 02/11] Added note about augs to changelog --- changelog.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.txt b/changelog.txt index 88180df59..a429579ab 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/09/2015 == +Noudess: Placing an aug tagged as magical onto a non-magical weapon now allows +the resulting weapon to hit creatures requiring magical creatures. Seems like +the correct thing, as the weapon is marked as magical. == 01/31/2015 == Trevius: Fixed FindGroundZ() and GetGroundZ() to once again utilize the X and Y arguments that are passed to them. From fe77c6fb3f69df4daba9131eada5fc7867504cdb Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 18 Feb 2015 16:26:48 -0500 Subject: [PATCH 03/11] Updated change log and made changes to worlddb.cpp so paineel characters start in paineel again on Titanium. --- changelog.txt | 3 +++ world/worlddb.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/changelog.txt b/changelog.txt index a429579ab..9db99cc58 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/18/2015 == +Noudess: Starting erudites that were supposed to start in paineel were landing +in erudin on Titanium. Fixed to be paineel. == 02/09/2015 == Noudess: Placing an aug tagged as magical onto a non-magical weapon now allows the resulting weapon to hit creatures requiring magical creatures. Seems like diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 223ae89d6..27093e2ea 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -348,8 +348,16 @@ void WorldDatabase::SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, Cha { case 0: { + if (in_cc->deity == 203) // Cazie erudites go to paineel + { + in_pp->zone_id = 75; // paineel + in_pp->binds[0].zoneId = 75; // paineel + } + else + { in_pp->zone_id = 24; // erudnext in_pp->binds[0].zoneId = 38; // tox + } break; } case 1: From ba49e5f696da69176a89c5dfd50181b17205f458 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 23 Feb 2015 13:32:10 -0500 Subject: [PATCH 04/11] Allow servers to set starting value for swimming instead of the hard coded value. --- changelog.txt | 6 ++++++ common/ruletypes.h | 1 + world/client.cpp | 7 +++++++ zone/client_packet.cpp | 4 ---- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/changelog.txt b/changelog.txt index f12dfd0ae..e27e7f44a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/23/2015 == +Noudess: Allow for a rule to set starting swimming value. +If the rule does not exist, it uses the old hard coded overrides. I moved +the swimming override to char create instead of setting it every time a char +enters a zone. + == 02/21/2015 == Noudess: Starting erudites that were supposed to start in paineel were landing in erudin on Titanium. Fixed to be paineel. diff --git a/common/ruletypes.h b/common/ruletypes.h index 12a8a0737..7be048385 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -133,6 +133,7 @@ RULE_INT ( Skills, MaxTrainTradeskills, 21 ) RULE_BOOL ( Skills, UseLimitTradeskillSearchSkillDiff, true ) RULE_INT ( Skills, MaxTradeskillSearchSkillDiff, 50 ) RULE_INT ( Skills, MaxTrainSpecializations, 50 ) // Max level a GM trainer will train casting specializations +RULE_INT ( Skills, SwimmingStartValue, 100 ) RULE_CATEGORY_END() RULE_CATEGORY( Pets ) diff --git a/world/client.cpp b/world/client.cpp index 540dfa683..2fbb6ffb9 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1430,6 +1430,13 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) SetClassStartingSkills(&pp); SetClassLanguages(&pp); pp.skills[SkillSenseHeading] = 200; + + // Allow server to force swimming training from a configured level + std::string value; + bool userule; + userule=RuleManager::Instance()->GetRule("Skills:SwimmingStartValue", value); + pp.skills[SkillSwimming] = (userule) ? atoi(value.c_str()) : 100; + // strcpy(pp.servername, WorldConfig::get()->ShortName.c_str()); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f93f83018..bd856fc45 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1431,10 +1431,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000){ m_pp.ldon_points_tak = 0; } if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; } - /* Set Swimming Skill 100 by default if under 100 */ - if (GetSkill(SkillSwimming) < 100) - SetSkill(SkillSwimming, 100); - /* Initialize AA's : Move to function eventually */ for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ aa[a] = &m_pp.aa_array[a]; } query = StringFormat( From 221c1f17c7e9b2489d12815579a579083c280c6b Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 23 Feb 2015 19:03:28 -0500 Subject: [PATCH 05/11] Streamline changes for Swimming Rule and add Sense Heading rules --- changelog.txt | 10 ++++++---- common/ruletypes.h | 2 ++ world/client.cpp | 8 ++------ zone/client_packet.cpp | 29 ++++++++++++++++++++++++++++- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/changelog.txt b/changelog.txt index e27e7f44a..f2fe06150 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,10 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 02/23/2015 == -Noudess: Allow for a rule to set starting swimming value. -If the rule does not exist, it uses the old hard coded overrides. I moved -the swimming override to char create instead of setting it every time a char -enters a zone. +Noudess: Allow for a rule to set starting swimming && SenseHeading value. +I moved the swimming override to char create instead of setting it +every time a char enters a zone. + +Also added rules to not ignore, but rather forrce sense heading packets to be +used to train it instead of maxing it out like before. == 02/21/2015 == Noudess: Starting erudites that were supposed to start in paineel were landing in erudin on Titanium. Fixed to be paineel. diff --git a/common/ruletypes.h b/common/ruletypes.h index 7be048385..7894c5ebd 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -134,6 +134,8 @@ RULE_BOOL ( Skills, UseLimitTradeskillSearchSkillDiff, true ) RULE_INT ( Skills, MaxTradeskillSearchSkillDiff, 50 ) RULE_INT ( Skills, MaxTrainSpecializations, 50 ) // Max level a GM trainer will train casting specializations RULE_INT ( Skills, SwimmingStartValue, 100 ) +RULE_BOOL ( Skills, TrainSenseHeading, false ) +RULE_INT ( Skills, SenseHeadingStartValue, 200 ) RULE_CATEGORY_END() RULE_CATEGORY( Pets ) diff --git a/world/client.cpp b/world/client.cpp index 2fbb6ffb9..1cf88d481 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1429,13 +1429,9 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) SetRaceStartingSkills(&pp); SetClassStartingSkills(&pp); SetClassLanguages(&pp); - pp.skills[SkillSenseHeading] = 200; - // Allow server to force swimming training from a configured level - std::string value; - bool userule; - userule=RuleManager::Instance()->GetRule("Skills:SwimmingStartValue", value); - pp.skills[SkillSwimming] = (userule) ? atoi(value.c_str()) : 100; + pp.skills[SkillSwimming] = RuleI(Skills, SwimmingStartValue); + pp.skills[SkillSenseHeading] = RuleI(Skills, SenseHeadingStartValue); // strcpy(pp.servername, WorldConfig::get()->ShortName.c_str()); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index bd856fc45..29abadb61 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -332,7 +332,13 @@ void MapOpcodes() ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save; ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq; ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute; - ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_Ignore; + + // Use or Ignore sense heading based on rule. + bool train=RuleB(Skills, TrainSenseHeading); + + ConnectedOpcodes[OP_SenseHeading] = + (train) ? &Client::Handle_OP_SenseHeading : &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps; ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD; ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode; @@ -11648,6 +11654,27 @@ void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app) return; } +void Client::Handle_OP_SenseHeading(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillSenseHeading)) + return; + + int chancemod=0; + + // The client seems to limit sense heading packets based on skill + // level. So if we're really low, we don't hit this routine very often. + // I think it's the GUI deciding when to skill you up. + // So, I'm adding a mod here which is larger at lower levels so + // very low levels get a much better chance to skill up when the GUI + // eventually sends a message. + if (GetLevel() <= 8) + chancemod += (9-level) * 10; + + CheckIncreaseSkill(SkillSenseHeading, nullptr, chancemod); + + return; +} + void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) { if (!HasSkill(SkillSenseTraps)) From 4835b7063cc259a3063ba94143f0dce1a5ae0929 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 28 Feb 2015 23:24:19 -0500 Subject: [PATCH 06/11] PERL remove proc functions $npc->RemoveMeleeProc(spell_id) $npc->RemoveDefensiveProc(spell_id) $npc->RemoveDefensiveProc(spell_id) --- zone/perl_npc.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index a2943d035..2011cbc98 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -2409,6 +2409,78 @@ XS(XS_NPC_AddDefensiveProc) { XSRETURN_EMPTY; } +XS(XS_NPC_RemoveMeleeProc); +XS(XS_NPC_RemoveMeleeProc) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::RemoveMeleeProc(THIS,spellid)"); + { + NPC * THIS; + int spell_id = (int)SvIV(ST(1)); + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveProcFromWeapon(spell_id, false); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_RemoveRangedProc); +XS(XS_NPC_RemoveRangedProc) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::RemoveRangedProc(THIS,spellid)"); + { + NPC * THIS; + int spell_id = (int)SvIV(ST(1)); + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveRangedProc(spell_id, false); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_RemoveDefensiveProc); +XS(XS_NPC_RemoveDefensiveProc) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::RemoveDefensiveProc(THIS,spellid)"); + { + NPC * THIS; + int spell_id = (int)SvIV(ST(1)); + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveDefensiveProc(spell_id, false); + } + XSRETURN_EMPTY; +} + XS(XS_NPC_ChangeLastName); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_ChangeLastName) { @@ -2566,6 +2638,9 @@ XS(boot_NPC) newXSproto(strcpy(buf, "AddMeleeProc"), XS_NPC_AddMeleeProc, file, "$$$"); newXSproto(strcpy(buf, "AddRangedProc"), XS_NPC_AddRangedProc, file, "$$$"); newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$"); + newXSproto(strcpy(buf, "RemoveMeleeProc"), XS_NPC_RemoveMeleeProc, file, "$$"); + newXSproto(strcpy(buf, "RemoveDefensiveProc"), XS_NPC_RemoveDefensiveProc, file, "$$"); + newXSproto(strcpy(buf, "RemoveDefensiveProc"), XS_NPC_RemoveDefensiveProc, file, "$$"); newXSproto(strcpy(buf, "ChangeLastName"), XS_NPC_ChangeLastName, file, "$:$"); newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$"); XSRETURN_YES; From d3249397f3db425bbddfe04b04bd7058b0ddca4b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 28 Feb 2015 23:39:44 -0500 Subject: [PATCH 07/11] fix to prior commit --- zone/perl_npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 2011cbc98..febee6941 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -2639,7 +2639,7 @@ XS(boot_NPC) newXSproto(strcpy(buf, "AddRangedProc"), XS_NPC_AddRangedProc, file, "$$$"); newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$"); newXSproto(strcpy(buf, "RemoveMeleeProc"), XS_NPC_RemoveMeleeProc, file, "$$"); - newXSproto(strcpy(buf, "RemoveDefensiveProc"), XS_NPC_RemoveDefensiveProc, file, "$$"); + newXSproto(strcpy(buf, "RemoveRangedProc"), XS_NPC_RemoveRangedProc, file, "$$"); newXSproto(strcpy(buf, "RemoveDefensiveProc"), XS_NPC_RemoveDefensiveProc, file, "$$"); newXSproto(strcpy(buf, "ChangeLastName"), XS_NPC_ChangeLastName, file, "$:$"); newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$"); From f95806b47b10db1aea1b1615d3e8a51601b114d7 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 2 Mar 2015 16:23:46 -0500 Subject: [PATCH 08/11] Move item caps that depend on spells/aas to be done after those are valid Also fix Sleeper's Tomb avatar proc to be counted towards item ATK --- zone/bonuses.cpp | 36 +++++++++++++++++++----------------- zone/client.h | 2 ++ zone/client_mods.cpp | 6 ++++++ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index c02a7897f..c17980297 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -81,6 +81,8 @@ void Client::CalcBonuses() CalcAABonuses(&aabonuses); //we're not quite ready for this Log.Out(Logs::Detail, Logs::AA, "Finished calculating AA Bonuses for %s.", this->GetCleanName()); + ProcessItemCaps(); // caps that depend on spell/aa bonuses + RecalcWeight(); CalcAC(); @@ -183,16 +185,24 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { AdditiveWornBonuses(inst, newbon); } } +} - // Caps - if(newbon->HPRegen > CalcHPRegenCap()) - newbon->HPRegen = CalcHPRegenCap(); +// These item stat caps depend on spells/AAs so we process them after those are processed +void Client::ProcessItemCaps() +{ + itembonuses.HPRegen = std::min(itembonuses.HPRegen, CalcHPRegenCap()); + itembonuses.ManaRegen = std::min(itembonuses.ManaRegen, CalcManaRegenCap()); + itembonuses.EnduranceRegen = std::min(itembonuses.EnduranceRegen, CalcEnduranceRegenCap()); - if(newbon->ManaRegen > CalcManaRegenCap()) - newbon->ManaRegen = CalcManaRegenCap(); + // The Sleeper Tomb Avatar proc counts towards item ATK + // The client uses a 100 here, so using a 100 here the client and server will agree + // For example, if you set the effect to be 200 it will get 100 item ATK and 100 spell ATK + if (IsValidSpell(2434) && FindBuff(2434)) { + itembonuses.ATK += 100; + spellbonuses.ATK -= 100; + } - if(newbon->EnduranceRegen > CalcEnduranceRegenCap()) - newbon->EnduranceRegen = CalcEnduranceRegenCap(); + itembonuses.ATK = std::min(itembonuses.ATK, CalcItemATKCap()); } void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, bool isTribute) { @@ -225,6 +235,7 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu newbon->HP += item->HP; newbon->Mana += item->Mana; newbon->Endurance += item->Endur; + newbon->ATK += item->Attack; newbon->STR += (item->AStr + item->HeroicStr); newbon->STA += (item->ASta + item->HeroicSta); newbon->DEX += (item->ADex + item->HeroicDex); @@ -278,6 +289,7 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu newbon->HP += CalcRecommendedLevelBonus( lvl, reclvl, item->HP ); newbon->Mana += CalcRecommendedLevelBonus( lvl, reclvl, item->Mana ); newbon->Endurance += CalcRecommendedLevelBonus( lvl, reclvl, item->Endur ); + newbon->ATK += CalcRecommendedLevelBonus( lvl, reclvl, item->Attack ); newbon->STR += CalcRecommendedLevelBonus( lvl, reclvl, (item->AStr + item->HeroicStr) ); newbon->STA += CalcRecommendedLevelBonus( lvl, reclvl, (item->ASta + item->HeroicSta) ); newbon->DEX += CalcRecommendedLevelBonus( lvl, reclvl, (item->ADex + item->HeroicDex) ); @@ -335,16 +347,6 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu if(item->EnduranceRegen > 0) newbon->EnduranceRegen += item->EnduranceRegen; - if(item->Attack > 0) { - - int cap = RuleI(Character, ItemATKCap); - cap += itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; - - if((newbon->ATK + item->Attack) > cap) - newbon->ATK = RuleI(Character, ItemATKCap); - else - newbon->ATK += item->Attack; - } if(item->DamageShield > 0) { if((newbon->DamageShield + item->DamageShield) > RuleI(Character, ItemDamageShieldCap)) newbon->DamageShield = RuleI(Character, ItemDamageShieldCap); diff --git a/zone/client.h b/zone/client.h index ce3913f2e..3a90e87eb 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1263,6 +1263,7 @@ protected: void CalcEdibleBonuses(StatBonuses* newbon); void CalcAABonuses(StatBonuses* newbon); void ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon); + void ProcessItemCaps(); void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true); bool client_data_loaded; @@ -1316,6 +1317,7 @@ private: int32 GetACMit(); int32 GetACAvoid(); int32 CalcATK(); + int32 CalcItemATKCap(); int32 CalcHaste(); int32 CalcAlcoholPhysicalEffect(); diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index a3da654a2..6aae0d0e9 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -2148,6 +2148,12 @@ int32 Client::CalcEnduranceRegenCap() return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100); } +int32 Client::CalcItemATKCap() +{ + int cap = RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; + return cap; +} + int Client::GetRawACNoShield(int &shield_ac) const { int ac = itembonuses.AC + spellbonuses.AC + aabonuses.AC; From fe294e60b523b75f2bf39c0834d70ecfee49cff4 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 3 Mar 2015 04:08:52 -0500 Subject: [PATCH 09/11] Fix for 'Invalid Slot ID' messages, item loss during corpse looting, and possible item loss during LDoN/Adventure merchant purchases --- changelog.txt | 7 ++++++ common/shareddb.cpp | 33 ++++++++++++++++---------- zone/corpse.cpp | 4 ++-- zone/inventory.cpp | 58 ++++++++++++++++++++++++++++++++++++--------- 4 files changed, 76 insertions(+), 26 deletions(-) diff --git a/changelog.txt b/changelog.txt index 8fbdecd1f..23138de97 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/03/2015 == +Uleat: Fix for 'Invalid Slot ID' messages. Bag slot count is now enforced during database saves to eliminate existing 'hidden' duplicated items..sorry MQ2 users... +Uleat: Fix for Item loss during corpse looting and possible item loss when purchasing items from LDoN or Adventure merchants. (cursor-related) + +== 02/27/2015 == +Uleat: Final 'tweak' for light sources until a valid issue arises. Wearable equipment now matches client behavior. Stackable light sources are bugged in the client..but, the current timer update implementation alleviates this condition. + == 02/26/2015 == Uleat: Updated light source criteria to (hopefully) match what the client uses (still needs tweaking) Uleat: Changed 'general' light source checks to accept the last valid light source (client behavior) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 396bdb494..aa4924665 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -151,28 +151,31 @@ bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const Ite bool SharedDatabase::SaveInventory(uint32 char_id, const ItemInst* inst, int16 slot_id) { - // If we never save tribute slots..how are we to ever benefit from them!!? The client - // object is destroyed upon zoning - including its inventory object..and if tributes - // don't exist in the database, then they will never be loaded when the new client - // object is created in the new zone object... Something to consider... - // - // (we could add them to the 'NoRent' checks and dispose of after 30 minutes offline) - //never save tribute slots: if(slot_id >= EmuConstants::TRIBUTE_BEGIN && slot_id <= EmuConstants::TRIBUTE_END) return true; if (slot_id >= EmuConstants::SHARED_BANK_BEGIN && slot_id <= EmuConstants::SHARED_BANK_BAGS_END) { // Shared bank inventory - if (!inst) - return DeleteSharedBankSlot(char_id, slot_id); - else - return UpdateSharedBankSlot(char_id, inst, slot_id); + if (!inst) { + return DeleteSharedBankSlot(char_id, slot_id); + } + else { + // Needed to clear out bag slots that 'REPLACE' in UpdateSharedBankSlot does not overwrite..otherwise, duplication occurs + // (This requires that parent then child items be sent..which should be how they are currently passed) + if (Inventory::SupportsContainers(slot_id)) + DeleteSharedBankSlot(char_id, slot_id); + return UpdateSharedBankSlot(char_id, inst, slot_id); + } } else if (!inst) { // All other inventory return DeleteInventorySlot(char_id, slot_id); } + // Needed to clear out bag slots that 'REPLACE' in UpdateInventorySlot does not overwrite..otherwise, duplication occurs + // (This requires that parent then child items be sent..which should be how they are currently passed) + if (Inventory::SupportsContainers(slot_id)) + DeleteInventorySlot(char_id, slot_id); return UpdateInventorySlot(char_id, inst, slot_id); } @@ -209,7 +212,9 @@ bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const ItemInst* inst, i // Save bag contents, if slot supports bag contents if (inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) - for (uint8 idx = SUB_BEGIN; idx < EmuConstants::ITEM_CONTAINER_SIZE; idx++) { + // Limiting to bag slot count will get rid of 'hidden' duplicated items and 'Invalid Slot ID' + // messages through attrition (and the modded code in SaveInventory) + for (uint8 idx = SUB_BEGIN; idx < inst->GetItem()->BagSlots && idx < EmuConstants::ITEM_CONTAINER_SIZE; idx++) { const ItemInst* baginst = inst->GetItem(idx); SaveInventory(char_id, baginst, Inventory::CalcSlotId(slot_id, idx)); } @@ -253,7 +258,9 @@ bool SharedDatabase::UpdateSharedBankSlot(uint32 char_id, const ItemInst* inst, // Save bag contents, if slot supports bag contents if (inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) { - for (uint8 idx = SUB_BEGIN; idx < EmuConstants::ITEM_CONTAINER_SIZE; idx++) { + // Limiting to bag slot count will get rid of 'hidden' duplicated items and 'Invalid Slot ID' + // messages through attrition (and the modded code in SaveInventory) + for (uint8 idx = SUB_BEGIN; idx < inst->GetItem()->BagSlots && idx < EmuConstants::ITEM_CONTAINER_SIZE; idx++) { const ItemInst* baginst = inst->GetItem(idx); SaveInventory(char_id, baginst, Inventory::CalcSlotId(slot_id, idx)); } diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 755fd3957..551f49a1b 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1217,9 +1217,9 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) { linker.SetLinkType(linker.linkItemInst); linker.SetItemInst(inst); - auto item_link = linker.GenerateLink(); + auto item_link = linker.GenerateLink(); - client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, item_link.c_str()); + client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, item_link.c_str()); if (!IsPlayerCorpse()) { Group *g = client->GetGroup(); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 00f16c2a6..badc059e0 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -882,28 +882,64 @@ bool Client::PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client void Client::PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data) { Log.Out(Logs::Detail, Logs::Inventory, "Putting loot item %s (%d) into slot %d", inst.GetItem()->Name, inst.GetItem()->ID, slot_id); - m_inv.PutItem(slot_id, inst); - SendLootItemInPacket(&inst, slot_id); + bool cursor_empty = m_inv.CursorEmpty(); if (slot_id == MainCursor) { + m_inv.PushCursor(inst); auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(this->CharacterID(), s, e); } else { + m_inv.PutItem(slot_id, inst); database.SaveInventory(this->CharacterID(), &inst, slot_id); } - if(bag_item_data) { // bag contents - int16 interior_slot; - // our bag went into slot_id, now let's pack the contents in - for(int i = SUB_BEGIN; i < EmuConstants::ITEM_CONTAINER_SIZE; i++) { - if(bag_item_data[i] == nullptr) + // Subordinate items in cursor buffer must be sent via ItemPacketSummonItem or we just overwrite the visible cursor and desync the client + if (slot_id == MainCursor && !cursor_empty) { + // RoF+ currently has a specialized cursor handler + if (GetClientVersion() < ClientVersion::RoF) + SendItemPacket(slot_id, &inst, ItemPacketSummonItem); + } + else { + SendLootItemInPacket(&inst, slot_id); + } + + if (bag_item_data) { + for (int index = 0; index < EmuConstants::ITEM_CONTAINER_SIZE; ++index) { + if (bag_item_data[index] == nullptr) continue; - const ItemInst *bagitem = database.CreateItem(bag_item_data[i]->item_id, bag_item_data[i]->charges, bag_item_data[i]->aug_1, bag_item_data[i]->aug_2, bag_item_data[i]->aug_3, bag_item_data[i]->aug_4, bag_item_data[i]->aug_5, bag_item_data[i]->aug_6, bag_item_data[i]->attuned); - interior_slot = Inventory::CalcSlotId(slot_id, i); - Log.Out(Logs::Detail, Logs::Inventory, "Putting bag loot item %s (%d) into slot %d (bag slot %d)", inst.GetItem()->Name, inst.GetItem()->ID, interior_slot, i); - PutLootInInventory(interior_slot, *bagitem); + + const ItemInst *bagitem = database.CreateItem( + bag_item_data[index]->item_id, + bag_item_data[index]->charges, + bag_item_data[index]->aug_1, + bag_item_data[index]->aug_2, + bag_item_data[index]->aug_3, + bag_item_data[index]->aug_4, + bag_item_data[index]->aug_5, + bag_item_data[index]->aug_6, + bag_item_data[index]->attuned + ); + + // Dump bag contents to cursor in the event that owning bag is not the first cursor item + // (This assumes that the data passed is correctly associated..no safety checks are implemented) + if (slot_id == MainCursor && !cursor_empty) { + Log.Out(Logs::Detail, Logs::Inventory, + "Putting bag loot item %s (%d) into slot %d (non-empty cursor override)", + inst.GetItem()->Name, inst.GetItem()->ID, MainCursor); + + PutLootInInventory(MainCursor, *bagitem); + } + else { + auto bag_slot = Inventory::CalcSlotId(slot_id, index); + + Log.Out(Logs::Detail, Logs::Inventory, + "Putting bag loot item %s (%d) into slot %d (bag slot %d)", + inst.GetItem()->Name, inst.GetItem()->ID, bag_slot, index); + + PutLootInInventory(bag_slot, *bagitem); + } safe_delete(bagitem); } } From 0210d6f6bf95ed9ae6dbb043013d34420800f2e7 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 4 Mar 2015 02:40:49 -0600 Subject: [PATCH 10/11] Fix Spell Book Deletion --- changelog.txt | 3 +++ zone/client_packet.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/changelog.txt b/changelog.txt index 23138de97..1bcc628f1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/04/2015 == +Akkadius: Fix Spell Book Deletion + == 03/03/2015 == Uleat: Fix for 'Invalid Slot ID' messages. Bag slot count is now enforced during database saves to eliminate existing 'hidden' duplicated items..sorry MQ2 users... Uleat: Fix for Item loss during corpse looting and possible item loss when purchasing items from LDoN or Adventure merchants. (cursor-related) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 7fd9d4cfc..84d34d4c3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5078,6 +5078,7 @@ void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app) if (m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) { m_pp.spell_book[dss->spell_slot] = SPELLBOOK_UNKNOWN; + database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[dss->spell_slot], dss->spell_slot); dss->success = 1; } else From c96ee79b1e30ac28e36a6f2da15b251780d42675 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 6 Mar 2015 04:26:26 -0500 Subject: [PATCH 11/11] Added ';' to safe_delete_array(data) in ~BulkZoneSpawnPacket() --- zone/entity.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index b1b139473..a28a4b846 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2737,7 +2737,7 @@ void EntityList::WriteEntityIDs() BulkZoneSpawnPacket::BulkZoneSpawnPacket(Client *iSendTo, uint32 iMaxSpawnsPerPacket) { - data = 0; + data = nullptr; pSendTo = iSendTo; pMaxSpawnsPerPacket = iMaxSpawnsPerPacket; } @@ -2745,7 +2745,7 @@ BulkZoneSpawnPacket::BulkZoneSpawnPacket(Client *iSendTo, uint32 iMaxSpawnsPerPa BulkZoneSpawnPacket::~BulkZoneSpawnPacket() { SendBuffer(); - safe_delete_array(data) + safe_delete_array(data); } bool BulkZoneSpawnPacket::AddSpawn(NewSpawn_Struct *ns) @@ -2764,7 +2764,8 @@ bool BulkZoneSpawnPacket::AddSpawn(NewSpawn_Struct *ns) return false; } -void BulkZoneSpawnPacket::SendBuffer() { +void BulkZoneSpawnPacket::SendBuffer() +{ if (!data) return;