From 7133357b1a92dfc9d185e0b8df81035be49d6d01 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 2 Mar 2014 03:46:51 -0500 Subject: [PATCH 01/82] Revisions to how charm works. Optional SQL for rules --- changelog.txt | 7 +++++++ common/ruletypes.h | 2 ++ .../git/optional/2013_03_02_CharmRules.sql | 2 ++ zone/aggro.cpp | 15 +++++++++++++-- zone/spells.cpp | 19 ++++++++++++++----- 5 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 utils/sql/git/optional/2013_03_02_CharmRules.sql diff --git a/changelog.txt b/changelog.txt index 56afea293..3a1ebb0bd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/02/2014 == +Kayen: Revision to charm code to be consistent with live based on extensive personal parsing. +*Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA +*Charisma DOES NOT extend charm durations. + +Optional SQL: utils/sql/git/optional/2014_03_02_CharmRules.sql + == 02/27/2014 == cavedude: Exported TrainDisc to Lua. diff --git a/common/ruletypes.h b/common/ruletypes.h index dcfeafbaf..614e12eb7 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -290,6 +290,8 @@ RULE_BOOL ( Spells, NPCIgnoreBaseImmunity, true) // Whether or not NPCs get to i RULE_REAL ( Spells, AvgSpellProcsPerMinute, 6.0) //Adjust rate for sympathetic spell procs RULE_INT ( Spells, ResistFalloff, 67) //Max that level that will adjust our resist chance based on level modifiers RULE_INT ( Spells, CharismaEffectiveness, 10) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. +RULE_INT ( Spells, CharismaEffectivenessCap, 200) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. +RULE_BOOL ( Spells, CharismaCharmDuration, false) // Allow CHA resist mod to extend charm duration. RULE_INT ( Spells, CharmBreakCheckChance, 25) //Determines chance for a charm break check to occur each buff tick. RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time can be reduced by spell haste RULE_INT ( Spells, RootBreakFromSpells, 20) //Chance for root to break when cast on. diff --git a/utils/sql/git/optional/2013_03_02_CharmRules.sql b/utils/sql/git/optional/2013_03_02_CharmRules.sql new file mode 100644 index 000000000..9780f8594 --- /dev/null +++ b/utils/sql/git/optional/2013_03_02_CharmRules.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:CharismaCharmDuration', 'false', 'Allow CHA to extend charm duration.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:CharismaResistCap', '200', 'Maximium amount of CHA that will effect charm resist rate.'); \ No newline at end of file diff --git a/zone/aggro.cpp b/zone/aggro.cpp index d9c50a430..4e40e7e24 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1388,13 +1388,24 @@ void Mob::ClearFeignMemory() { bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { + /* + Charm formula is correct based on over 50 hours of personal live parsing - Kayen + Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA + Charisma DOES NOT extend charm durations. + Base effect value of charm spells in the spell file DOES NOT effect duration OR resist rate (unclear if does anything) + */ + if(!caster) return false; if(spells[spell_id].ResistDiff <= -600) return true; - //Applies additional Charisma bonus to resist rate - float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,0,0,1); + float resist_check = 0; + + if (RuleB(Spells, CharismaCharmDuration)) + resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,0,0,true); + else + resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster); if(IsCharmSpell(spell_id)) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 855b292d5..e487f1f93 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3417,7 +3417,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r // not all unresistable, so changing this to only check certain spells if(IsResistableSpell(spell_id)) { - spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust); + if (IsCharmSpell(spell_id)) + spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust,true); + else + spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust); + if(spell_effectiveness < 100) { if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) ) @@ -4245,10 +4249,15 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if (CharismaCheck) { - //For charm chance to break checks, Default 10 CHA = -1 resist mod. - int16 cha_resist_modifier = 0; - cha_resist_modifier = caster->GetCHA()/RuleI(Spells, CharismaEffectiveness); - resist_modifier -= cha_resist_modifier; + //Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA + //Charisma DOES NOT extend charm durations. + int16 charisma = caster->GetCHA(); + + if (charisma > RuleI(Spells, CharismaEffectivenessCap)) + charisma = RuleI(Spells, CharismaEffectivenessCap); + + resist_modifier -= charisma/RuleI(Spells, CharismaEffectiveness); + Shout("Resist MOD = %i", resist_modifier); } //Add our level, resist and -spell resist modifier to our roll chance From 39914c8eb41c2bf58920a1fd33ecb0b523172108 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 2 Mar 2014 04:24:51 -0500 Subject: [PATCH 02/82] debug msg removed --- zone/spells.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index e487f1f93..c623a35c8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4257,7 +4257,6 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use charisma = RuleI(Spells, CharismaEffectivenessCap); resist_modifier -= charisma/RuleI(Spells, CharismaEffectiveness); - Shout("Resist MOD = %i", resist_modifier); } //Add our level, resist and -spell resist modifier to our roll chance From f4b30c5861050c66ca10a8634af3ddc09f98be5c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 2 Mar 2014 17:36:18 -0500 Subject: [PATCH 03/82] Fix for SQL --- utils/sql/git/required/2014_02_02_SpellCriticalsAA.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/sql/git/required/2014_02_02_SpellCriticalsAA.sql b/utils/sql/git/required/2014_02_02_SpellCriticalsAA.sql index aa83e8ebc..33244febc 100644 --- a/utils/sql/git/required/2014_02_02_SpellCriticalsAA.sql +++ b/utils/sql/git/required/2014_02_02_SpellCriticalsAA.sql @@ -7,8 +7,8 @@ REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES UPDATE aa_effects SET base2 = 100 WHERE effectid = 294 AND base2 = 0; -- Consumption of Soul (Need live values - These are what had hard coded, but using spell effect) -REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('589', '1', '303', '200', '0'); -REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('589', '2', '139', '2766', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('559', '1', '303', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('559', '2', '139', '2766', '0'); REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('560', '1', '303', '400', '0'); REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('560', '2', '139', '2766', '0'); REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('561', '1', '303', '600', '0'); From 84c85a46057222d2bdebe15ffec45358e65b3b00 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 2 Mar 2014 19:44:42 -0500 Subject: [PATCH 04/82] Added lower bounds for chance of charm fading. Parsed on live to be approximately no lower than 5% regardless of level difference. --- zone/aggro.cpp | 15 +++++++++------ zone/mob.h | 2 +- zone/spells.cpp | 6 +++++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 4e40e7e24..07a8569a6 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1393,6 +1393,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA Charisma DOES NOT extend charm durations. Base effect value of charm spells in the spell file DOES NOT effect duration OR resist rate (unclear if does anything) + Charm has a lower limit of 5% chance to break per tick, regardless of resist modifiers / level difference. */ if(!caster) return false; @@ -1402,11 +1403,6 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { float resist_check = 0; - if (RuleB(Spells, CharismaCharmDuration)) - resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,0,0,true); - else - resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster); - if(IsCharmSpell(spell_id)) { if (spells[spell_id].powerful_flag == -1) //If charm spell has this set(-1), it can not break till end of duration. @@ -1416,8 +1412,13 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { if (MakeRandomInt(0, 99) > RuleI(Spells, CharmBreakCheckChance)) return true; + if (RuleB(Spells, CharismaCharmDuration)) + resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,0,0,true,true); + else + resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0, false, true); + //2: The mob makes a resistance check against the charm - if (resist_check == 100) + if (resist_check == 100) return true; else @@ -1437,6 +1438,8 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { else { // Assume this is a harmony/pacify spell + resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster); + if (resist_check == 100) return true; } diff --git a/zone/mob.h b/zone/mob.h index ca80500d5..20da2be3e 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -190,7 +190,7 @@ public: virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration){ return duration;} virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false, - int resist_override = 0, bool CharismaCheck = false); + int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false); uint16 GetSpecializeSkillValue(uint16 spell_id) const; void SendSpellBarDisable(); void SendSpellBarEnable(uint16 spellid); diff --git a/zone/spells.cpp b/zone/spells.cpp index c623a35c8..0d110a36e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4048,7 +4048,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) // pvp_resist_base // pvp_resist_calc // pvp_resist_cap -float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck) +float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick) { if(!caster) @@ -4277,6 +4277,10 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use resist_chance = spells[spell_id].MinResist; } + //Charm can not have less than 5% chance to fail. + if (CharmTick && (resist_chance < 10)) + resist_chance = 10; + //Finally our roll int roll = MakeRandomInt(0, 200); if(roll > resist_chance) From 00068158c1beea0ece4a257ca6816dcfd562e30f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 2 Mar 2014 22:10:32 -0500 Subject: [PATCH 05/82] Melee Crits, HoTs, DoTs messages should now filter correctly Clients that also support seeing others DoTs will now see them if they don't filter them. Note: HoTs 'mine only' doesn't seem to be different from 'show' --- changelog.txt | 3 ++ zone/StringIDs.h | 6 +++- zone/attack.cpp | 78 ++++++++++++++++++++++++++++++++---------- zone/client.cpp | 54 ++++++++++++++++++++++++++--- zone/mob.h | 2 +- zone/spell_effects.cpp | 2 +- 6 files changed, 118 insertions(+), 27 deletions(-) diff --git a/changelog.txt b/changelog.txt index 3a1ebb0bd..e5695ca13 100644 --- a/changelog.txt +++ b/changelog.txt @@ -7,6 +7,9 @@ Kayen: Revision to charm code to be consistent with live based on extensive pers Optional SQL: utils/sql/git/optional/2014_03_02_CharmRules.sql +demonstar55: Melee Crits, HoTs, DoTs messages should now be filtered correctly on all clients. + Clients that also support seeing others DoTs will now see them if they don't filter them + note: some newer clients have a 'mine only' option for HoTs but it functions the same as show == 02/27/2014 == cavedude: Exported TrainDisc to Lua. diff --git a/zone/StringIDs.h b/zone/StringIDs.h index 2cc076b59..c1ead87e6 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -290,7 +290,7 @@ #define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining) #define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell. #define YOU_HEAL 9068 //You have healed %1 for %2 points of damage. -#define OTHER_HIT_DOT 9072 //%1 has taken %2 damage from your %3. +#define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3. #define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage. #define SHAKE_OFF_STUN 9077 #define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses! @@ -353,10 +353,14 @@ #define NOW_INVISIBLE 12950 //%1 is now Invisible. #define NOW_VISIBLE 12951 //%1 is now Visible. #define GUILD_NOT_MEMBER2 12966 //You are not in a guild. +#define HOT_HEAL_SELF 12976 //You have been healed for %1 hit points by your %2. +#define HOT_HEAL_OTHER 12997 //You have healed %1 for %2 hit points with your %3. +#define HOT_HEALED_OTHER 12998 //%1 healed you for %2 hit points by %3. #define DISC_LEVEL_USE_ERROR 13004 //You are not sufficient level to use this discipline. #define TOGGLE_ON 13172 //Asking server to turn ON your incoming tells. #define TOGGLE_OFF 13173 //Asking server to turn OFF all incoming tells for you. #define DUEL_INPROGRESS 13251 //You have already accepted a duel with someone else cowardly dog. +#define OTHER_HIT_DOT 13327 //%1 has taken %2 damage from %3 by %4. #define GENERIC_MISS 15041 //%1 missed %2 #endif diff --git a/zone/attack.cpp b/zone/attack.cpp index 3701e249d..aad79032b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3829,49 +3829,83 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons // Everhood - So we can see our dot dmg like live shows it. if(spell_id != SPELL_UNKNOWN && damage > 0 && attacker && attacker != this && attacker->IsClient()) { //might filter on (attack_skill>200 && attack_skill<250), but I dont think we need it - if(attacker->CastToClient()->GetFilter(FilterDOT) != FilterHide) { - attacker->Message_StringID(MT_DoTDamage, OTHER_HIT_DOT, GetCleanName(),itoa(damage),spells[spell_id].name); - } + attacker->FilteredMessage_StringID(attacker, MT_DoTDamage, FilterDOT, + YOUR_HIT_DOT, GetCleanName(), itoa(damage), spells[spell_id].name); + // older clients don't have the below String ID, but it will be filtered + entity_list.FilteredMessageClose_StringID(attacker, true, 200, + MT_DoTDamage, FilterDOT, OTHER_HIT_DOT, GetCleanName(), + itoa(damage), attacker->GetCleanName(), spells[spell_id].name); } } //end packet sending } -void Mob::HealDamage(uint32 amount, Mob* caster) { +void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id) +{ int32 maxhp = GetMaxHP(); int32 curhp = GetHP(); uint32 acthealed = 0; - if(caster && amount > 0) - { - if(caster->IsNPC() && !caster->IsPet()) - { + if (caster && amount > 0) { + if (caster->IsNPC() && !caster->IsPet()) { float npchealscale = caster->CastToNPC()->GetHealScale(); - amount = ((float)amount * npchealscale) / (float)100; + amount = (static_cast(amount) * npchealscale) / 100.0f; } } - if(amount > (maxhp - curhp)) + if (amount > (maxhp - curhp)) acthealed = (maxhp - curhp); else acthealed = amount; if (acthealed > 100) { if (caster) { - Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), itoa(acthealed)); - if (caster != this) - caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), itoa(acthealed)); + if (IsBuffSpell(spell_id)) { // hots + // message to caster + if (caster->IsClient() && caster == this) { + if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater) + FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, + HOT_HEAL_SELF, itoa(acthealed), spells[spell_id].name); + else + FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, + YOU_HEALED, GetCleanName(), itoa(acthealed)); + } else if (caster->IsClient() && caster != this) { + if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater) + caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, + HOT_HEAL_OTHER, GetCleanName(), itoa(acthealed), + spells[spell_id].name); + else + caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, + YOU_HEAL, GetCleanName(), itoa(acthealed)); + } + // message to target + if (IsClient() && caster != this) { + if (CastToClient()->GetClientVersionBit() & BIT_SoFAndLater) + FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime, + HOT_HEALED_OTHER, caster->GetCleanName(), + itoa(acthealed), spells[spell_id].name); + else + FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime, + YOU_HEALED, caster->GetCleanName(), itoa(acthealed)); + } + } else { // normal heals + FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage, + YOU_HEALED, caster->GetCleanName(), itoa(acthealed)); + if (caster != this) + caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage, + YOU_HEAL, GetCleanName(), itoa(acthealed)); + } } else { Message(MT_NonMelee, "You have been healed for %d points of damage.", acthealed); } } if (curhp < maxhp) { - if ((curhp+amount)>maxhp) - curhp=maxhp; + if ((curhp + amount) > maxhp) + curhp = maxhp; else - curhp+=amount; + curhp += amount; SetHP(curhp); SendHPUpdate(); @@ -4232,7 +4266,9 @@ void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) { critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100 damage = (damage * critMod) / 100; - entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRITICAL_HIT, GetCleanName(), itoa(damage)); + entity_list.FilteredMessageClose_StringID(this, false, 200, + MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT, + GetCleanName(), itoa(damage)); } } } @@ -4348,7 +4384,9 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack damage = damage * critMod / 100; if (crip_success) { - entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRIPPLING_BLOW, GetCleanName(), itoa(damage)); + entity_list.FilteredMessageClose_StringID(this, false, 200, + MT_CritMelee, FilterMeleeCrits, CRIPPLING_BLOW, + GetCleanName(), itoa(damage)); // Crippling blows also have a chance to stun //Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a staggers message. if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(IMMUNE_STUN)){ @@ -4356,7 +4394,9 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack defender->Stun(0); } } else { - entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRITICAL_HIT, GetCleanName(), itoa(damage)); + entity_list.FilteredMessageClose_StringID(this, false, 200, + MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT, + GetCleanName(), itoa(damage)); } } } diff --git a/zone/client.cpp b/zone/client.cpp index cc79233ce..e8af229ae 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2852,7 +2852,12 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){ else ClientFilters[FilterSpellCrits] = FilterHide; - Filter1(FilterMeleeCrits); + if (filter->filters[FilterMeleeCrits] == 0) + ClientFilters[FilterMeleeCrits] = FilterShow; + else if (filter->filters[FilterMeleeCrits] == 1) + ClientFilters[FilterMeleeCrits] = FilterShowSelfOnly; + else + ClientFilters[FilterMeleeCrits] = FilterHide; if(filter->filters[FilterSpellDamage] == 0) ClientFilters[FilterSpellDamage] = FilterShow; @@ -2866,12 +2871,41 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){ Filter0(FilterOthersHit); Filter0(FilterMissedMe); Filter1(FilterDamageShields); - Filter1(FilterDOT); + + if (GetClientVersionBit() & BIT_SoDAndLater) { + if (filter->filters[FilterDOT] == 0) + ClientFilters[FilterDOT] = FilterShow; + else if (filter->filters[FilterDOT] == 1) + ClientFilters[FilterDOT] = FilterShowSelfOnly; + else if (filter->filters[FilterDOT] == 2) + ClientFilters[FilterDOT] = FilterShowGroupOnly; + else + ClientFilters[FilterDOT] = FilterHide; + } else { + if (filter->filters[FilterDOT] == 0) // show functions as self only + ClientFilters[FilterDOT] = FilterShowSelfOnly; + else + ClientFilters[FilterDOT] = FilterHide; + } + Filter1(FilterPetHits); Filter1(FilterPetMisses); Filter1(FilterFocusEffects); Filter1(FilterPetSpells); - Filter1(FilterHealOverTime); + + if (GetClientVersionBit() & BIT_SoDAndLater) { + if (filter->filters[FilterHealOverTime] == 0) + ClientFilters[FilterHealOverTime] = FilterShow; + // This is called 'Show Mine Only' in the clients, but functions the same as show + // so instead of apply special logic, just set to show + else if (filter->filters[FilterHealOverTime] == 1) + ClientFilters[FilterHealOverTime] = FilterShow; + else + ClientFilters[FilterHealOverTime] = FilterHide; + } else { + // these clients don't have a 'self only' filter + Filter1(FilterHealOverTime); + } } // this version is for messages with no parameters @@ -2983,8 +3017,18 @@ bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter) return false; } else if (mode == FilterShowGroupOnly) { Group *g = GetGroup(); - if (!g || !g->IsGroupMember(sender)) + Raid *r = GetRaid(); + if (g) { + if (g->IsGroupMember(sender)) + return true; + } else if (r && sender->IsClient()) { + uint32 rgid1 = r->GetGroup(this); + uint32 rgid2 = r->GetGroup(sender->CastToClient()); + if (rgid1 != 0xFFFFFFFF && rgid1 == rgid2) + return true; + } else { return false; + } } } @@ -3027,7 +3071,7 @@ void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType fil type = 4; if (!message1) { - Message_StringID(type, string_id); // use the simple message instead + FilteredMessage_StringID(sender, type, filter, string_id); // use the simple message instead return; } diff --git a/zone/mob.h b/zone/mob.h index 20da2be3e..c9f1aa299 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -297,7 +297,7 @@ public: bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false); inline void SetOOCRegen(int32 newoocregen) {oocregen = newoocregen;} virtual void Heal(); - virtual void HealDamage(uint32 ammount, Mob* caster = nullptr); + virtual void HealDamage(uint32 ammount, Mob* caster = nullptr, uint16 spell_id = SPELL_UNKNOWN); virtual void SetMaxHP() { cur_hp = max_hp; } virtual inline uint16 GetBaseRace() const { return base_race; } virtual inline uint8 GetBaseGender() const { return base_gender; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index bd584ea6e..2f73b915c 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3230,7 +3230,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste if(caster) effect_value = caster->GetActSpellHealing(spell_id, effect_value); - HealDamage(effect_value, caster); + HealDamage(effect_value, caster, spell_id); //healing aggro would go here; removed for now break; } From a67aed9538b97a7aed966473820bf3878d8dcb41 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 3 Mar 2014 14:59:39 -0500 Subject: [PATCH 06/82] Implemented deadly strikes and increase rogue throwing crit rate New rules: Combat:RogueCritThrowingChance, Combat:RogueDeadlyStrikeChance, Combat:RogueDeadlyStrikeMod Rogue throwing crit rate is rather ridiculous. These rules might need tweaking but they gave me fairly live-like results, even with AA added --- changelog.txt | 6 ++++++ common/ruletypes.h | 3 +++ zone/StringIDs.h | 1 + zone/attack.cpp | 21 +++++++++++++++++++-- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index e5695ca13..ab4ef1edd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/03/2014 == +demonstar55: Implemented deadly strikes and gave rogues higher innate throwing crit chance + +New rules: Combat:RogueCritThrowingChance, Combat:RogueDeadlyStrikeChance, Combat:RogueDeadlyStrikeMod + Defaults should give fairly close to live results + == 03/02/2014 == Kayen: Revision to charm code to be consistent with live based on extensive personal parsing. *Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA diff --git a/common/ruletypes.h b/common/ruletypes.h index 614e12eb7..3b1e5ca44 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -308,6 +308,9 @@ RULE_INT ( Combat, WarBerBaseCritChance, 3 ) //The base crit chance for warriors RULE_INT ( Combat, BerserkBaseCritChance, 6 ) //The bonus base crit chance you get when you're berserk RULE_INT ( Combat, NPCBashKickLevel, 6 ) //The level that npcs can KICK/BASH RULE_INT ( Combat, NPCBashKickStunChance, 15 ) //Percent chance that a bash/kick will stun +RULE_INT ( Combat, RogueCritThrowingChance, 25) //Rogue throwing crit bonus +RULE_INT ( Combat, RogueDeadlyStrikeChance, 80) //Rogue chance throwing from behind crit becomes a deadly strike +RULE_INT ( Combat, RogueDeadlyStrikeMod, 2) //Deadly strike modifier to crit damage RULE_INT ( Combat, ClientBaseCritChance, 0 ) //The base crit chance for all clients, this will stack with warrior's/zerker's crit chance. RULE_BOOL ( Combat, UseIntervalAC, true) RULE_INT ( Combat, PetAttackMagicLevel, 30) diff --git a/zone/StringIDs.h b/zone/StringIDs.h index c1ead87e6..4fb04539b 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -160,6 +160,7 @@ #define ASSASSINATES 1016 //%1 ASSASSINATES their victim!! #define CRIPPLING_BLOW 1021 //%1 lands a Crippling Blow!(%2) #define CRITICAL_HIT 1023 //%1 scores a critical hit! (%2) +#define DEADLY_STRIKE 1024 //%1 scores a Deadly Strike!(%2) #define RESISTS_URGE 1025 //%1 resists their urge to flee. #define BERSERK_START 1027 //%1 goes into a berserker frenzy! #define DEATH_PACT 1028 //%1's death pact has been benevolently fulfilled! diff --git a/zone/attack.cpp b/zone/attack.cpp index aad79032b..3316c3eb3 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4333,11 +4333,16 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack } } + int deadlyChance = 0; + int deadlyMod = 0; if(skill == SkillArchery && GetClass() == RANGER && GetSkill(SkillArchery) >= 65) critChance += 6; - if(skill == SkillThrowing && GetClass() == ROGUE && GetSkill(SkillThrowing) >= 65) - critChance += 6; + if (skill == SkillThrowing && GetClass() == ROGUE && GetSkill(SkillThrowing) >= 65) { + critChance += RuleI(Combat, RogueCritThrowingChance); + deadlyChance = RuleI(Combat, RogueDeadlyStrikeChance); + deadlyMod = RuleI(Combat, RogueDeadlyStrikeMod); + } int CritChanceBonus = GetCriticalChanceBonus(skill); @@ -4383,6 +4388,14 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100 damage = damage * critMod / 100; + bool deadlySuccess = false; + if (deadlyChance && MakeRandomFloat(0, 1) < static_cast(deadlyChance) / 100.0f) { + if (BehindMob(defender, GetX(), GetY())) { + damage *= deadlyMod; + deadlySuccess = true; + } + } + if (crip_success) { entity_list.FilteredMessageClose_StringID(this, false, 200, MT_CritMelee, FilterMeleeCrits, CRIPPLING_BLOW, @@ -4393,6 +4406,10 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack defender->Emote("staggers."); defender->Stun(0); } + } else if (deadlySuccess) { + entity_list.FilteredMessageClose_StringID(this, false, 200, + MT_CritMelee, FilterMeleeCrits, DEADLY_STRIKE, + GetCleanName(), itoa(damage)); } else { entity_list.FilteredMessageClose_StringID(this, false, 200, MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT, From 057e4603db7c1768ed2074ac6aca27c1e1938441 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 3 Mar 2014 15:01:52 -0500 Subject: [PATCH 07/82] Optimized EntityList::AddHealAggro --- zone/entity.cpp | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index c42ebc685..f5533cb84 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3016,6 +3016,7 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam) { NPC *cur = nullptr; uint16 count = 0; + std::list npc_sub_list; auto it = npc_list.begin(); while (it != npc_list.end()) { cur = it->second; @@ -3025,11 +3026,13 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam) continue; } if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) { + npc_sub_list.push_back(cur); ++count; } ++it; } + if (thedam > 1) { if (count > 0) thedam /= count; @@ -3039,32 +3042,26 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam) } cur = nullptr; - it = npc_list.begin(); - while (it != npc_list.end()) { - cur = it->second; - if (!cur->CheckAggro(target)) { - ++it; - continue; - } + auto sit = npc_sub_list.begin(); + while (sit != npc_sub_list.end()) { + cur = *sit; - if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) { - if (cur->IsPet()) { - if (caster) { - if (cur->CheckAggro(caster)) { - cur->AddToHateList(caster, thedam); - } + if (cur->IsPet()) { + if (caster) { + if (cur->CheckAggro(caster)) { + cur->AddToHateList(caster, thedam); } - } else { - if (caster) { - if (cur->CheckAggro(caster)) { - cur->AddToHateList(caster, thedam); - } else { - cur->AddToHateList(caster, thedam * 0.33); - } + } + } else { + if (caster) { + if (cur->CheckAggro(caster)) { + cur->AddToHateList(caster, thedam); + } else { + cur->AddToHateList(caster, thedam * 0.33); } } } - ++it; + ++sit; } } From ea677389ad79698d822100c53ebd03f1de2d760f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 4 Mar 2014 06:42:41 -0500 Subject: [PATCH 08/82] Root spell revision Optional SQL --- changelog.txt | 9 ++++++ common/ruletypes.h | 1 + common/spdat.cpp | 3 +- zone/attack.cpp | 69 ++++++++++++++++++++++++++++-------------- zone/mob.h | 1 + zone/spell_effects.cpp | 20 ++++++++++-- 6 files changed, 75 insertions(+), 28 deletions(-) diff --git a/changelog.txt b/changelog.txt index ab4ef1edd..1d46228f5 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,14 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/04/2014 == +Kayen: Revision to root code to be consistent with live based on extensive personal parsing. +*ROOT has a 40% chance to do a resist check to break each buff tick. +*If multiple roots on target. Root in first slot will always and only be check to break from nukes. +*If multiple roots on target and broken by spell. Roots are removed ONE at a time in order of buff slot. +*Roots on beneficial spells can not be broken by spell damage. + +Optional SQL: utils/sql/git/optional/2014_03_04_RootRule.sql + == 03/03/2014 == demonstar55: Implemented deadly strikes and gave rogues higher innate throwing crit chance diff --git a/common/ruletypes.h b/common/ruletypes.h index 3b1e5ca44..83324d533 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -300,6 +300,7 @@ RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amou RULE_BOOL ( Spells, AdditiveBonusValues, false) //Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects) RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this? RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live +RULE_INT ( Spells, RootBreakCheckChance, 40) //Determines chance for a root break check to occur each buff tick. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/common/spdat.cpp b/common/spdat.cpp index 04af27424..0f4387b83 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -390,8 +390,7 @@ bool IsPartialCapableSpell(uint16 spell_id) if (spells[spell_id].no_partial_resist) return false; - if (IsPureNukeSpell(spell_id) || IsFearSpell(spell_id) || - IsEffectInSpell(spell_id, SE_Root)) + if (IsPureNukeSpell(spell_id) || IsFearSpell(spell_id)) return true; return false; diff --git a/zone/attack.cpp b/zone/attack.cpp index 3316c3eb3..594961c7c 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3685,29 +3685,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons if(spell_id != SPELL_UNKNOWN && !iBuffTic) { //see if root will break - if (IsRooted() && !FromDamageShield) { // neotoyko: only spells cancel root - - /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 - The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. - There is no distinction of any kind between the caster inflicted damage, or anyone - else's damage. There is also no distinction between Direct and DOT damage in the root code. - There is however, a provision that if the damage inflicted is greater than 500 per hit, the - chance to break root is increased. My guess is when this code was put in place, the devs at - the time couldn't imagine DOT damage getting that high. - */ - int BreakChance = RuleI(Spells, RootBreakFromSpells); - BreakChance -= BreakChance*rooted_mod/100; - - if (BreakChance < 1) - BreakChance = 1; - - if (MakeRandomInt(0, 99) < BreakChance) { - mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance"); - BuffFadeByEffect(SE_Root, buffslot); // buff slot is passed through so a root w/ dam doesnt cancel itself - } else { - mlog(COMBAT__HITS, "Spell did not break root. BreakChance percent chance"); - } - } + if (IsRooted() && !FromDamageShield) // neotoyko: only spells cancel root + TryRootFadeByDamage(buffslot); } else if(spell_id == SPELL_UNKNOWN) { @@ -4575,6 +4554,50 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, float chance) } } +bool Mob::TryRootFadeByDamage(int buffslot) +{ + /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 + The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. + There is no distinction of any kind between the caster inflicted damage, or anyone + else's damage. There is also no distinction between Direct and DOT damage in the root code. + There is however, a provision that if the damage inflicted is greater than 500 per hit, the + chance to break root is increased. My guess is when this code was put in place, the devs at + the time couldn't imagine DOT damage getting that high. + */ + + int BreakChance = RuleI(Spells, RootBreakFromSpells); + + BreakChance -= BreakChance*rooted_mod/100; + + if (BreakChance < 1) + BreakChance = 1; + + if (MakeRandomInt(0, 99) < BreakChance) { + + /* + - Check buffslot to make sure damage from a root does not cancel the root + - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. + - Only roots on determental spells can be broken by damage. + */ + + uint32 buff_count = GetMaxTotalSlots(); + for(int i = 0; i < buff_count; i++) + { + if(IsValidSpell(buffs[i].spellid) && + (IsEffectInSpell(buffs[i].spellid, SE_Root) && IsDetrimentalSpell(buffs[i].spellid) && i != buffslot)){ + if (!TryFadeEffect(i)) { + BuffFadeBySlot(i); + mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance"); + return true; + } + } + } + } + + mlog(COMBAT__HITS, "Spell did not break root. BreakChance percent chance"); + return false; +} + int32 Mob::RuneAbsorb(int32 damage, uint16 type) { uint32 buff_max = GetMaxTotalSlots(); diff --git a/zone/mob.h b/zone/mob.h index c9f1aa299..723f868cf 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -600,6 +600,7 @@ public: void MeleeLifeTap(int32 damage); bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true); bool ImprovedTaunt(); + bool TryRootFadeByDamage(int buffslot); void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2f73b915c..f01476779 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3322,9 +3322,23 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste } case SE_Root: { - float SpellEffectiveness = ResistSpell(spells[spell_id].resisttype, spell_id, caster); - if(SpellEffectiveness < 25) { - BuffFadeByEffect(SE_Root); + + /* Root formula derived from extensive personal live parses - Kayen + ROOT has a 40% chance to do a resist check to break. + Resist check has NO LOWER bounds. + If multiple roots on target. Root in first slot will be checked first to break from nukes. + If multiple roots on target and broken by spell. Roots are removed ONE at a time in order of buff slot. + */ + + if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){ + + float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster); + + if(resist_check == 100) + break; + else + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); } break; From 0ef95d8fe69af318eef3129284a954e6044f6184 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 4 Mar 2014 06:43:57 -0500 Subject: [PATCH 09/82] Root rule optional SQL --- utils/sql/git/optional/2013_03_04_RootRule.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 utils/sql/git/optional/2013_03_04_RootRule.sql diff --git a/utils/sql/git/optional/2013_03_04_RootRule.sql b/utils/sql/git/optional/2013_03_04_RootRule.sql new file mode 100644 index 000000000..b034a0374 --- /dev/null +++ b/utils/sql/git/optional/2013_03_04_RootRule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:RootBreakCheckChance', '40', 'Chance for root to do a resist check each tick. Decrease for longer roots.'); From 58f42f1af1f10162b13f96cea6c60dd714080d03 Mon Sep 17 00:00:00 2001 From: sorvani Date: Tue, 4 Mar 2014 22:39:52 -0600 Subject: [PATCH 10/82] Created RemoveFromInstance and RemoveAllFromInstance and exported to lua_general.cpp --- changelog.txt | 4 ++++ zone/lua_general.cpp | 10 ++++++++++ zone/questmgr.cpp | 37 +++++++++++++++++++++++++++++++++++++ zone/questmgr.h | 4 ++++ 4 files changed, 55 insertions(+) diff --git a/changelog.txt b/changelog.txt index ab4ef1edd..1995e8440 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/04/2014 == +Sorvani: Created RemoveFromInstance and RemoveAllFromInstance to remove a single player or all players in an instance. +Sorvani: Exported to Lua as eq.remove_from_instance(instance_id) and eq.remove_all_from_instance(instance_id). + == 03/03/2014 == demonstar55: Implemented deadly strikes and gave rogues higher innate throwing crit chance diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 7b275ea67..cca71641e 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -659,6 +659,14 @@ void lua_assign_raid_to_instance(uint32 instance_id) { quest_manager.AssignRaidToInstance(instance_id); } +void lua_remove_from_instance(uint32 instance_id) { + quest_manager.RemoveFromInstance(instance_id); +} + +void lua_remove_all_from_instance(uint32 instance_id) { + quest_manager.RemoveAllFromInstance(instance_id); +} + void lua_flag_instance_by_group_leader(uint32 zone, uint32 version) { quest_manager.FlagInstanceByGroupLeader(zone, version); } @@ -1195,6 +1203,8 @@ luabind::scope lua_register_general() { luabind::def("assign_to_instance", &lua_assign_to_instance), luabind::def("assign_group_to_instance", &lua_assign_group_to_instance), luabind::def("assign_raid_to_instance", &lua_assign_raid_to_instance), + luabind::def("remove_from_instance", &lua_remove_from_instance), + luabind::def("remove_all_from_instance", &lua_remove_all_from_instance), luabind::def("flag_instance_by_group_leader", &lua_flag_instance_by_group_leader), luabind::def("flag_instance_by_raid_leader", &lua_flag_instance_by_raid_leader), luabind::def("fly_mode", &lua_fly_mode), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 484a63d40..5e12d7b32 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2557,6 +2557,43 @@ void QuestManager::AssignRaidToInstance(uint16 instance_id) } } +void QuestManager::RemoveFromInstance(uint16 instance_id) +{ + QuestManagerCurrentQuestVars(); + if(initiator) { + if(database.RemoveClientFromInstance(instance_id, initiator->CharacterID())) { + initiator->Message(0, "Removed client from instance."); + } else { + initiator->Message(0, "Failed to remove client from instance."); + } + } +} + +void QuestManager::RemoveAllFromInstance(uint16 instance_id) +{ + QuestManagerCurrentQuestVars(); + if(initiator) { + std::list charid_list; + boolean removed_all = true; + uint16 fail_count = 0; + database.GetCharactersInInstance(instance_id,charid_list); + auto iter = charid_list.begin(); + while(iter != charid_list.end()) { + if(!database.RemoveClientFromInstance(instance_id, *iter)) { + removed_all = false; + ++fail_count; + } + ++iter; + } + if (removed_all) { + initiator->Message(MT_Say, "Removed all players from instance."); + } else { + // once the expedition system is in, this message it not relevant + initiator->Message(MT_Say, "Failed to remove %i player(s) from instance.", fail_count); + } + } +} + void QuestManager::MovePCInstance(int zone_id, int instance_id, float x, float y, float z, float heading) { QuestManagerCurrentQuestVars(); diff --git a/zone/questmgr.h b/zone/questmgr.h index 7c1960283..86568349c 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -211,6 +211,10 @@ public: void AssignToInstance(uint16 instance_id); void AssignGroupToInstance(uint16 instance_id); void AssignRaidToInstance(uint16 instance_id); + void RemoveFromInstance(uint16 instance_id); + //void RemoveGroupFromInstance(uint16 instance_id); //potentially useful but not implmented at this time. + //void RemoveRaidFromInstance(uint16 instance_id); //potentially useful but not implmented at this time. + void RemoveAllFromInstance(uint16 instance_id); void MovePCInstance(int zone_id, int instance_id, float x, float y, float z, float heading); void FlagInstanceByGroupLeader(uint32 zone, int16 version); void FlagInstanceByRaidLeader(uint32 zone, int16 version); From 3f6036a512e5fc5dcf9da82a898eb0c67f32608e Mon Sep 17 00:00:00 2001 From: sorvani Date: Tue, 4 Mar 2014 23:11:33 -0600 Subject: [PATCH 11/82] missed a couple MT_Say --- zone/questmgr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 5e12d7b32..b4d18408e 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2562,9 +2562,9 @@ void QuestManager::RemoveFromInstance(uint16 instance_id) QuestManagerCurrentQuestVars(); if(initiator) { if(database.RemoveClientFromInstance(instance_id, initiator->CharacterID())) { - initiator->Message(0, "Removed client from instance."); + initiator->Message(MT_Say, "Removed client from instance."); } else { - initiator->Message(0, "Failed to remove client from instance."); + initiator->Message(MT_Say, "Failed to remove client from instance."); } } } From 05df6c5b21e343374334e4bf9a5e4704fd50aa0e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 5 Mar 2014 01:09:18 -0500 Subject: [PATCH 12/82] Fix QuestManager::RemoveAllFromInstance --- zone/questmgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index b4d18408e..3d51cf6c6 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2574,7 +2574,7 @@ void QuestManager::RemoveAllFromInstance(uint16 instance_id) QuestManagerCurrentQuestVars(); if(initiator) { std::list charid_list; - boolean removed_all = true; + bool removed_all = true; uint16 fail_count = 0; database.GetCharactersInInstance(instance_id,charid_list); auto iter = charid_list.begin(); From d70c4d7bbe82b83945cb9303c9ee0fbafe12da1a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 5 Mar 2014 01:34:17 -0500 Subject: [PATCH 13/82] Fix rogue's evade to be single target This is also a lot nice performance wise --- changelog.txt | 3 +++ zone/StringIDs.h | 6 ++++++ zone/aggro.cpp | 8 ++++++++ zone/client_packet.cpp | 18 ++++++++++-------- zone/mob.h | 1 + 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/changelog.txt b/changelog.txt index 1995e8440..814673eba 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/05/2014 == +demonstar55: Corrected rogue's evade to be single target + == 03/04/2014 == Sorvani: Created RemoveFromInstance and RemoveAllFromInstance to remove a single player or all players in an instance. Sorvani: Exported to Lua as eq.remove_from_instance(instance_id) and eq.remove_all_from_instance(instance_id). diff --git a/zone/StringIDs.h b/zone/StringIDs.h index 4fb04539b..afa598e1c 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -99,6 +99,12 @@ #define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together. #define TRADESKILL_TRIVIAL 338 //You can no longer advance your skill from making this item. #define TRADESKILL_SUCCEED 339 //You have fashioned the items together to create something new! +#define EVADE_SUCCESS 343 //You have momentarily ducked away from the main combat. +#define EVADE_FAIL 344 //Your attempts at ducking clear of combat fail. +#define HIDE_FAIL 345 //You failed to hide yourself. +#define HIDE_SUCCESS 346 //You have hidden yourself from view. +#define SNEAK_SUCCESS 347 //You are as quiet as a cat stalking its prey. +#define SNEAK_FAIL 348 //You are as quiet as a herd of running elephants. #define MEND_CRITICAL 349 //You magically mend your wounds and heal considerable damage. #define MEND_SUCCESS 350 //You mend your wounds and heal some damage. #define MEND_WORSEN 351 //You have worsened your wounds! diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 07a8569a6..612edd47a 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1447,3 +1447,11 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { return false; } +void Mob::RogueEvade(Mob *other) +{ + int amount = other->GetHateAmount(this) - (GetLevel() * 13); + other->SetHate(this, std::max(1, amount)); + + return; +} + diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f8d90f745..441681fd0 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3478,21 +3478,23 @@ void Client::Handle_OP_Hide(const EQApplicationPacket *app) } if(GetClass() == ROGUE){ EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage,sizeof(SimpleMessage_Struct)); - SimpleMessage_Struct *msg=(SimpleMessage_Struct *)outapp->pBuffer; - msg->color=0x010E; - if (!auto_attack && entity_list.Fighting(this)) { + SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; + msg->color = 0x010E; + Mob *evadetar = GetTarget(); + if (!auto_attack && (evadetar && evadetar->CheckAggro(this) + && evadetar->IsNPC())) { if (MakeRandomInt(0, 260) < (int)GetSkill(SkillHide)) { - msg->string_id=343; - entity_list.Evade(this); + msg->string_id = EVADE_SUCCESS; + RogueEvade(evadetar); } else { - msg->string_id=344; + msg->string_id = EVADE_FAIL; } } else { if (hidden){ - msg->string_id=346; + msg->string_id = HIDE_SUCCESS; } else { - msg->string_id=345; + msg->string_id = HIDE_FAIL; } } FastQueuePacket(&outapp); diff --git a/zone/mob.h b/zone/mob.h index c9f1aa299..6e76c9ce7 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -146,6 +146,7 @@ public: virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); bool CombatRange(Mob* other); virtual inline bool IsBerserk() { return false; } // only clients + void RogueEvade(Mob *other); //Appearance void SendLevelAppearance(); From 0e8f62b48068b1a9e5d60a201cfde6d6446f82d2 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 5 Mar 2014 01:52:41 -0500 Subject: [PATCH 14/82] Fixes #129 - Initialize pointer to nullptr. --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 2b9580452..1ec736ecf 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -14967,7 +14967,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - Bot* targetedBot; + Bot *targetedBot = nullptr; if(c->GetTarget() != nullptr) { if (c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner() == c)) From 822a1b100339375b948863234dcc6386db6823d4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 5 Mar 2014 23:02:57 -0500 Subject: [PATCH 15/82] Fix NPC swarm pets despawning on owner death Fix NPC swarm pets IsAttackAllowed logic --- zone/aggro.cpp | 8 ++++++++ zone/spells.cpp | 15 +++------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 612edd47a..862c91605 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -513,6 +513,14 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) else if(our_owner && our_owner == target) return false; + // invalidate for swarm pets for later on if their owner is a corpse + if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner && + our_owner->IsCorpse() && !our_owner->IsPlayerCorpse()) + our_owner = nullptr; + if (target->IsNPC() && target->CastToNPC()->GetSwarmInfo() && target_owner && + target_owner->IsCorpse() && !target_owner->IsPlayerCorpse()) + target_owner = nullptr; + //cannot hurt untargetable mobs bodyType bt = target->GetBodyType(); diff --git a/zone/spells.cpp b/zone/spells.cpp index 0d110a36e..965f96427 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -123,21 +123,12 @@ void NPC::SpellProcess() { Mob::SpellProcess(); - if(GetSwarmInfo()){ - Mob *swp_o = GetSwarmInfo()->GetOwner(); - if(!swp_o) - { + if (GetSwarmInfo()) { + if (GetSwarmInfo()->duration->Check(false)) Depop(); - } - - if(GetSwarmInfo()->duration->Check(false)) - { - Depop(); - } Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target); - if(GetSwarmInfo()->target != 0) - { + if (GetSwarmInfo()->target != 0) { if(!targMob || (targMob && targMob->IsCorpse())) Depop(); } From b0e24b346ef15aa71cce411668030b4d27c00df6 Mon Sep 17 00:00:00 2001 From: sorvani Date: Wed, 5 Mar 2014 22:19:55 -0600 Subject: [PATCH 16/82] Fixes #119 --- changelog.txt | 1 + common/database.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 09a93239a..b9f59ba88 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 03/05/2014 == demonstar55: Corrected rogue's evade to be single target +sorvani: fixed crash issue 119 == 03/04/2014 == Sorvani: Created RemoveFromInstance and RemoveAllFromInstance to remove a single player or all players in an instance. diff --git a/common/database.cpp b/common/database.cpp index 04e4d3b9a..4dcee568e 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2522,10 +2522,11 @@ bool Database::GetUnusedInstanceID(uint16 &instance_id) safe_delete_array(query); if (mysql_num_rows(result) != 0) { row = mysql_fetch_row(result); - mysql_free_result(result); if(atoi(row[0]) <= max) { count = atoi(row[0]); + mysql_free_result(result); } else { + mysql_free_result(result); if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id > %u ORDER BY id", count), errbuf, &result)) { safe_delete_array(query); if (mysql_num_rows(result) != 0) { From 6c00eb9344bc9c8ebb17a2c8d9c26610981deb4f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 6 Mar 2014 02:38:22 -0500 Subject: [PATCH 17/82] Renamed optional SQL --- .../optional/{2013_03_04_RootRule.sql => 2014_03_04_RootRule.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename utils/sql/git/optional/{2013_03_04_RootRule.sql => 2014_03_04_RootRule.sql} (100%) diff --git a/utils/sql/git/optional/2013_03_04_RootRule.sql b/utils/sql/git/optional/2014_03_04_RootRule.sql similarity index 100% rename from utils/sql/git/optional/2013_03_04_RootRule.sql rename to utils/sql/git/optional/2014_03_04_RootRule.sql From d22c8832f78d981b820faadafc438f4d6dad15a5 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 6 Mar 2014 02:39:54 -0500 Subject: [PATCH 18/82] Renamed optional SQL --- .../{2013_03_02_CharmRules.sql => 2014_03_02_CharmRules.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename utils/sql/git/optional/{2013_03_02_CharmRules.sql => 2014_03_02_CharmRules.sql} (100%) diff --git a/utils/sql/git/optional/2013_03_02_CharmRules.sql b/utils/sql/git/optional/2014_03_02_CharmRules.sql similarity index 100% rename from utils/sql/git/optional/2013_03_02_CharmRules.sql rename to utils/sql/git/optional/2014_03_02_CharmRules.sql From 55f5d4affa40c027b4afa97f2bc83cdba256a60c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 6 Mar 2014 07:36:47 -0500 Subject: [PATCH 19/82] SE_Root and SE_RootBreakChance updates --- zone/attack.cpp | 41 ++++++++++++++++++++--------------------- zone/bonuses.cpp | 10 ++++++++++ zone/common.h | 4 +++- zone/mob.h | 1 - zone/spell_effects.cpp | 8 +++----- zone/spells.cpp | 1 + zone/zonedb.cpp | 1 + 7 files changed, 38 insertions(+), 28 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 594961c7c..a291a709b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4564,32 +4564,31 @@ bool Mob::TryRootFadeByDamage(int buffslot) chance to break root is increased. My guess is when this code was put in place, the devs at the time couldn't imagine DOT damage getting that high. */ + + /* General Mechanics + - Check buffslot to make sure damage from a root does not cancel the root + - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. + - Only roots on determental spells can be broken by damage. + */ - int BreakChance = RuleI(Spells, RootBreakFromSpells); + if (!spellbonuses.Root[0] || spellbonuses.Root[1] < 0) + return false; + + if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ - BreakChance -= BreakChance*rooted_mod/100; + int BreakChance = RuleI(Spells, RootBreakFromSpells); + + BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; - if (BreakChance < 1) - BreakChance = 1; + if (BreakChance < 1) + BreakChance = 1; - if (MakeRandomInt(0, 99) < BreakChance) { + if (MakeRandomInt(0, 99) < BreakChance) { - /* - - Check buffslot to make sure damage from a root does not cancel the root - - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. - - Only roots on determental spells can be broken by damage. - */ - - uint32 buff_count = GetMaxTotalSlots(); - for(int i = 0; i < buff_count; i++) - { - if(IsValidSpell(buffs[i].spellid) && - (IsEffectInSpell(buffs[i].spellid, SE_Root) && IsDetrimentalSpell(buffs[i].spellid) && i != buffslot)){ - if (!TryFadeEffect(i)) { - BuffFadeBySlot(i); - mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance"); - return true; - } + if (!TryFadeEffect(spellbonuses.Root[1])) { + BuffFadeBySlot(spellbonuses.Root[1]); + mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance"); + return true; } } } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 7cde92ae4..45fa20e8b 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2533,6 +2533,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->FrenziedDevastation += spells[spell_id].base2[i]; break; + case SE_Root: + if (newbon->Root[0] && (newbon->Root[1] > buffslot)){ + newbon->Root[0] = 1; + newbon->Root[1] = buffslot; + } + else { + newbon->Root[0] = 1; + newbon->Root[1] = buffslot; + } + break; } } } diff --git a/zone/common.h b/zone/common.h index eac6b0940..bd744316f 100644 --- a/zone/common.h +++ b/zone/common.h @@ -162,7 +162,8 @@ struct Buffs_Struct { int32 caston_x; int32 caston_y; int32 caston_z; - int32 ExtraDIChance; + int32 ExtraDIChance; + int16 RootBreakChance; //Not saved to dbase bool persistant_buff; bool client; //True if the caster is a client bool UpdateClient; @@ -339,6 +340,7 @@ struct StatBonuses { bool DivineAura; // invulnerability bool DistanceRemoval; // Check if Cancle if Moved effect is present int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid + int8 Root[2]; // The lowest buff slot a root can be found. [0] = Bool if has root [1] = buff slot int16 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana. //bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect //bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect diff --git a/zone/mob.h b/zone/mob.h index 6fad15013..a98c125c4 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1085,7 +1085,6 @@ protected: bool inWater; // Set to true or false by Water Detection code if enabled by rules bool has_virus; // whether this mob has a viral spell on them uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids - int16 rooted_mod; //Modifier to root break chance, defined when root is cast on a target. bool offhand; bool has_shieldequiped; bool has_numhits; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f01476779..bdf60a390 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1631,12 +1631,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) snprintf(effect_desc, _EDLEN, "Root: %+i", effect_value); #endif rooted = true; - rooted_mod = 0; if (caster){ - rooted_mod = caster->aabonuses.RootBreakChance + - caster->itembonuses.RootBreakChance + - caster->spellbonuses.RootBreakChance; + buffs[buffslot].RootBreakChance = caster->aabonuses.RootBreakChance + + caster->itembonuses.RootBreakChance + + caster->spellbonuses.RootBreakChance; } break; @@ -3709,7 +3708,6 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) case SE_Root: { rooted = false; - rooted_mod = 0; break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 965f96427..cef63351c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2951,6 +2951,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].caston_z = 0; buffs[emptyslot].dot_rune = 0; buffs[emptyslot].ExtraDIChance = 0; + buffs[emptyslot].RootBreakChance = 0; if (level_override > 0) { buffs[emptyslot].UpdateClient = true; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 278f87eaf..9457a5f8d 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2654,6 +2654,7 @@ void ZoneDatabase::LoadBuffs(Client *c) { buffs[slot_id].caston_y = caston_y; buffs[slot_id].caston_z = caston_z; buffs[slot_id].ExtraDIChance = ExtraDIChance; + buffs[slot_id].RootBreakChance = 0; buffs[slot_id].UpdateClient = false; if(IsRuneSpell(spell_id)) { c->SetHasRune(true); From 496f8151c9966c173a1d59f9cf731e4bde897149 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 6 Mar 2014 07:44:39 -0500 Subject: [PATCH 20/82] root related bonuses for negate effect. --- zone/bonuses.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 45fa20e8b..49cfe8a08 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -3888,10 +3888,15 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_FrenziedDevastation: - spellbonuses.FrenziedDevastation += effect_value; - aabonuses.FrenziedDevastation += effect_value; - itembonuses.FrenziedDevastation += effect_value; + spellbonuses.FrenziedDevastation = effect_value; + aabonuses.FrenziedDevastation = effect_value; + itembonuses.FrenziedDevastation = effect_value; break; + + case SE_Root: + spellbonuses.Root[0] = effect_value; + spellbonuses.Root[1] = -1; + } } } From fb3e3c8447a3e1803a6e7d5efd50a6264d491b0a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 6 Mar 2014 07:52:10 -0500 Subject: [PATCH 21/82] fix --- zone/spell_effects.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index bdf60a390..4361761de 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3707,6 +3707,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) case SE_Root: { + buffs[slot].RootBreakChance = 0; rooted = false; break; } From 3ec1c894eef86f8a0e6b2304c721dc72e246851a Mon Sep 17 00:00:00 2001 From: ukmeth0d Date: Fri, 7 Mar 2014 01:34:30 +0100 Subject: [PATCH 22/82] Fixed 2H Blunt Animation to match Live Melee bots (with no mana) will now sit when not full HP and out of combat. --- zone/attack.cpp | 4 ++-- zone/bot.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 3316c3eb3..75f742983 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -91,7 +91,7 @@ bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* w case ItemType2HBlunt: // 2H Blunt { skillinuse = Skill2HBlunt; - type = anim2HWeapon; + type = anim2HSlashing; //anim2HWeapon break; } case ItemType2HPiercing: // 2H Piercing @@ -140,7 +140,7 @@ bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* w } case Skill2HBlunt: // 2H Blunt { - type = anim2HWeapon; + type = anim2HSlashing; //anim2HWeapon break; } case 99: // 2H Piercing // change to Skill2HPiercing once activated diff --git a/zone/bot.cpp b/zone/bot.cpp index 2b9580452..7bd337a14 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3151,9 +3151,9 @@ void Bot::SpellProcess() void Bot::BotMeditate(bool isSitting) { if(isSitting) { // If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate - if(GetManaRatio() < 99.0f) + if(GetManaRatio() < 99.0f || GetHPRatio() < 99.0f) { - if(!IsSitting()) + if (!IsEngaged() && !IsSitting()) Sit(); } else From e25fd47828585adf6f767838c7410f0aa222c5a0 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 7 Mar 2014 19:59:44 -0800 Subject: [PATCH 23/82] Added some other timer functions to lua, removed perl memory leak stop gap for the moment until i decide how to fix it... --- zone/embparser.cpp | 13 +++++----- zone/lua_general.cpp | 36 +++++++++++++++++++++++--- zone/questmgr.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++ zone/questmgr.h | 8 +++++- 4 files changed, 107 insertions(+), 10 deletions(-) diff --git a/zone/embparser.cpp b/zone/embparser.cpp index cdf4e5735..ff917c3e3 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -670,11 +670,11 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 ret_value = perl->dosub(std::string(pkgprefix).append("::").append(event).c_str()); #ifdef EMBPERL_XS_CLASSES - std::string eval_str = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client = undef;"; - eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc = undef;"; - eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem = undef;"; - eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list = undef;"; - perl->eval(eval_str.c_str()); +// std::string eval_str = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client = undef;"; +// eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc = undef;"; +// eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem = undef;"; +// eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list = undef;"; +// perl->eval(eval_str.c_str()); #endif } catch(const char * err) { @@ -687,7 +687,8 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 error += "::"; error += event; error += " - "; - error += err; + if(strlen(err) > 0) + error += err; AddError(error); } } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 7b275ea67..2c11545ef 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -168,14 +168,38 @@ void lua_set_timer(const char *timer, int time_ms) { quest_manager.settimerMS(timer, time_ms); } +void lua_set_timer(const char *timer, int time_ms, Lua_ItemInst inst) { + quest_manager.settimerMS(timer, time_ms, inst); +} + +void lua_set_timer(const char *timer, int time_ms, Lua_Mob mob) { + quest_manager.settimerMS(timer, time_ms, mob); +} + void lua_stop_timer(const char *timer) { quest_manager.stoptimer(timer); } +void lua_stop_timer(const char *timer, Lua_ItemInst inst) { + quest_manager.stoptimer(timer, inst); +} + +void lua_stop_timer(const char *timer, Lua_Mob mob) { + quest_manager.stoptimer(timer, mob); +} + void lua_stop_all_timers() { quest_manager.stopalltimers(); } +void lua_stop_all_timers(Lua_ItemInst inst) { + quest_manager.stopalltimers(inst); +} + +void lua_stop_all_timers(Lua_Mob mob) { + quest_manager.stopalltimers(mob); +} + void lua_depop() { quest_manager.depop(0); } @@ -1091,9 +1115,15 @@ luabind::scope lua_register_general() { luabind::def("spawn_from_spawn2", (Lua_Mob(*)(uint32))&lua_spawn_from_spawn2), luabind::def("enable_spawn2", &lua_enable_spawn2), luabind::def("disable_spawn2", &lua_disable_spawn2), - luabind::def("set_timer", &lua_set_timer), - luabind::def("stop_timer", &lua_stop_timer), - luabind::def("stop_all_timers", &lua_stop_all_timers), + luabind::def("set_timer", (void(*)(const char*, int))&lua_set_timer), + luabind::def("set_timer", (void(*)(const char*, int, Lua_ItemInst))&lua_set_timer), + luabind::def("set_timer", (void(*)(const char*, int, Lua_Mob))&lua_set_timer), + luabind::def("stop_timer", (void(*)(const char*))&lua_stop_timer), + luabind::def("stop_timer", (void(*)(const char*, Lua_ItemInst))&lua_stop_timer), + luabind::def("stop_timer", (void(*)(const char*, Lua_Mob))&lua_stop_timer), + luabind::def("stop_all_timers", (void(*)(void))&lua_stop_all_timers), + luabind::def("stop_all_timers", (void(*)(Lua_ItemInst))&lua_stop_all_timers), + luabind::def("stop_all_timers", (void(*)(Lua_Mob))&lua_stop_all_timers), luabind::def("depop", (void(*)(void))&lua_depop), luabind::def("depop", (void(*)(int))&lua_depop), luabind::def("depop_with_timer", (void(*)(void))&lua_depop_with_timer), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 484a63d40..850ef93f8 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -484,6 +484,29 @@ void QuestManager::settimerMS(const char *timer_name, int milliseconds) { QTimerList.push_back(QuestTimer(milliseconds, owner, timer_name)); } +void QuestManager::settimerMS(const char *timer_name, int milliseconds, ItemInst *inst) { + if (inst) { + inst->SetTimer(timer_name, milliseconds); + } +} + +void QuestManager::settimerMS(const char *timer_name, int milliseconds, Mob *mob) { + std::list::iterator cur = QTimerList.begin(), end; + + end = QTimerList.end(); + while (cur != end) { + if (cur->mob && cur->mob == mob && cur->name == timer_name) + { + cur->Timer_.Enable(); + cur->Timer_.Start(milliseconds, false); + return; + } + ++cur; + } + + QTimerList.push_back(QuestTimer(milliseconds, mob, timer_name)); +} + void QuestManager::stoptimer(const char *timer_name) { QuestManagerCurrentQuestVars(); @@ -504,6 +527,25 @@ void QuestManager::stoptimer(const char *timer_name) { } } +void QuestManager::stoptimer(const char *timer_name, ItemInst *inst) { + if (inst) { + inst->StopTimer(timer_name); + } +} + +void QuestManager::stoptimer(const char *timer_name, Mob *mob) { + std::list::iterator cur = QTimerList.begin(), end; + + end = QTimerList.end(); + while (cur != end) { + if (cur->mob && cur->mob == mob && cur->name == timer_name) { + QTimerList.erase(cur); + return; + } + ++cur; + } +} + void QuestManager::stopalltimers() { QuestManagerCurrentQuestVars(); @@ -523,6 +565,24 @@ void QuestManager::stopalltimers() { } } +void QuestManager::stopalltimers(ItemInst *inst) { + if (inst) { + inst->ClearTimers(); + } +} + +void QuestManager::stopalltimers(Mob *mob) { + std::list::iterator cur = QTimerList.begin(), end, tmp; + + end = QTimerList.end(); + while (cur != end) { + if (cur->mob && cur->mob == mob) + cur = QTimerList.erase(cur); + else + ++cur; + } +} + void QuestManager::emote(const char *str) { QuestManagerCurrentQuestVars(); if (!owner) { diff --git a/zone/questmgr.h b/zone/questmgr.h index 7c1960283..1ad763aa0 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -66,9 +66,15 @@ public: void addloot(int item_id, int charges = 0, bool equipitem = true); void Zone(const char *zone_name); void settimer(const char *timer_name, int seconds); - void settimerMS(const char *timer_name, int milliseconds); + void settimerMS(const char *timer_name, int milliseconds); + void settimerMS(const char *timer_name, int milliseconds, ItemInst *inst); + void settimerMS(const char *timer_name, int milliseconds, Mob *mob); void stoptimer(const char *timer_name); + void stoptimer(const char *timer_name, ItemInst *inst); + void stoptimer(const char *timer_name, Mob *mob); void stopalltimers(); + void stopalltimers(ItemInst *inst); + void stopalltimers(Mob *mob); void emote(const char *str); void shout(const char *str); void shout2(const char *str); From 9ecf98195c5b43f86faf534587efe37dd16ad53c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 8 Mar 2014 05:35:22 -0500 Subject: [PATCH 24/82] Lull spell effect revisions --- changelog.txt | 6 ++++++ zone/aggro.cpp | 3 ++- zone/spells.cpp | 17 +++++++---------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/changelog.txt b/changelog.txt index b9f59ba88..df6cc662d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/08/2014 == +Kayen: Revision to lull/harmony/pacification code to be consistent with live based on extensive personal parsing. +*Lulls on initial cast do not check regular resists (MR ect) but only apply a flat ~7.5 % resist chance + level modifier +*If Lull is resisted, a second resistance check is made this time using regular resists and a charisma modifier (same as charm) +which if 'resisted' will cause the caster to gain aggro. + == 03/05/2014 == demonstar55: Corrected rogue's evade to be single target sorvani: fixed crash issue 119 diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 862c91605..71956a535 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1446,7 +1446,8 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { else { // Assume this is a harmony/pacify spell - resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster); + // If 'Lull' spell resists, do a second resist check with a charisma modifier AND regular resist checks. If resists agian you gain aggro. + resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, true); if (resist_check == 100) return true; diff --git a/zone/spells.cpp b/zone/spells.cpp index cef63351c..8769e16a2 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4173,16 +4173,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } //Setup our base resist chance. - //Lulls have a slightly higher chance to resist than normal 15/200 or ~ 7.5% - int resist_chance; - if(IsHarmonySpell(spell_id)) - { - resist_chance = 15; - } - else - { - resist_chance = 0; - } + int resist_chance = 0; //Adjust our resist chance based on level modifiers int temp_level_diff = GetLevel() - caster->GetLevel(); @@ -4242,6 +4233,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if (CharismaCheck) { //Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA + //'Lull' spells only check charisma if inital cast is resisted to see if mob will aggro, same modifier/cap as above. //Charisma DOES NOT extend charm durations. int16 charisma = caster->GetCHA(); @@ -4251,6 +4243,11 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use resist_modifier -= charisma/RuleI(Spells, CharismaEffectiveness); } + //Lull spells DO NOT use regular resists on initial cast, instead they use a flat +15 modifier. Live parses confirm this. + //Regular resists are used when checking if mob will aggro off of a lull resist. + if(!CharismaCheck && IsHarmonySpell(spell_id)) + target_resist = 15; + //Add our level, resist and -spell resist modifier to our roll chance resist_chance += level_mod; resist_chance += resist_modifier; From 2e4b4b94edbe4ea221f3213af76fb3629ca92d73 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 9 Mar 2014 17:55:24 -0700 Subject: [PATCH 25/82] Memory leak work around for perl, should play better with events that call other events now. --- zone/embparser.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++---- zone/embparser.h | 1 + zone/questmgr.h | 1 + 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/zone/embparser.cpp b/zone/embparser.cpp index ff917c3e3..c869104f8 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -632,6 +632,37 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 perl->eval(cmd.c_str()); #ifdef EMBPERL_XS_CLASSES + + { + std::string cl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client"; + std::string np = (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc"; + std::string qi = (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem"; + std::string enl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list"; + if(clear_vars_.find(cl) != clear_vars_.end()) { + std::string eval_str = cl; + eval_str += " = undef;"; + perl->eval(eval_str.c_str()); + } + + if (clear_vars_.find(np) != clear_vars_.end()) { + std::string eval_str = np; + eval_str += " = undef;"; + perl->eval(eval_str.c_str()); + } + + if (clear_vars_.find(qi) != clear_vars_.end()) { + std::string eval_str = qi; + eval_str += " = undef;"; + perl->eval(eval_str.c_str()); + } + + if (clear_vars_.find(enl) != clear_vars_.end()) { + std::string eval_str = enl; + eval_str += " = undef;"; + perl->eval(eval_str.c_str()); + } + } + char namebuf[64]; //init a couple special vars: client, npc, entity_list @@ -670,11 +701,16 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 ret_value = perl->dosub(std::string(pkgprefix).append("::").append(event).c_str()); #ifdef EMBPERL_XS_CLASSES -// std::string eval_str = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client = undef;"; -// eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc = undef;"; -// eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem = undef;"; -// eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list = undef;"; -// perl->eval(eval_str.c_str()); + { + std::string cl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client"; + std::string np = (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc"; + std::string qi = (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem"; + std::string enl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list"; + clear_vars_[cl] = 1; + clear_vars_[np] = 1; + clear_vars_[qi] = 1; + clear_vars_[enl] = 1; + } #endif } catch(const char * err) { @@ -694,6 +730,30 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 } quest_manager.EndQuest(); + +#ifdef EMBPERL_XS_CLASSES + if(!quest_manager.QuestsRunning()) { + auto iter = clear_vars_.begin(); + std::string eval_str; + while(iter != clear_vars_.end()) { + eval_str += iter->first; + eval_str += " = undef;"; + ++iter; + } + clear_vars_.clear(); + + try { + perl->eval(eval_str.c_str()); + } + catch (const char * err) { + std::string error = "Script clear error: "; + if (strlen(err) > 0) + error += err; + AddError(error); + } + } +#endif + return ret_value; } diff --git a/zone/embparser.h b/zone/embparser.h index 2540402b2..ffca78075 100644 --- a/zone/embparser.h +++ b/zone/embparser.h @@ -114,6 +114,7 @@ private: std::map vars_; SV *_empty_sv; + std::map clear_vars_; }; #endif diff --git a/zone/questmgr.h b/zone/questmgr.h index ca74e938a..81ccc58c2 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -41,6 +41,7 @@ public: void StartQuest(Mob *_owner, Client *_initiator = nullptr, ItemInst* _questitem = nullptr); void EndQuest(); + bool QuestsRunning() { return !quests_running_.empty(); } void Process(); From 93105966b6a7b1d57a063e6304d5de5da548111f Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 9 Mar 2014 18:41:50 -0700 Subject: [PATCH 26/82] Added some missing bufffadebyx functions to lua mob --- zone/lua_mob.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++- zone/lua_mob.h | 9 ++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 1fc1f38e3..17588caef 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1816,6 +1816,52 @@ void Lua_Mob::SetDestructibleObject(bool set) { self->SetDestructibleObject(set); } +bool Lua_Mob::IsImmuneToSpell(int spell_id, Lua_Mob caster) { + Lua_Safe_Call_Bool(); + return self->IsImmuneToSpell(spell_id, caster); +} + +void Lua_Mob::BuffFadeBySpellID(int spell_id) { + Lua_Safe_Call_Void(); + self->BuffFadeBySpellID(spell_id); +} + +void Lua_Mob::BuffFadeByEffect(int effect_id) { + Lua_Safe_Call_Void(); + self->BuffFadeByEffect(effect_id); +} + +void Lua_Mob::BuffFadeByEffect(int effect_id, int skipslot) { + Lua_Safe_Call_Void(); + self->BuffFadeByEffect(effect_id, skipslot); +} + +void Lua_Mob::BuffFadeAll() { + Lua_Safe_Call_Void(); + self->BuffFadeAll(); +} + +void Lua_Mob::BuffFadeBySlot(int slot) { + Lua_Safe_Call_Void(); + self->BuffFadeBySlot(slot); +} + +void Lua_Mob::BuffFadeBySlot(int slot, bool recalc_bonuses) { + Lua_Safe_Call_Void(); + self->BuffFadeBySlot(slot, recalc_bonuses); +} + +int Lua_Mob::CanBuffStack(int spell_id, int caster_level) { + Lua_Safe_Call_Int(); + return self->CanBuffStack(spell_id, caster_level); +} + +int Lua_Mob::CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite) { + Lua_Safe_Call_Int(); + return self->CanBuffStack(spell_id, caster_level, fail_if_overwrite); +} + + luabind::scope lua_register_mob() { return luabind::class_("Mob") .def(luabind::constructor<>()) @@ -2125,7 +2171,16 @@ luabind::scope lua_register_mob() { .def("ProcessSpecialAbilities", (void(Lua_Mob::*)(std::string))&Lua_Mob::ProcessSpecialAbilities) .def("SetAppearance", (void(Lua_Mob::*)(int))&Lua_Mob::SetAppearance) .def("SetAppearance", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetAppearance) - .def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject); + .def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject) + .def("IsImmuneToSpell", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::IsImmuneToSpell) + .def("BuffFadeBySpellID", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySpellID) + .def("BuffFadeByEffect", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeByEffect) + .def("BuffFadeByEffect", (void(Lua_Mob::*)(int,int))&Lua_Mob::BuffFadeByEffect) + .def("BuffFadeAll", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeAll) + .def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot) + .def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot) + .def("CanBuffStack", (int(Lua_Mob::*)(int,int))&Lua_Mob::CanBuffStack) + .def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack); } luabind::scope lua_register_special_abilities() { diff --git a/zone/lua_mob.h b/zone/lua_mob.h index c57e0ca84..b3e44805a 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -345,6 +345,15 @@ public: void SetAppearance(int app); void SetAppearance(int app, bool ignore_self); void SetDestructibleObject(bool set); + bool IsImmuneToSpell(int spell_id, Lua_Mob caster); + void BuffFadeBySpellID(int spell_id); + void BuffFadeByEffect(int effect_id); + void BuffFadeByEffect(int effect_id, int skipslot); + void BuffFadeAll(); + void BuffFadeBySlot(int slot); + void BuffFadeBySlot(int slot, bool recalc_bonuses); + int CanBuffStack(int spell_id, int caster_level); + int CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite); }; #endif From 39abb4f50c8f22deec9099d2fcc4243882597b93 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 10 Mar 2014 16:43:33 -0700 Subject: [PATCH 27/82] Changed how item faction bonuses are cleared. --- zone/mob.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index a77589d63..d7b37d996 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4761,11 +4761,7 @@ int32 Mob::GetItemFactionBonus(uint32 pFactionID) { } void Mob::ClearItemFactionBonuses() { - std::map :: iterator itr; - for(itr = item_faction_bonuses.begin(); itr != item_faction_bonuses.end(); ++itr) - { - item_faction_bonuses.erase(itr->first); - } + item_faction_bonuses.clear(); } FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { From 21bdc8c5b6bff3ede0f015c1b827c62b8d568d06 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 12 Mar 2014 05:14:19 -0400 Subject: [PATCH 28/82] Coverted melee and magic runes to use bonuses. Removed all the old rune flags now that none of them are used. Fixed issues where runes would not fade properly if damage = remaing rune amount Fixed issue where runes would stop absorbing damage if you had multiple runes. --- changelog.txt | 4 ++++ zone/attack.cpp | 31 +++++++++++------------------ zone/bonuses.cpp | 38 ++++++++++++++++++++++++++++++++++- zone/client.cpp | 4 ++-- zone/common.h | 4 ++-- zone/mob.cpp | 6 ------ zone/mob.h | 14 ------------- zone/pets.cpp | 1 - zone/spell_effects.cpp | 14 +++++-------- zone/spells.cpp | 45 ------------------------------------------ zone/zonedb.cpp | 6 ------ 11 files changed, 62 insertions(+), 105 deletions(-) diff --git a/changelog.txt b/changelog.txt index df6cc662d..e7973ed6d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- + +== 03/12/2014 == +Kayen: Melee/Magic runes are now calculated as bonuses. Resolved issues with runes not working and not fading properly. + == 03/08/2014 == Kayen: Revision to lull/harmony/pacification code to be consistent with live based on extensive personal parsing. *Lulls on initial cast do not check regular resists (MR ect) but only apply a flat ~7.5 % resist chance + level modifier diff --git a/zone/attack.cpp b/zone/attack.cpp index a291a709b..9ffcddb6d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3188,7 +3188,6 @@ int32 Mob::ReduceDamage(int32 damage) damage -= damage_to_reduce; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - //UpdateRuneFlags(); } else { @@ -3213,7 +3212,6 @@ int32 Mob::ReduceDamage(int32 damage) damage -= damage_to_reduce; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - UpdateRuneFlags(); } else { @@ -3221,7 +3219,6 @@ int32 Mob::ReduceDamage(int32 damage) " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); damage -= damage_to_reduce; - UpdateRuneFlags(); } } } @@ -3243,7 +3240,7 @@ int32 Mob::ReduceDamage(int32 damage) if(damage < 1) return -6; - if (HasRune()) + if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0) damage = RuneAbsorb(damage, SE_Rune); if(damage < 1) @@ -3317,7 +3314,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi damage -= damage_to_reduce; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - //UpdateRuneFlags(); } else { @@ -3341,7 +3337,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi damage -= damage_to_reduce; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - UpdateRuneFlags(); } else { @@ -3349,7 +3344,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi " damage remaining.", damage_to_reduce, buffs[slot].magic_rune); buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce); damage -= damage_to_reduce; - UpdateRuneFlags(); } } } @@ -3372,7 +3366,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi return 0; - if (HasSpellRune()) + if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0) damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); if(damage < 1) @@ -4602,9 +4596,10 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) uint32 buff_max = GetMaxTotalSlots(); if (type == SE_Rune){ for(uint32 slot = 0; slot < buff_max; slot++) { - if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].melee_rune) && IsEffectInSpell(buffs[slot].spellid, type)){ + if(slot == spellbonuses.MeleeRune[1] && spellbonuses.MeleeRune[0] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)){ uint32 melee_rune_left = buffs[slot].melee_rune; - if(melee_rune_left >= damage) + + if(melee_rune_left > damage) { melee_rune_left -= damage; buffs[slot].melee_rune = melee_rune_left; @@ -4615,22 +4610,20 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) { if(melee_rune_left > 0) damage -= melee_rune_left; + if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - UpdateRuneFlags(); - continue; } } } - return damage; } - + else{ for(uint32 slot = 0; slot < buff_max; slot++) { - if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].magic_rune) && IsEffectInSpell(buffs[slot].spellid, type)){ + if(slot == spellbonuses.AbsorbMagicAtt[1] && spellbonuses.AbsorbMagicAtt[0] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)){ uint32 magic_rune_left = buffs[slot].magic_rune; - if(magic_rune_left >= damage) + if(magic_rune_left > damage) { magic_rune_left -= damage; buffs[slot].magic_rune = magic_rune_left; @@ -4641,14 +4634,14 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) { if(magic_rune_left > 0) damage -= magic_rune_left; + if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); - UpdateRuneFlags(); - continue; } } } - return damage; } + + return damage; } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 49cfe8a08..99941b893 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2538,11 +2538,36 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->Root[0] = 1; newbon->Root[1] = buffslot; } - else { + else if (!newbon->Root[0]){ newbon->Root[0] = 1; newbon->Root[1] = buffslot; } break; + + case SE_Rune: + + if (newbon->MeleeRune[0] && (newbon->MeleeRune[1] > buffslot)){ + + newbon->MeleeRune[0] = effect_value; + newbon->MeleeRune[1] = buffslot; + } + else if (!newbon->MeleeRune[0]){ + newbon->MeleeRune[0] = effect_value; + newbon->MeleeRune[1] = buffslot; + } + + break; + + case SE_AbsorbMagicAtt: + if (newbon->AbsorbMagicAtt[0] && (newbon->AbsorbMagicAtt[1] > buffslot)){ + newbon->AbsorbMagicAtt[0] = effect_value; + newbon->AbsorbMagicAtt[1] = buffslot; + } + else if (!newbon->AbsorbMagicAtt[0]){ + newbon->AbsorbMagicAtt[0] = effect_value; + newbon->AbsorbMagicAtt[1] = buffslot; + } + break; } } } @@ -3896,7 +3921,18 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) case SE_Root: spellbonuses.Root[0] = effect_value; spellbonuses.Root[1] = -1; + break; + case SE_Rune: + spellbonuses.MeleeRune[0] = effect_value; + spellbonuses.MeleeRune[1] = -1; + break; + + case SE_AbsorbMagicAtt: + spellbonuses.AbsorbMagicAtt[0] = effect_value; + spellbonuses.AbsorbMagicAtt[1] = -1; + break; + } } } diff --git a/zone/client.cpp b/zone/client.cpp index e8af229ae..ebf5d806c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6710,9 +6710,9 @@ void Client::SendStatsWindow(Client* client, bool use_window) uint32 buff_count = GetMaxTotalSlots(); for (int i=0; i < buff_count; i++) { if (buffs[i].spellid != SPELL_UNKNOWN) { - if ((HasRune() || HasPartialMeleeRune()) && buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; } + if (buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; } - if ((HasSpellRune() || HasPartialSpellRune()) && buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; } + if (buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; } } } diff --git a/zone/common.h b/zone/common.h index bd744316f..581d11430 100644 --- a/zone/common.h +++ b/zone/common.h @@ -342,8 +342,8 @@ struct StatBonuses { int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid int8 Root[2]; // The lowest buff slot a root can be found. [0] = Bool if has root [1] = buff slot int16 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana. - //bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect - //bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect + uint16 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot + uint16 MeleeRune[2]; // 0 = rune value 1 = buff slot // AAs int8 Packrat; //weight reduction for items, 1 point = 10% diff --git a/zone/mob.cpp b/zone/mob.cpp index d7b37d996..3c1500d19 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -349,12 +349,6 @@ Mob::Mob(const char* in_name, nextinchpevent = -1; TempPets(false); - SetHasRune(false); - SetHasSpellRune(false); - SetHasPartialMeleeRune(false); - SetHasPartialSpellRune(false); - - m_hasDeathSaveChance = false; m_is_running = false; diff --git a/zone/mob.h b/zone/mob.h index a98c125c4..c771d57b7 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -248,14 +248,6 @@ public: virtual int GetMaxTotalSlots() const { return 0; } virtual void InitializeBuffSlots() { buffs = nullptr; current_buff_count = 0; } virtual void UninitializeBuffSlots() { } - inline bool HasRune() const { return m_hasRune; } - inline bool HasSpellRune() const { return m_hasSpellRune; } - inline bool HasPartialMeleeRune() const { return m_hasPartialMeleeRune; } - inline bool HasPartialSpellRune() const { return m_hasPartialSpellRune; } - inline void SetHasRune(bool hasRune) { m_hasRune = hasRune; } - inline void SetHasSpellRune(bool hasSpellRune) { m_hasSpellRune = hasSpellRune; } - inline void SetHasPartialMeleeRune(bool hasPartialMeleeRune) { m_hasPartialMeleeRune = hasPartialMeleeRune; } - inline void SetHasPartialSpellRune(bool hasPartialSpellRune) { m_hasPartialSpellRune = hasPartialSpellRune; } EQApplicationPacket *MakeBuffsPacket(bool for_target = true); void SendBuffsToClient(Client *c); inline Buffs_Struct* GetBuffs() { return buffs; } @@ -993,7 +985,6 @@ protected: float FindGroundZ(float new_x, float new_y, float z_offset=0.0); VERTEX UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached); void PrintRoute(); - void UpdateRuneFlags(); virtual float GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod); @@ -1192,11 +1183,6 @@ protected: float tar_vz; float test_vector; - bool m_hasRune; - bool m_hasSpellRune; - bool m_hasPartialMeleeRune; - bool m_hasPartialSpellRune; - bool m_hasDeathSaveChance; uint32 m_spellHitsLeft[38]; // Used to track which spells will have their numhits incremented when spell finishes casting, 38 Buffslots int flymode; bool m_targetable; diff --git a/zone/pets.cpp b/zone/pets.cpp index 5e81c3f84..d5946ce58 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -627,7 +627,6 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) { } } } - UpdateRuneFlags(); //restore their equipment... for(i = 0; i < MAX_WORN_INVENTORY; i++) { diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4361761de..cebd548dd 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1242,7 +1242,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #endif effect_value = ApplySpellEffectiveness(caster, spell_id, effect_value); buffs[buffslot].melee_rune = effect_value; - SetHasRune(true); break; } @@ -1251,17 +1250,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Spell Absorb Rune: %+i", effect_value); #endif - if(effect_value > 0) { - buffs[buffslot].magic_rune = effect_value; - SetHasSpellRune(true); - } + if(effect_value > 0) + buffs[buffslot].magic_rune = effect_value; + break; } case SE_MitigateMeleeDamage: { - buffs[buffslot].melee_rune = GetPartialMeleeRuneAmount(spell_id); - SetHasPartialMeleeRune(true); + buffs[buffslot].melee_rune = spells[spell_id].max[i]; break; } @@ -1279,8 +1276,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_MitigateSpellDamage: { - buffs[buffslot].magic_rune = GetPartialMagicRuneAmount(spell_id); - SetHasPartialSpellRune(true); + buffs[buffslot].magic_rune = spells[spell_id].max[i]; break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 8769e16a2..332906883 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5133,51 +5133,6 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration) } } } -void Mob::UpdateRuneFlags() -{ - bool Has_SE_Rune = false, Has_SE_AbsorbMagicAtt = false, Has_SE_MitigateMeleeDamage = false, Has_SE_MitigateSpellDamage = false; - uint32 buff_count = GetMaxTotalSlots(); - for (unsigned int i = 0; i < buff_count; ++i) - { - if (buffs[i].spellid != SPELL_UNKNOWN) - { - for (int j = 0; j < EFFECT_COUNT; ++j) - { - switch(spells[buffs[i].spellid].effectid[j]) - { - case SE_Rune: - { - Has_SE_Rune = true; - break; - } - case SE_AbsorbMagicAtt: - { - Has_SE_AbsorbMagicAtt = true; - break; - } - case SE_MitigateMeleeDamage: - { - Has_SE_MitigateMeleeDamage = true; - break; - } - case SE_MitigateSpellDamage: - { - Has_SE_MitigateSpellDamage = true; - break; - } - - default: - break; - } - } - } - } - - SetHasRune(Has_SE_Rune); - SetHasSpellRune(Has_SE_AbsorbMagicAtt); - SetHasPartialMeleeRune(Has_SE_MitigateMeleeDamage); - SetHasPartialSpellRune(Has_SE_MitigateSpellDamage); -} int Client::GetCurrentBuffSlots() const { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 9457a5f8d..6c3b8ce6e 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2656,12 +2656,6 @@ void ZoneDatabase::LoadBuffs(Client *c) { buffs[slot_id].ExtraDIChance = ExtraDIChance; buffs[slot_id].RootBreakChance = 0; buffs[slot_id].UpdateClient = false; - if(IsRuneSpell(spell_id)) { - c->SetHasRune(true); - } - else if(IsMagicRuneSpell(spell_id)) { - c->SetHasSpellRune(true); - } } mysql_free_result(result); From c2399644273322c718beeb723d9bd9cd2931be54 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 13 Mar 2014 14:45:54 -0700 Subject: [PATCH 29/82] Tests for hextoi, hextoi64, atobool const hextoi / hextoi64 / atobool. null check for each. tests for each. --- common/StringUtil.cpp | 15 ++- common/StringUtil.h | 6 +- tests/CMakeLists.txt | 2 + tests/atobool_test.h | 103 ++++++++++++++++++ tests/hextoi_32_64_test.h | 213 ++++++++++++++++++++++++++++++++++++++ tests/main.cpp | 4 + 6 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 tests/atobool_test.h create mode 100644 tests/hextoi_32_64_test.h diff --git a/common/StringUtil.cpp b/common/StringUtil.cpp index 8a73ba9b4..71bb961e1 100644 --- a/common/StringUtil.cpp +++ b/common/StringUtil.cpp @@ -193,7 +193,10 @@ uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const cha return *strlen; } -uint32 hextoi(char* num) { +uint32 hextoi(const char* num) { + if (num == nullptr) + return 0; + int len = strlen(num); if (len < 3) return 0; @@ -217,7 +220,10 @@ uint32 hextoi(char* num) { return ret; } -uint64 hextoi64(char* num) { +uint64 hextoi64(const char* num) { + if (num == nullptr) + return 0; + int len = strlen(num); if (len < 3) return 0; @@ -241,7 +247,10 @@ uint64 hextoi64(char* num) { return ret; } -bool atobool(char* iBool) { +bool atobool(const char* iBool) { + + if (iBool == nullptr) + return false; if (!strcasecmp(iBool, "true")) return true; if (!strcasecmp(iBool, "false")) diff --git a/common/StringUtil.h b/common/StringUtil.h index 81ba6e27e..46ff43183 100644 --- a/common/StringUtil.h +++ b/common/StringUtil.h @@ -33,9 +33,9 @@ void MakeLowerString(const char *source, char *target); int MakeAnyLenString(char** ret, const char* format, ...); uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...); -uint32 hextoi(char* num); -uint64 hextoi64(char* num); -bool atobool(char* iBool); +uint32 hextoi(const char* num); +uint64 hextoi64(const char* num); +bool atobool(const char* iBool); char* strn0cpy(char* dest, const char* source, uint32 size); // return value =true if entire string(source) fit, false if it was truncated diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2cffeae88..80f6a2f1d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,6 +11,8 @@ SET(tests_headers fixed_memory_variable_test.h ipc_mutex_test.h memory_mapped_file_test.h + atobool_test.h + hextoi_32_64_test.h ) ADD_EXECUTABLE(tests ${tests_sources} ${tests_headers}) diff --git a/tests/atobool_test.h b/tests/atobool_test.h new file mode 100644 index 000000000..ce02df02d --- /dev/null +++ b/tests/atobool_test.h @@ -0,0 +1,103 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __EQEMU_TESTS_ATOBOOL_H +#define __EQEMU_TESTS_ATOBOOL_H + +#include "cppunit/cpptest.h" +#include "../common/StringUtil.h" + +class atoboolTest : public Test::Suite { + typedef void(atoboolTest::*TestFunction)(void); +public: + atoboolTest() { + TEST_ADD(atoboolTest::TrueTest); + TEST_ADD(atoboolTest::FalseTest); + TEST_ADD(atoboolTest::YesTest); + TEST_ADD(atoboolTest::NoTest); + TEST_ADD(atoboolTest::OnTest); + TEST_ADD(atoboolTest::OffTest); + TEST_ADD(atoboolTest::EnableTest); + TEST_ADD(atoboolTest::DisableTest); + TEST_ADD(atoboolTest::EnabledTest); + TEST_ADD(atoboolTest::DisabledTest); + TEST_ADD(atoboolTest::YTest); + TEST_ADD(atoboolTest::NTest); + TEST_ADD(atoboolTest::nullptrTest); + } + + ~atoboolTest() { + } + + private: + + void TrueTest() { + TEST_ASSERT(atobool("true")); + } + + void FalseTest() { + TEST_ASSERT(!atobool("false")); + } + + void YesTest() { + TEST_ASSERT(atobool("yes")); + } + + void NoTest() { + TEST_ASSERT(!atobool("no")); + } + + void OnTest() { + TEST_ASSERT(atobool("on")); + } + + void OffTest() { + TEST_ASSERT(!atobool("off")); + } + + void EnableTest() { + TEST_ASSERT(atobool("enable")); + } + + void DisableTest() { + TEST_ASSERT(!atobool("disable")); + } + + void EnabledTest() { + TEST_ASSERT(atobool("enabled")); + } + + void DisabledTest() { + TEST_ASSERT(!atobool("disabled")); + } + + void YTest() { + TEST_ASSERT(atobool("y")); + } + + void NTest() { + TEST_ASSERT(!atobool("n")); + } + + void nullptrTest() { + TEST_ASSERT(!atobool(nullptr)); + } + +}; + +#endif diff --git a/tests/hextoi_32_64_test.h b/tests/hextoi_32_64_test.h new file mode 100644 index 000000000..eda5e7fc2 --- /dev/null +++ b/tests/hextoi_32_64_test.h @@ -0,0 +1,213 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __EQEMU_TESTS_HEXTOI_32_64_H +#define __EQEMU_TESTS_HEXTOI_32_64_H + +#include "cppunit/cpptest.h" +#include "../common/StringUtil.h" + +class hextoi_32_64_Test : public Test::Suite { + typedef void(hextoi_32_64_Test::*TestFunction)(void); +public: + hextoi_32_64_Test() { + TEST_ADD(hextoi_32_64_Test::nullptr32Test); + TEST_ADD(hextoi_32_64_Test::ShortString32Test); + TEST_ADD(hextoi_32_64_Test::SingleDigitUpper32Test); + TEST_ADD(hextoi_32_64_Test::SingleDigitLower32Test); + TEST_ADD(hextoi_32_64_Test::DoubleDigitUpper32Test); + TEST_ADD(hextoi_32_64_Test::DoubleDigitLower32Test); + + TEST_ADD(hextoi_32_64_Test::nullptr64Test); + TEST_ADD(hextoi_32_64_Test::ShortString64Test); + TEST_ADD(hextoi_32_64_Test::SingleDigitUpper64Test); + TEST_ADD(hextoi_32_64_Test::SingleDigitLower64Test); + TEST_ADD(hextoi_32_64_Test::DoubleDigitUpper64Test); + TEST_ADD(hextoi_32_64_Test::DoubleDigitLower64Test); + } + + ~hextoi_32_64_Test() { + } + + private: + + void nullptr32Test() { + TEST_ASSERT(hextoi(nullptr) == 0); + } + + void ShortString32Test() { + // if the string is too short then it should + // spit out a zero. + // strings should be formatted: 0x** or 0X** + TEST_ASSERT(hextoi("") == 0); + TEST_ASSERT(hextoi("0") == 0); + TEST_ASSERT(hextoi("01") == 0); + } + + void SingleDigitUpper32Test() { + TEST_ASSERT(hextoi("0x0") == 0); + TEST_ASSERT(hextoi("0x1") == 1); + TEST_ASSERT(hextoi("0x2") == 2); + TEST_ASSERT(hextoi("0x3") == 3); + TEST_ASSERT(hextoi("0x4") == 4); + TEST_ASSERT(hextoi("0x5") == 5); + TEST_ASSERT(hextoi("0x6") == 6); + TEST_ASSERT(hextoi("0x7") == 7); + TEST_ASSERT(hextoi("0x8") == 8); + TEST_ASSERT(hextoi("0x9") == 9); + TEST_ASSERT(hextoi("0xA") == 10); + TEST_ASSERT(hextoi("0xB") == 11); + TEST_ASSERT(hextoi("0xC") == 12); + TEST_ASSERT(hextoi("0xD") == 13); + TEST_ASSERT(hextoi("0xE") == 14); + TEST_ASSERT(hextoi("0xF") == 15); + } + + void SingleDigitLower32Test() { + TEST_ASSERT(hextoi("0x0") == 0); + TEST_ASSERT(hextoi("0x1") == 1); + TEST_ASSERT(hextoi("0x2") == 2); + TEST_ASSERT(hextoi("0x3") == 3); + TEST_ASSERT(hextoi("0x4") == 4); + TEST_ASSERT(hextoi("0x5") == 5); + TEST_ASSERT(hextoi("0x6") == 6); + TEST_ASSERT(hextoi("0x7") == 7); + TEST_ASSERT(hextoi("0x8") == 8); + TEST_ASSERT(hextoi("0x9") == 9); + TEST_ASSERT(hextoi("0xa") == 10); + TEST_ASSERT(hextoi("0xb") == 11); + TEST_ASSERT(hextoi("0xc") == 12); + TEST_ASSERT(hextoi("0xd") == 13); + TEST_ASSERT(hextoi("0xe") == 14); + TEST_ASSERT(hextoi("0xf") == 15); + } + + // A bit excessive to do an exhaustive test like this + // but it usefully tests multi digit hex. + void DoubleDigitUpper32Test() { + + std::string prepend = "0x"; + std::string hexToTest; + + std::string hexElements = "0123456789ABCDEF"; + uint32 value = 0; + for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) { + for (std::string::iterator secondDigitIter = hexElements.begin(); secondDigitIter != hexElements.end(); ++secondDigitIter) { + std::string hexToTest = prepend + *firstDigitIter + *secondDigitIter; + TEST_ASSERT(hextoi(hexToTest.c_str()) == value); + value++; + } + } + } + + // A bit excessive to do an exhaustive test like this + // but it usefully tests multi digit hex. + void DoubleDigitLower32Test() { + std::string prepend = "0x"; + std::string hexToTest; + + std::string hexElements = "0123456789abcdef"; + uint32 value = 0; + for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) { + for (std::string::iterator secondDigitIter = hexElements.begin(); secondDigitIter != hexElements.end(); ++secondDigitIter) { + std::string hexToTest = prepend + *firstDigitIter + *secondDigitIter; + TEST_ASSERT(hextoi(hexToTest.c_str()) == value); + value++; + } + } + } + + + void nullptr64Test() { + TEST_ASSERT(hextoi64(nullptr) == 0); + } + + void ShortString64Test() { + // if the string is too short then it should + // spit out a zero. + // strings should be formatted: 0x** or 0X** + TEST_ASSERT(hextoi64("") == 0); + TEST_ASSERT(hextoi64("0") == 0); + TEST_ASSERT(hextoi64("01") == 0); + } + + void SingleDigitUpper64Test() { + std::string prepend = "0x"; + std::string hexToTest; + + std::string hexElements = "0123456789ABCDEF"; + uint64 value = 0; + for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) { + std::string hexToTest = prepend + *firstDigitIter; + TEST_ASSERT(hextoi64(hexToTest.c_str()) == value); + value++; + } + } + + void SingleDigitLower64Test() { + + std::string prepend = "0x"; + std::string hexToTest; + + std::string hexElements = "0123456789abcdef"; + uint64 value = 0; + for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) { + std::string hexToTest = prepend + *firstDigitIter; + TEST_ASSERT(hextoi64(hexToTest.c_str()) == value); + value++; + } + } + + // A bit excessive to do an exhaustive test like this + // but it usefully tests multi digit hex. + void DoubleDigitUpper64Test() { + + std::string prepend = "0x"; + std::string hexToTest; + + std::string hexElements = "0123456789ABCDEF"; + uint64 value = 0; + for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) { + for (std::string::iterator secondDigitIter = hexElements.begin(); secondDigitIter != hexElements.end(); ++secondDigitIter) { + std::string hexToTest = prepend + *firstDigitIter + *secondDigitIter; + TEST_ASSERT(hextoi64(hexToTest.c_str()) == value); + value++; + } + } + } + + // A bit excessive to do an exhaustive test like this + // but it usefully tests multi digit hex. + void DoubleDigitLower64Test() { + std::string prepend = "0x"; + std::string hexToTest; + + std::string hexElements = "0123456789abcdef"; + uint64 value = 0; + for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) { + for (std::string::iterator secondDigitIter = hexElements.begin(); secondDigitIter != hexElements.end(); ++secondDigitIter) { + std::string hexToTest = prepend + *firstDigitIter + *secondDigitIter; + TEST_ASSERT(hextoi64(hexToTest.c_str()) == value); + value++; + } + } + } + +}; + +#endif diff --git a/tests/main.cpp b/tests/main.cpp index d12aa143c..fd0862edb 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -24,6 +24,8 @@ #include "ipc_mutex_test.h" #include "fixed_memory_test.h" #include "fixed_memory_variable_test.h" +#include "atobool_test.h" +#include "hextoi_32_64_test.h" int main() { try { @@ -34,6 +36,8 @@ int main() { tests.add(new IPCMutexTest()); tests.add(new FixedMemoryHashTest()); tests.add(new FixedMemoryVariableHashTest()); + tests.add(new atoboolTest()); + tests.add(new hextoi_32_64_Test()); tests.run(*output, true); } catch(...) { return -1; From 4170434b960e856bb19204cbe444c64a150076c5 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 17 Mar 2014 04:53:47 -0400 Subject: [PATCH 30/82] ** Fix for RoF clients not displaying Augment Restrictions in the Item Info window. ** Change to Client::SummonItem() to enforce valid item/augment combinations. (Run the optional sql file first, before posting any SummonItem() failure issues in the forums.) --- changelog.txt | 10 + common/eq_constants.h | 77 +++ common/patches/RoF.cpp | 3 +- common/patches/RoF_structs.h | 5 +- common/ruletypes.h | 6 + .../2014_03_17_EnforceAugmentRules.sql | 3 + zone/client.h | 2 +- zone/inventory.cpp | 461 ++++++++++++++---- 8 files changed, 460 insertions(+), 107 deletions(-) create mode 100644 utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql diff --git a/changelog.txt b/changelog.txt index e7973ed6d..23374f83c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,15 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/17/2014 == +Uleat: Updated Client::SummonItem() to check for valid item combinations when augmentations are passed. +Uleat: Changed the return type of Client::SummonItem() from void to bool. Calling methods and APIs have not been update as yet. +Uleat: Fixed the RoF Item structure to properly pass the 'augrestrict' variable. RoF clients now show restrictions in the Item Information window. + +Optional SQL: 2014/03/17_EnforceAugmentRules.sql +Note: This adds the rules Inventory:EnforceAugmentRestriction, Inventory:EnforceAugmentUsability and Inventory:EnforceAugmentWear. + If you run into script/recipe issues, running this sql file will set the default enforcement rules to false. + If you still run into issues, you may want to check that your scripts are not trying to augment non-common items. + Please post any failures as bugs and be sure to include the base item ID, as well as any augment IDs that you are using. == 03/12/2014 == Kayen: Melee/Magic runes are now calculated as bonuses. Resolved issues with runes not working and not fading properly. diff --git a/common/eq_constants.h b/common/eq_constants.h index d612017ad..8b4db6fc3 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -137,6 +137,83 @@ enum ItemUseTypes : uint8 */ }; +/* +** Augmentation use types (in-work) +** +** (ref: dbstr_us.txt) +** +*/ +enum AugmentationUseTypes : uint32 { + AugTypeNone = 0, // not 100% sure on this... + AugTypeGeneralSingleStat, /*1^16^1 (General: Single Stat)^0*/ + AugTypeGeneralMultipleStat, /*2^16^2 (General: Multiple Stat)^0*/ + AugTypeGeneralSpellEffect, /*3^16^3 (General: Spell Effect)^0*/ + AugTypeWeaponGeneral, /*4^16^4 (Weapon: General)^0*/ + AugTypeWeaponElemDamage, /*5^16^5 (Weapon: Elem Damage)^0*/ + AugTypeWeaponBaseDamage, /*6^16^6 (Weapon: Base Damage)^0*/ + AugTypeGeneralGroup, /*7^16^7 (General: Group)^0*/ + AugTypeGeneralRaid, /*8^16^8 (General: Raid)^0*/ + AugTypeGeneralDragonsPoints, /*9^16^9 (General: Dragons Points)^0*/ + AugTypeCraftedCommon, /*10^16^10 (Crafted: Common)^0*/ + AugTypeCraftedGroup1, /*11^16^11 (Crafted: Group)^0*/ + AugTypeCraftedRaid1, /*12^16^12 (Crafted: Raid)^0*/ + AugTypeEnergeiacGroup, /*13^16^13 (Energeiac: Group)^0*/ + AugTypeEnergeiacRaid, /*14^16^14 (Energeiac: Raid)^0*/ + AugTypeEmblem, /*15^16^15 (Emblem)^0*/ + AugTypeCraftedGroup2, /*16^16^16 (Crafted: Group)^0*/ + AugTypeCraftedRaid2, /*17^16^17 (Crafted: Raid)^0*/ + AugTypeUnknown1, /*18^16^18^0*/ + AugTypeUnknown2, /*19^16^19^0*/ + AugTypeOrnamentation, /*20^16^20 (Ornamentation)^0*/ + AugTypeSpecialOrnamentation, /*21^16^21 (Special Ornamentation)^0*/ + AugTypeUnknown3, /*22^16^22^0*/ + AugTypeUnknown4, /*23^16^23^0*/ + AugTypeUnknown5, /*24^16^24^0*/ + AugTypeUnknown6, /*25^16^25^0*/ + AugTypeUnknown7, /*26^16^26^0*/ + AugTypeUnknown8, /*27^16^27^0*/ + AugTypeUnknown9, /*28^16^28^0*/ + AugTypeUnknown10, /*29^16^29^0*/ + AugTypeEpic25, /*30^16^30^0*/ + AugTypeTest, /*31^16^Test^0*/ // listed as 31^16^31^0 in 5-10 client + _AugTypeCount +}; + +/* +** Augmentation restriction types (in-work) +** +** (ref: eqstr_us.txt) +** +*/ +enum AugmentationRestrictionTypes : uint8 { +/*4690*/ AugRestrAny = 0, +/*9134*/ AugRestrArmor, +/*9135*/ AugRestrWeapons, +/*9136*/ AugRestr1HWeapons, +/*9137*/ AugRestr2HWeapons, +/*9138*/ AugRestr1HSlash, +/*9139*/ AugRestr1HBlunt, +/*9140*/ AugRestrPiercing, +/*9148*/ AugRestrHandToHand, +/*9141*/ AugRestr2HSlash, +/*9142*/ AugRestr2HBlunt, +/*9143*/ AugRestr2HPierce, +/*9144*/ AugRestrBows, +/*9145*/ AugRestrShields, +/*8052*/ AugRestr1HSlash1HBluntOrHandToHand, +/*9200*/ AugRestr1HBluntOrHandToHand, // no listed peq entries + +// these three appear to be post-RoF (12-10-2012) and can not be verified until RoF (05-10-2013) is supported +/*????*/ AugRestrUnknown1, +/*????*/ AugRestrUnknown2, +/*????*/ AugRestrUnknown3, // last value in peq entries + _AugRestrCount + +/*4687*/ //AugTypeAllItems, // ?? unknown atm +/*4688*/ //AugTypePrestige, // ?? unknown atm +/*4689*/ //AugTypeNonPrestige, // ?? unknown atm +}; + /* ** Container use types ** diff --git a/common/patches/RoF.cpp b/common/patches/RoF.cpp index 07cea6dfb..cbe228c92 100644 --- a/common/patches/RoF.cpp +++ b/common/patches/RoF.cpp @@ -5070,8 +5070,9 @@ char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint memset(&isbs, 0, sizeof(RoF::structs::ItemSecondaryBodyStruct)); isbs.augtype = item->AugType; - isbs.augrestrict = item->AugRestrict; isbs.augdistiller = 0; + isbs.augrestrict = item->AugRestrict; + for(int x = 0; x < 5; ++x) { diff --git a/common/patches/RoF_structs.h b/common/patches/RoF_structs.h index a2635ceae..918bbe68d 100644 --- a/common/patches/RoF_structs.h +++ b/common/patches/RoF_structs.h @@ -4427,8 +4427,11 @@ struct AugSlotStruct struct ItemSecondaryBodyStruct { uint32 augtype; - uint32 augrestrict; + // swapped augrestrict and augdistiller positions + // (this swap does show the proper augment restrictions in Item Information window now) + // unsure what the purpose of augdistiller is at this time -U 3/17/2014 uint32 augdistiller; // New to December 10th 2012 client - NEW + uint32 augrestrict; AugSlotStruct augslots[6]; uint32 ldonpoint_type; diff --git a/common/ruletypes.h b/common/ruletypes.h index 83324d533..83db6c3f0 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -537,6 +537,12 @@ RULE_BOOL( QueryServ, MerchantLogTransactions, false) // Logs Merchant Transacti RULE_BOOL( QueryServ, PlayerLogPCCoordinates, false) // Logs Player Coordinates with certain events RULE_CATEGORY_END() +RULE_CATEGORY( Inventory ) +RULE_BOOL ( Inventory, EnforceAugmentRestriction, true) // Forces augment slot restrictions +RULE_BOOL ( Inventory, EnforceAugmentUsability, true) // Forces augmented item usability +RULE_BOOL ( Inventory, EnforceAugmentWear, true) // Forces augment wear slot validation +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql b/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql new file mode 100644 index 000000000..e89d4a437 --- /dev/null +++ b/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql @@ -0,0 +1,3 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentRestriction', 'false', 'Forces augment slot restrictions.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentUsability', 'false', 'Forces augmented item usability.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentWear', 'false', 'Forces augment wear slot validation.'); \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index 10465dbc6..97ee1b32f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -794,7 +794,7 @@ public: void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false); void PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data = 0); bool AutoPutLootInInventory(ItemInst& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0); - void SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR); + bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR); void SetStats(uint8 type,int16 set_val); void IncStats(uint8 type,int16 increase_val); void DropItem(int16 slot_id); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 865eaa8e5..c6f1663da 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -200,124 +200,377 @@ bool Client::CheckLoreConflict(const Item_Struct* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != SLOT_INVALID); } -void Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { +bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { + // I have 'over-logged' failure messages to aid in any troubleshooting of issues that arise from this change. + // The 'LogFile' code may be taken out once after a period of time with no script/recipe issues. + // + // I have also incorporated a bool return type..but, have not updated any calling methods to process failures. + // Once the APIs are updated, scripts may also be updated. + const Item_Struct* item = database.GetItem(item_id); - if (item == nullptr) { - Message(0, "No such item: %i", item_id); - return; - } else { + if(item == nullptr) { + Message(13, "Item %u does not exist.", item_id); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + else { // if the item is stackable and the charge amount is -1 or 0 then set to 1 charge. // removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant - if (charges <= 0 && item->Stackable) { + if(charges <= 0 && item->Stackable) charges = 1; - // if the charges is -1, then no charge value was passed in set to max charges - } else if(charges == -1) { - charges = item->MaxCharges; - // in any other situation just use charges as passed - } - } - // Checking to see if the Item is lore or not. - bool foundlore = CheckLoreConflict(item); - //TODO: check for lore conflict on augments + // if the charges is -1, then no charge value was passed in set to max charges + else if(charges == -1) + charges = item->MaxCharges; + + // in any other situation just use charges as passed + } + + // Base item is lore..so, cancel summon + if(CheckLoreConflict(item)) { + // this is the 'you can not pickup another...' message. I don't feel this is appropriate + // for a summoning failure response + // DuplicateLoreMessage(item_id); + Message(13, "You already have a lore %s (%i) in your inventory.", item->Name, item_id); + + return false; + } // Checking to see if it is a GM only Item or not. - //bool foundgm = (item->gm && (this->Admin() < 100)); - bool foundgm = false; + /* + if(item->gm && (this->Admin() < 100)) + Message(0, "You are not a GM and can not summon this item!"); - if (!foundlore && !foundgm) { // Okay, It isn't LORE, or if it is, it is not in player's inventory. - ItemInst* inst = database.CreateItem(item, charges); - if (inst) { - // Corrected the augment references to reflect augment name/id instead of base item name/id - if (aug1) { - const Item_Struct* augitem1 = database.GetItem(aug1); - if (augitem1) { - if (!CheckLoreConflict(augitem1)) { - inst->PutAugment(&database, 0, aug1); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem1->Name, aug1); - } - } - } - if (aug2) { - const Item_Struct* augitem2 = database.GetItem(aug2); - if (augitem2) { - if (!CheckLoreConflict(augitem2)) { - inst->PutAugment(&database, 1, aug2); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem2->Name, aug2); - } - } - } - if (aug3) { - const Item_Struct* augitem3 = database.GetItem(aug3); - if (augitem3) { - if (!CheckLoreConflict(augitem3)) { - inst->PutAugment(&database, 2, aug3); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem3->Name, aug3); - } - } - } - if (aug4) { - const Item_Struct* augitem4 = database.GetItem(aug4); - if (augitem4) { - if (!CheckLoreConflict(augitem4)) { - inst->PutAugment(&database, 3, aug4); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem4->Name, aug4); - } - } - } - if (aug5) { - const Item_Struct* augitem5 = database.GetItem(aug5); - if (augitem5) { - if (!CheckLoreConflict(augitem5)) { - inst->PutAugment(&database, 4, aug5); - } - else { - Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem5->Name, aug5); - } - } - } - if (attuned) { - if (inst->GetItem()->Attuneable) { - inst->SetInstNoDrop(true); - } - } - if (to_slot == SLOT_CURSOR) - { - //inst->SetCharges( - PushItemOnCursor(*inst); - // Send item packet to user - SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem); - } - else - { - PutItemInInventory(to_slot, *inst, true); - } - safe_delete(inst); + return false; + */ - if ((RuleB(Character, EnableDiscoveredItems))) - { - if(!GetGM() && !IsDiscovered(item_id)) - DiscoverItem(item_id); + if(((item->ItemClass != ItemClassCommon) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5)) { + Message(13, "You can not augment an augment or a non-common class item."); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an augment or a non-common class item.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + uint32 augments[MAX_AUGMENT_SLOTS] = { aug1, aug2, aug3, aug4, aug5 }; + + for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { + if(augments[iter] && (database.GetItem(augments[iter]) == nullptr)) { + Message(13, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an augment (Aug%i) with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + } + + uint32 classes = item->Classes; + uint32 races = item->Races; + uint32 slots = item->Slots; + + bool enforcewear = RuleB(Inventory, EnforceAugmentWear); + bool enforcerestr = RuleB(Inventory, EnforceAugmentRestriction); + bool enforceusable = RuleB(Inventory, EnforceAugmentUsability); + + for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { + const Item_Struct* augtest = database.GetItem(augments[iter]); + + if(augtest) { + if(CheckLoreConflict(augtest)) { + // ditto + // DuplicateLoreMessage(augtest->ID); + Message(13, "You already have a lore %s (%u) in your inventory.", augtest->Name, augtest->ID); + + return false; + } + + /* + if(augtest->gm && (this->Admin() < 100)) { + Message(0, "You are not a GM and can not summon this augment!"); + + return false; + } + */ + + if(augtest->AugType == 0) { + Message(13, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to use a non-augment item (Aug%i) as an augment.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, iter + 1, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + if(enforcewear) { + if((item->AugSlotType[iter] == AugTypeNone) || !(((uint32)1 << (item->AugSlotType[iter] - 1)) & augtest->AugType)) { + Message(13, "Augment %u (Aug%i) is not allowed wear on Item %u.", augments[iter], iter + 1, item->ID); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an item with a disallowed augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + } + + if(enforcerestr) { + bool restrictfail = false; + uint8 it = item->ItemType; + + switch(augtest->AugRestrict) { + case AugRestrAny: + break; + case AugRestrArmor: + switch(it) { + case ItemTypeArmor: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrWeapons: + switch(it) { + case ItemType1HSlash: + case ItemType1HBlunt: + case ItemType1HPiercing: + case ItemTypeMartial: + case ItemType2HSlash: + case ItemType2HBlunt: + case ItemType2HPiercing: + case ItemTypeBow: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HWeapons: + switch(it) { + case ItemType1HSlash: + case ItemType1HBlunt: + case ItemType1HPiercing: + case ItemTypeMartial: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr2HWeapons: + switch(it) { + case ItemType2HSlash: + case ItemType2HBlunt: + case ItemType2HPiercing: + case ItemTypeBow: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HSlash: + switch(it) { + case ItemType1HSlash: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HBlunt: + switch(it) { + case ItemType1HBlunt: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrPiercing: + switch(it) { + case ItemType1HPiercing: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrHandToHand: + switch(it) { + case ItemTypeMartial: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr2HSlash: + switch(it) { + case ItemType2HSlash: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr2HBlunt: + switch(it) { + case ItemType2HBlunt: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr2HPierce: + switch(it) { + case ItemType2HPiercing: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrBows: + switch(it) { + case ItemTypeBow: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestrShields: + switch(it) { + case ItemTypeShield: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HSlash1HBluntOrHandToHand: + switch(it) { + case ItemType1HSlash: + case ItemType1HBlunt: + case ItemTypeMartial: + break; + default: + restrictfail = true; + break; + } + break; + case AugRestr1HBluntOrHandToHand: + switch(it) { + case ItemType1HBlunt: + case ItemTypeMartial: + break; + default: + restrictfail = true; + break; + } + break; + // These 3 are in-work + case AugRestrUnknown1: + case AugRestrUnknown2: + case AugRestrUnknown3: + default: + restrictfail = true; + break; + } + + if(restrictfail) { + Message(13, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], iter + 1, item->ID); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an item with a restricted augment (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + } + + if(enforceusable) { + classes &= augtest->Classes; + + if(item->Classes && !classes) { + Message(13, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable by any class.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + races &= augtest->Races; + + if(item->Races && !races) { + Message(13, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable by any race.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + slots &= augtest->Slots; + + if(item->Slots && !slots) { + Message(13, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], iter + 1); + LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable in any slot.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } } } } - else { // Item was already in inventory & is a LORE item or was a GM only item. Give them a message about it. - if (foundlore){ - DuplicateLoreMessage(item_id); - //Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id); - } - else if (foundgm) - Message(0, "You are not a GM to summon this item"); + + // Item is not lore.. + // Item is not GM-Only.. + // Augments are valid and match allowed slots.. + // ..or Augments are valid and do not match allowed slots and.. + // ..EnforceAugmentWear=false and EnforceAugmentRestriction=false and EnforceAugmentUsability=false + + ItemInst* inst = database.CreateItem(item, charges); + + if(inst == nullptr) { + Message(13, "An unknown server error has occurred and your item was not created."); + LogFile->write(EQEMuLog::Error, "Player %s on account %s encountered an unknown item creation error.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; } + + for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { + if(augments[iter]) + inst->PutAugment(&database, iter, augments[iter]); + } + + // This may need augment checks as well..left out for now + if(attuned && inst->GetItem()->Attuneable) + inst->SetInstNoDrop(true); + + if(to_slot == SLOT_CURSOR) { + //inst->SetCharges( + PushItemOnCursor(*inst); + // Send item packet to user + SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem); + } + else { + PutItemInInventory(to_slot, *inst, true); + } + + safe_delete(inst); + + if((RuleB(Character, EnableDiscoveredItems)) && !GetGM()) { + if(!IsDiscovered(item_id)) + DiscoverItem(item_id); + + for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { + if(augments[iter] && !IsDiscovered(augments[iter])) + DiscoverItem(augments[iter]); + } + } + + return true; } // Drop item from inventory to ground (generally only dropped from SLOT_CURSOR) From a84862897ac15b863ac88f6a4a789bd106452b09 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 18 Mar 2014 06:00:46 -0400 Subject: [PATCH 31/82] Fix for name/account discrepancy in \\zone\inventory.cpp. Rearranged/condensed code snippets in Client::SummonItem(). Added 'augslotvisible' check to augment validation in C::SI(). --- changelog.txt | 5 ++ zone/inventory.cpp | 199 ++++++++++++++++++++++++--------------------- 2 files changed, 111 insertions(+), 93 deletions(-) diff --git a/changelog.txt b/changelog.txt index 23374f83c..08fc5dd27 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/18/2014 == +Uleat: Fixed the name/account discrepancy in the Client::SummonItem() code as well as the origin of the mistake (thanks K_K!) +Uleat: Condensed and rearranged certain snippets of code in SummonItem(). Added a 'augslotvisible' check to validation check. +Note: If you are experiencing issues with SummonItem, please enable 'INVENTORY_ERROR' logging if it not active on your server. + == 03/17/2014 == Uleat: Updated Client::SummonItem() to check for valid item combinations when augmentations are passed. Uleat: Changed the return type of Client::SummonItem() from void to bool. Calling methods and APIs have not been update as yet. diff --git a/zone/inventory.cpp b/zone/inventory.cpp index c6f1663da..b18514056 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -201,72 +201,45 @@ bool Client::CheckLoreConflict(const Item_Struct* item) { } bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { - // I have 'over-logged' failure messages to aid in any troubleshooting of issues that arise from this change. - // The 'LogFile' code may be taken out once after a period of time with no script/recipe issues. - // - // I have also incorporated a bool return type..but, have not updated any calling methods to process failures. - // Once the APIs are updated, scripts may also be updated. + // TODO: update calling methods and script apis to handle a failure return const Item_Struct* item = database.GetItem(item_id); + // make sure the item exists if(item == nullptr) { Message(13, "Item %u does not exist.", item_id); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } - else { - // if the item is stackable and the charge amount is -1 or 0 then set to 1 charge. - // removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant - if(charges <= 0 && item->Stackable) - charges = 1; - - // if the charges is -1, then no charge value was passed in set to max charges - else if(charges == -1) - charges = item->MaxCharges; - - // in any other situation just use charges as passed - } - - // Base item is lore..so, cancel summon - if(CheckLoreConflict(item)) { - // this is the 'you can not pickup another...' message. I don't feel this is appropriate - // for a summoning failure response + // check that there is not a lore conflict between base item and existing inventory + else if(CheckLoreConflict(item)) { // DuplicateLoreMessage(item_id); Message(13, "You already have a lore %s (%i) in your inventory.", item->Name, item_id); return false; } + // check to make sure we are augmenting an augmentable item + else if(((item->ItemClass != ItemClassCommon) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5)) { + Message(13, "You can not augment an augment or a non-common class item."); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an augment or a non-common class item.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); - // Checking to see if it is a GM only Item or not. + return false; + } + // check to make sure we are a GM if the item is GM-only /* - if(item->gm && (this->Admin() < 100)) - Message(0, "You are not a GM and can not summon this item!"); + else if(item->gm && (this->Admin() < 100)) + Message(13, "You are not a GM and can not summon this item."); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only item with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5); return false; */ - if(((item->ItemClass != ItemClassCommon) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5)) { - Message(13, "You can not augment an augment or a non-common class item."); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an augment or a non-common class item.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); - - return false; - } - uint32 augments[MAX_AUGMENT_SLOTS] = { aug1, aug2, aug3, aug4, aug5 }; - for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { - if(augments[iter] && (database.GetItem(augments[iter]) == nullptr)) { - Message(13, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an augment (Aug%i) with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); - - return false; - } - } - uint32 classes = item->Classes; uint32 races = item->Races; uint32 slots = item->Slots; @@ -278,41 +251,62 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { const Item_Struct* augtest = database.GetItem(augments[iter]); - if(augtest) { + if(augtest == nullptr) { + if(augments[iter]) { + Message(13, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an augment (Aug%i) with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + } + else { + // check that there is not a lore conflict between augment and existing inventory if(CheckLoreConflict(augtest)) { - // ditto // DuplicateLoreMessage(augtest->ID); Message(13, "You already have a lore %s (%u) in your inventory.", augtest->Name, augtest->ID); return false; } - + // check that augment is an actual augment + else if(augtest->AugType == 0) { + Message(13, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to use a non-augment item (Aug%i) as an augment.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, (iter + 1), aug1, aug2, aug3, aug4, aug5); + + return false; + } + // check to make sure we are a GM if the augment is GM-only /* - if(augtest->gm && (this->Admin() < 100)) { - Message(0, "You are not a GM and can not summon this augment!"); + else if(augtest->gm && (this->Admin() < 100)) { + Message(13, "You are not a GM and can not summon this augment."); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only augment (Aug%i) with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5); return false; } */ - if(augtest->AugType == 0) { - Message(13, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to use a non-augment item (Aug%i) as an augment.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, iter + 1, aug1, aug2, aug3, aug4, aug5); - - return false; - } - + // check for augment type allowance if(enforcewear) { if((item->AugSlotType[iter] == AugTypeNone) || !(((uint32)1 << (item->AugSlotType[iter] - 1)) & augtest->AugType)) { - Message(13, "Augment %u (Aug%i) is not allowed wear on Item %u.", augments[iter], iter + 1, item->ID); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an item with a disallowed augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + Message(13, "Augment %u (Aug%i) is not acceptable wear on Item %u.", augments[iter], iter + 1, item->ID); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an item with an unacceptable augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5); + + return false; + } + + if(item->AugSlotVisible[iter] == 0) { + Message(13, "Item %u has not evolved enough to accept Augment %u (Aug%i).", item->ID, augments[iter], iter + 1); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an unevolved item with augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5); return false; } } + // check for augment to item restriction if(enforcerestr) { bool restrictfail = false; uint8 it = item->ItemType; @@ -481,41 +475,38 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, } if(restrictfail) { - Message(13, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], iter + 1, item->ID); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to augment an item with a restricted augment (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), iter + 1, item->ID, aug1, aug2, aug3, aug4, aug5); + Message(13, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], (iter + 1), item->ID); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an item with a restricted augment (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5); return false; } } if(enforceusable) { - classes &= augtest->Classes; - - if(item->Classes && !classes) { - Message(13, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable by any class.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + // check for class usability + if(item->Classes && !(classes &= augtest->Classes)) { + Message(13, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], (iter + 1)); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable by any class.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } - races &= augtest->Races; - - if(item->Races && !races) { - Message(13, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable by any race.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + // check for race usability + if(item->Races && !(races &= augtest->Races)) { + Message(13, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], (iter + 1)); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable by any race.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } - slots &= augtest->Slots; - - if(item->Slots && !slots) { - Message(13, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], iter + 1); - LogFile->write(EQEMuLog::Error, "Player %s on account %s attempted to create an item unusable in any slot.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + // check for slot usability + if(item->Slots && !(slots &= augtest->Slots)) { + Message(13, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], (iter + 1)); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable in any slot.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } @@ -523,35 +514,56 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, } } - // Item is not lore.. - // Item is not GM-Only.. - // Augments are valid and match allowed slots.. - // ..or Augments are valid and do not match allowed slots and.. - // ..EnforceAugmentWear=false and EnforceAugmentRestriction=false and EnforceAugmentUsability=false + // validation passed..so, set the charges and create the actual item + + // if the item is stackable and the charge amount is -1 or 0 then set to 1 charge. + // removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant + if(charges <= 0 && item->Stackable) + charges = 1; + + // if the charges is -1, then no charge value was passed in set to max charges + else if(charges == -1) + charges = item->MaxCharges; + + // in any other situation just use charges as passed ItemInst* inst = database.CreateItem(item, charges); if(inst == nullptr) { Message(13, "An unknown server error has occurred and your item was not created."); + // this goes to logfile since this is a major error LogFile->write(EQEMuLog::Error, "Player %s on account %s encountered an unknown item creation error.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - account_name, GetName(), item->ID, aug1, aug2, aug3, aug4, aug5); + GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); return false; } + // add any validated augments for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) { if(augments[iter]) inst->PutAugment(&database, iter, augments[iter]); } - // This may need augment checks as well..left out for now + // attune item if(attuned && inst->GetItem()->Attuneable) inst->SetInstNoDrop(true); + // check to see if item is usable in requested slot + if(enforceusable && (((to_slot >= 0) && (to_slot <= 21)) || (to_slot == 9999))) { + uint32 slottest = (to_slot == 9999) ? 22 : to_slot; + + if(!(slots & ((uint32)1 << slottest))) { + Message(0, "This item is not equipable at slot %u - moving to cursor.", to_slot); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to equip an item unusable in slot %u - moved to cursor.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", + GetName(), account_name, to_slot, item->ID, aug1, aug2, aug3, aug4, aug5); + + to_slot = SLOT_CURSOR; + } + } + + // put item into inventory if(to_slot == SLOT_CURSOR) { - //inst->SetCharges( PushItemOnCursor(*inst); - // Send item packet to user SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem); } else { @@ -560,6 +572,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, safe_delete(inst); + // discover item and any augments if((RuleB(Character, EnableDiscoveredItems)) && !GetGM()) { if(!IsDiscovered(item_id)) DiscoverItem(item_id); @@ -1373,7 +1386,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { //verify shared bank transactions in the database if(src_inst && src_slot_id >= 2500 && src_slot_id <= 2550) { if(!database.VerifyInventory(account_id, src_slot_id, src_inst)) { - LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", account_name, GetName()); + LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(dst_slot_id,0,true); return(false); } @@ -1388,7 +1401,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } if(dst_inst && dst_slot_id >= 2500 && dst_slot_id <= 2550) { if(!database.VerifyInventory(account_id, dst_slot_id, dst_inst)) { - LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", account_name, GetName()); + LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(src_slot_id,0,true); return(false); } From 02e291d4e878e81611b32827451f50dfa788a8d7 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 20 Mar 2014 00:53:49 -0400 Subject: [PATCH 32/82] Further refinements to root, charm, mez and fear behaviors. Updates to a few rule due to new/corrected parse data. All behaviors defined from weeks of extensive live parsing Root Break Chance from DD now will scale based on level difference. Root has a baseline aproximately 6% chance to break per check when target has at 0% chance to resist spells.(ie green cons 60 levels lower with tash). Fear has an approximately 70% chance to trigger a resist check each tick to determine if it will fade early. (no baseline break chance) Charisma less than 100, gives -20 resist mod to intial fear casts Charisma from 100 to 255 will progressively reduce this mod to 0. Charisma DOES NOT effect UNDEAD fears Charmisma less than 75 significantly increase CHARM/MEZ/LULL resist rates. Mez spells will now also use charisma resist check, as they do on live. --- changelog.txt | 6 ++ common/ruletypes.h | 7 +- common/spdat.cpp | 2 +- .../git/optional/2014_03_19_RulesUpdates.sql | 6 ++ zone/aggro.cpp | 2 +- zone/attack.cpp | 63 +++++++++-------- zone/mob.h | 4 +- zone/spell_effects.cpp | 20 +++++- zone/spells.cpp | 67 +++++++++++++++---- 9 files changed, 127 insertions(+), 50 deletions(-) create mode 100644 utils/sql/git/optional/2014_03_19_RulesUpdates.sql diff --git a/changelog.txt b/changelog.txt index 08fc5dd27..e2805115e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +Kayen: Further refinements to root, charm, mez and fear behaviors - See commit message for full details + +New rule for 'Fear' break chance, and updates to default settings of various rules. +Optional SQL: utils/sql/git/optional/2014_03_19_RulesUpdates.sql + + == 03/18/2014 == Uleat: Fixed the name/account discrepancy in the Client::SummonItem() code as well as the origin of the mistake (thanks K_K!) Uleat: Condensed and rearranged certain snippets of code in SummonItem(). Added a 'augslotvisible' check to validation check. diff --git a/common/ruletypes.h b/common/ruletypes.h index 83db6c3f0..fd50d1514 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -290,17 +290,18 @@ RULE_BOOL ( Spells, NPCIgnoreBaseImmunity, true) // Whether or not NPCs get to i RULE_REAL ( Spells, AvgSpellProcsPerMinute, 6.0) //Adjust rate for sympathetic spell procs RULE_INT ( Spells, ResistFalloff, 67) //Max that level that will adjust our resist chance based on level modifiers RULE_INT ( Spells, CharismaEffectiveness, 10) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. -RULE_INT ( Spells, CharismaEffectivenessCap, 200) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. +RULE_INT ( Spells, CharismaEffectivenessCap, 255) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. RULE_BOOL ( Spells, CharismaCharmDuration, false) // Allow CHA resist mod to extend charm duration. RULE_INT ( Spells, CharmBreakCheckChance, 25) //Determines chance for a charm break check to occur each buff tick. RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time can be reduced by spell haste -RULE_INT ( Spells, RootBreakFromSpells, 20) //Chance for root to break when cast on. +RULE_INT ( Spells, RootBreakFromSpells, 55) //Chance for root to break when cast on. RULE_INT ( Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing. RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount. RULE_BOOL ( Spells, AdditiveBonusValues, false) //Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects) RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this? RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live -RULE_INT ( Spells, RootBreakCheckChance, 40) //Determines chance for a root break check to occur each buff tick. +RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick. +RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/common/spdat.cpp b/common/spdat.cpp index 0f4387b83..c75acc056 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -390,7 +390,7 @@ bool IsPartialCapableSpell(uint16 spell_id) if (spells[spell_id].no_partial_resist) return false; - if (IsPureNukeSpell(spell_id) || IsFearSpell(spell_id)) + if (IsPureNukeSpell(spell_id)) return true; return false; diff --git a/utils/sql/git/optional/2014_03_19_RulesUpdates.sql b/utils/sql/git/optional/2014_03_19_RulesUpdates.sql new file mode 100644 index 000000000..d360528e6 --- /dev/null +++ b/utils/sql/git/optional/2014_03_19_RulesUpdates.sql @@ -0,0 +1,6 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FearBreakCheckChance', '70', 'Chance for fear to do a resist check each tick. Decrease for longer fears.'); + +-- Updates rule value if server is using the OLD DEFAULT values +UPDATE rule_values SET rule_value = 55 WHERE rule_name LIKE 'Spells:RootBreakFromSpells' AND rule_value = 20; +UPDATE rule_values SET rule_value = 70 WHERE rule_name LIKE 'Spells:RootBreakCheckChance' AND rule_value = 40; +UPDATE rule_values SET rule_value = 255 WHERE rule_name LIKE 'Spells:CharismaResistCap' AND rule_value = 200; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 71956a535..144f7363e 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1398,7 +1398,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { /* Charm formula is correct based on over 50 hours of personal live parsing - Kayen - Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA + Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 CHA) Charisma DOES NOT extend charm durations. Base effect value of charm spells in the spell file DOES NOT effect duration OR resist rate (unclear if does anything) Charm has a lower limit of 5% chance to break per tick, regardless of resist modifiers / level difference. diff --git a/zone/attack.cpp b/zone/attack.cpp index ccbc3a8f8..be099adc6 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3680,7 +3680,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons if(spell_id != SPELL_UNKNOWN && !iBuffTic) { //see if root will break if (IsRooted() && !FromDamageShield) // neotoyko: only spells cancel root - TryRootFadeByDamage(buffslot); + TryRootFadeByDamage(buffslot, attacker); } else if(spell_id == SPELL_UNKNOWN) { @@ -4548,34 +4548,43 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, float chance) } } -bool Mob::TryRootFadeByDamage(int buffslot) -{ - /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 - The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. - There is no distinction of any kind between the caster inflicted damage, or anyone - else's damage. There is also no distinction between Direct and DOT damage in the root code. - There is however, a provision that if the damage inflicted is greater than 500 per hit, the - chance to break root is increased. My guess is when this code was put in place, the devs at - the time couldn't imagine DOT damage getting that high. - */ - - /* General Mechanics - - Check buffslot to make sure damage from a root does not cancel the root - - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. - - Only roots on determental spells can be broken by damage. - */ - - if (!spellbonuses.Root[0] || spellbonuses.Root[1] < 0) - return false; - - if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ - - int BreakChance = RuleI(Spells, RootBreakFromSpells); +bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { + + /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 + The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. + There is no distinction of any kind between the caster inflicted damage, or anyone + else's damage. There is also no distinction between Direct and DOT damage in the root code. + + /* General Mechanics + - Check buffslot to make sure damage from a root does not cancel the root + - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. + - Only roots on determental spells can be broken by damage. + - Root break chance values obtained from live parses. + */ + + if (!attacker || !spellbonuses.Root[0] || spellbonuses.Root[1] < 0) + return false; + + if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ + + int BreakChance = RuleI(Spells, RootBreakFromSpells); - BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; + BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; + int level_diff = attacker->GetLevel() - GetLevel(); - if (BreakChance < 1) - BreakChance = 1; + //Use baseline if level difference <= 1 (ie. If target is (1) level less than you, or equal or greater level) + + if (level_diff == 2) + BreakChance = (BreakChance * 80) /100; //Decrease by 20%; + + else if (level_diff >= 3 && level_diff <= 20) + BreakChance = (BreakChance * 60) /100; //Decrease by 40%; + + else if (level_diff > 21) + BreakChance = (BreakChance * 20) /100; //Decrease by 80%; + + if (BreakChance < 1) + BreakChance = 1; if (MakeRandomInt(0, 99) < BreakChance) { diff --git a/zone/mob.h b/zone/mob.h index c771d57b7..c8453d301 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -191,7 +191,7 @@ public: virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration){ return duration;} virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false, - int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false); + int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false); uint16 GetSpecializeSkillValue(uint16 spell_id) const; void SendSpellBarDisable(); void SendSpellBarEnable(uint16 spellid); @@ -593,7 +593,7 @@ public: void MeleeLifeTap(int32 damage); bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true); bool ImprovedTaunt(); - bool TryRootFadeByDamage(int buffslot); + bool TryRootFadeByDamage(int buffslot, Mob* attacker); void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index cebd548dd..7fd2f4332 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -830,14 +830,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Fear: %+i", effect_value); #endif - //use resistance value for duration... - buffs[buffslot].ticsremaining = ((buffs[buffslot].ticsremaining * partial) / 100); - if(IsClient()) { if(buffs[buffslot].ticsremaining > RuleI(Character, MaxFearDurationForPlayerCharacter)) buffs[buffslot].ticsremaining = RuleI(Character, MaxFearDurationForPlayerCharacter); } + if(RuleB(Combat, EnableFearPathing)){ if(IsClient()) @@ -3327,6 +3325,22 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){ + float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0,0,0,true); + + if(resist_check == 100) + break; + else + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + } + + break; + } + + case SE_Fear: + { + if (MakeRandomInt(0, 99) < RuleI(Spells, FearBreakCheckChance)){ + float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster); if(resist_check == 100) diff --git a/zone/spells.cpp b/zone/spells.cpp index 332906883..86d21ea7a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3409,7 +3409,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r // not all unresistable, so changing this to only check certain spells if(IsResistableSpell(spell_id)) { - if (IsCharmSpell(spell_id)) + if (IsCharmSpell(spell_id) || IsMezSpell(spell_id) || IsFearSpell(spell_id)) spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust,true); else spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust); @@ -4040,7 +4040,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) // pvp_resist_base // pvp_resist_calc // pvp_resist_cap -float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick) +float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick, bool IsRoot) { if(!caster) @@ -4079,8 +4079,10 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } //Check for fear resist + bool IsFear = false; if(IsFearSpell(spell_id)) { + IsFear = true; int fear_resist_bonuses = CalcFearResistChance(); if(MakeRandomInt(0, 99) < fear_resist_bonuses) { @@ -4089,7 +4091,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } } - if (!CharismaCheck){ + if (!CharmTick){ //Check for Spell Effect specific resistance chances (ie AA Mental Fortitude) int se_resist_bonuses = GetSpellEffectResistChance(spell_id); @@ -4232,15 +4234,38 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if (CharismaCheck) { - //Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA - //'Lull' spells only check charisma if inital cast is resisted to see if mob will aggro, same modifier/cap as above. - //Charisma DOES NOT extend charm durations. + /* + Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 cha) + Charisma less than ~ 75 gives a postive modifier to resist checks at approximate ratio of -10 CHA = +6 Resist. + Mez spells do same initial resist check as a above. + Lull spells only check charisma if inital cast is resisted to see if mob will aggro, same modifier/cap as above. + Charisma DOES NOT extend charm durations. + Fear resist chance is given a -20 resist modifier if CHA is < 100, from 100-255 it progressively reduces the negative mod to 0. + Fears verse undead DO NOT apply a charisma modifer. (Note: unknown Base1 values defined in undead fears do not effect duration). + */ int16 charisma = caster->GetCHA(); - if (charisma > RuleI(Spells, CharismaEffectivenessCap)) - charisma = RuleI(Spells, CharismaEffectivenessCap); + if (IsFear && (spells[spell_id].targettype != 10)){ - resist_modifier -= charisma/RuleI(Spells, CharismaEffectiveness); + if (charisma < 100) + resist_modifier -= 20; + + else if (charisma <= 255) + resist_modifier += (charisma - 100)/8; + } + + else { + + if (charisma >= 75){ + + if (charisma > RuleI(Spells, CharismaEffectivenessCap)) + charisma = RuleI(Spells, CharismaEffectivenessCap); + + resist_modifier -= (charisma - 75)/RuleI(Spells, CharismaEffectiveness); + } + else + resist_modifier += ((75 - charisma)/10) * 6; //Increase Resist Chance + } } //Lull spells DO NOT use regular resists on initial cast, instead they use a flat +15 modifier. Live parses confirm this. @@ -4266,10 +4291,26 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use resist_chance = spells[spell_id].MinResist; } - //Charm can not have less than 5% chance to fail. - if (CharmTick && (resist_chance < 10)) - resist_chance = 10; - + //Average charm duration agianst mobs with 0% chance to resist on LIVE is ~ 68 ticks. + //Minimum resist chance should be caclulated factoring in the RuleI(Spells, CharmBreakCheckChance) + if (CharmTick) { + + int min_charmbreakchance = ((100/RuleI(Spells, CharmBreakCheckChance))/66 * 100)*2; + + if (resist_chance < min_charmbreakchance) + resist_chance = min_charmbreakchance; + } + + //Average root duration agianst mobs with 0% chance to resist on LIVE is ~ 22 ticks (6% resist chance). + //Minimum resist chance should be caclulated factoring in the RuleI(Spells, RootBreakCheckChance) + if (IsRoot) { + + int min_rootbreakchance = ((100/RuleI(Spells, RootBreakCheckChance))/22 * 100)*2; + + if (resist_chance < min_rootbreakchance) + resist_chance = min_rootbreakchance; + } + //Finally our roll int roll = MakeRandomInt(0, 200); if(roll > resist_chance) From cfdd48b2a3cd05e2a70b4e0cb4a56697ae3a914c Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 22 Mar 2014 05:17:38 -0400 Subject: [PATCH 33/82] Fix for bot guild script failures. MOVED 'load_bots.sql' and 'drop_bots.sql' into this repository. (They are updated) --- changelog.txt | 10 + utils/sql/git/README | 5 +- utils/sql/git/bots/README | 3 + utils/sql/git/bots/drop_bots.sql | 21 ++ utils/sql/git/bots/load_bots.sql | 307 ++++++++++++++++++ ..._03_22_BotGuildMember_ScriptFailureFix.sql | 27 ++ 6 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 utils/sql/git/bots/README create mode 100644 utils/sql/git/bots/drop_bots.sql create mode 100644 utils/sql/git/bots/load_bots.sql create mode 100644 utils/sql/git/bots/required/2014_03_22_BotGuildMember_ScriptFailureFix.sql diff --git a/changelog.txt b/changelog.txt index e2805115e..0403a5c7f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,15 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/22/2014 == +Uleat: Moved the existing 'load_bots' and 'drop_bots' sqls into the emu git repository for the time being. Look to the + /utils/sql/git/bots/ folder to find them. The 'load_bots' sql has been updated to include the below fix, as well as + collecting the multiple files into one. This should allow HeidiSQL to now run it properly. (You will still need to + search for the optional scripts for the time being.) +Uleat: Fixed bot guild script failure. For existing bot databases only, use this sql file to update the affected table and view. + +Required Bot SQL: 2014_03_22_BotGuildMember_ScriptFailureFix.sql + +== 03/19/2014 == Kayen: Further refinements to root, charm, mez and fear behaviors - See commit message for full details New rule for 'Fear' break chance, and updates to default settings of various rules. diff --git a/utils/sql/git/README b/utils/sql/git/README index 12f97658a..9b433f638 100644 --- a/utils/sql/git/README +++ b/utils/sql/git/README @@ -9,4 +9,7 @@ What we'll do instead as follows: All updates will follow a specific format of YYYY_MM_DD_Desc.sql, this is so it's easy to sort. So the following is a good example of what I expect to see -2013_02_16_GitConversion.sql \ No newline at end of file +2013_02_16_GitConversion.sql + + +The new bots/ folder contains two sub-folders named optional/ and required/ for updates. \ No newline at end of file diff --git a/utils/sql/git/bots/README b/utils/sql/git/bots/README new file mode 100644 index 000000000..3f3c94cb1 --- /dev/null +++ b/utils/sql/git/bots/README @@ -0,0 +1,3 @@ +Use this new load_bots.sql to source bot information into your database. + +This file now contains all of the script information so that it may be run from inside of HeidiSQL. \ No newline at end of file diff --git a/utils/sql/git/bots/drop_bots.sql b/utils/sql/git/bots/drop_bots.sql new file mode 100644 index 000000000..d02425d73 --- /dev/null +++ b/utils/sql/git/bots/drop_bots.sql @@ -0,0 +1,21 @@ +DROP TABLE IF EXISTS `botbuffs`; +DROP TABLE IF EXISTS `botpetinventory`; +DROP TABLE IF EXISTS `botpetbuffs`; +DROP TABLE IF EXISTS `botpets`; +DROP TABLE IF EXISTS `botgroupmembers`; +DROP TABLE IF EXISTS `botgroup`; +DROP TABLE IF EXISTS `botgroups`; +DROP TABLE IF EXISTS `botinventory`; +DROP TABLE IF EXISTS `botguildmembers`; +DROP TABLE IF EXISTS `botstances`; +DROP TABLE IF EXISTS `bottimers`; +DROP TABLE IF EXISTS `bots`; +DROP VIEW IF EXISTS `vwGuildMembers`; +DROP VIEW IF EXISTS `vwBotCharacterMobs`; +DROP VIEW IF EXISTS `vwBotGroups`; + +delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1; + +delete from commands where command = 'bot'; + +update spawn2 set enabled = 0 where id in (59297,59298); \ No newline at end of file diff --git a/utils/sql/git/bots/load_bots.sql b/utils/sql/git/bots/load_bots.sql new file mode 100644 index 000000000..d5a358d69 --- /dev/null +++ b/utils/sql/git/bots/load_bots.sql @@ -0,0 +1,307 @@ +-- This is pretty much a straight copy of the original files with fixes applied. +-- HeidiSQL does not like sub-directory references, so this should now run from there. +-- The 'headers' are left in place for reference only. + +-- FILE: +-- source player_tables/botguildmembers.sql; +CREATE TABLE IF NOT EXISTS `botguildmembers` ( + `char_id` int(11) NOT NULL default '0', + `guild_id` mediumint(8) unsigned NOT NULL default '0', + `rank` tinyint(3) unsigned NOT NULL default '0', + `tribute_enable` tinyint(3) unsigned NOT NULL default '0', + `total_tribute` int(10) unsigned NOT NULL default '0', + `last_tribute` int(10) unsigned NOT NULL default '0', + `banker` tinyint(3) unsigned NOT NULL default '0', + `public_note` text NULL, + `alt` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`char_id`) +) ENGINE=InnoDB; + +-- FILE: +-- source player_tables/bots.sql; +update spawn2 set enabled = 1 where id in (59297,59298); + +INSERT INTO rule_values VALUES ('1', 'Bots:BotManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.'); +INSERT INTO rule_values VALUES ('1', 'Bots:BotFinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.'); +INSERT INTO rule_values VALUES ('1', 'Bots:CreateBotCount', '150', 'Number of bots that each account can create'); +INSERT INTO rule_values VALUES ('1', 'Bots:SpawnBotCount', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid'); +INSERT INTO rule_values VALUES ('1', 'Bots:BotQuest', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl'); +INSERT INTO rule_values VALUES ('1', 'Bots:BotGroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.'); +INSERT INTO rule_values VALUES ('1', 'Bots:BotSpellQuest', 'false', 'Anita Thrall\'s (Anita_Thrall.pl) Bot Spell Scriber quests.'); + +-- this is a hack fix to maintain the original file process +delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1; +INSERT INTO rule_values VALUES ('1', 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs'); + +-- field count has changed +-- INSERT INTO `commands` VALUES ('bot', '0', 'Type \"#bot help\" to the see the list of available commands for bots.'); +INSERT INTO `commands` VALUES ('bot', '0'); + +CREATE TABLE bots ( + `BotID` int(10) unsigned NOT NULL AUTO_INCREMENT, + `BotOwnerCharacterID` int(10) unsigned NOT NULL, + `BotSpellsID` int(10) unsigned NOT NULL DEFAULT '0', + `Name` varchar(64) NOT NULL, + `LastName` varchar(32) DEFAULT NULL, + `BotLevel` tinyint(2) unsigned NOT NULL DEFAULT '0', + `Race` smallint(5) NOT NULL DEFAULT '0', + `Class` tinyint(2) NOT NULL DEFAULT '0', + `Gender` tinyint(2) NOT NULL DEFAULT '0', + `Size` float NOT NULL DEFAULT '0', + `Face` int(10) NOT NULL DEFAULT '1', + `LuclinHairStyle` int(10) NOT NULL DEFAULT '1', + `LuclinHairColor` int(10) NOT NULL DEFAULT '1', + `LuclinEyeColor` int(10) NOT NULL DEFAULT '1', + `LuclinEyeColor2` int(10) NOT NULL DEFAULT '1', + `LuclinBeardColor` int(10) NOT NULL DEFAULT '1', + `LuclinBeard` int(10) NOT NULL DEFAULT '0', + `DrakkinHeritage` int(10) NOT NULL DEFAULT '0', + `DrakkinTattoo` int(10) NOT NULL DEFAULT '0', + `DrakkinDetails` int(10) NOT NULL DEFAULT '0', + `HP` INTEGER NOT NULL DEFAULT '0', + `Mana` INTEGER NOT NULL DEFAULT '0', + `MR` smallint(5) NOT NULL DEFAULT '0', + `CR` smallint(5) NOT NULL DEFAULT '0', + `DR` smallint(5) NOT NULL DEFAULT '0', + `FR` smallint(5) NOT NULL DEFAULT '0', + `PR` smallint(5) NOT NULL DEFAULT '0', + `Corrup` SMALLINT(5) NOT NULL DEFAULT '0', + `AC` smallint(5) NOT NULL DEFAULT '0', + `STR` mediumint(8) NOT NULL DEFAULT '75', + `STA` mediumint(8) NOT NULL DEFAULT '75', + `DEX` mediumint(8) NOT NULL DEFAULT '75', + `AGI` mediumint(8) NOT NULL DEFAULT '75', + `_INT` mediumint(8) NOT NULL DEFAULT '80', + `WIS` mediumint(8) NOT NULL DEFAULT '75', + `CHA` mediumint(8) NOT NULL DEFAULT '75', + `ATK` mediumint(9) NOT NULL DEFAULT '0', + `BotCreateDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `LastSpawnDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `TotalPlayTime` int(10) unsigned NOT NULL DEFAULT '0', + `LastZoneId` smallint(6) NOT NULL DEFAULT '0', + `BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT '', + PRIMARY KEY (`BotID`) +) ENGINE=InnoDB; + +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`); +ALTER TABLE `guild_members` DROP PRIMARY KEY; + +DROP VIEW IF EXISTS `vwGuildMembers`; +CREATE VIEW `vwGuildMembers` AS + select 'C' as mobtype, +cm.char_id, +cm.guild_id, +cm.rank, +cm.tribute_enable, +cm.total_tribute, +cm.last_tribute, +cm.banker, +cm.public_note, +cm.alt +from guild_members as cm +union all +select 'B' as mobtype, +bm.char_id, +bm.guild_id, +bm.rank, +bm.tribute_enable, +bm.total_tribute, +bm.last_tribute, +bm.banker, +bm.public_note, +bm.alt +from botguildmembers as bm; + +DROP VIEW IF EXISTS `vwBotCharacterMobs`; +CREATE VIEW `vwBotCharacterMobs` AS + select 'C' as mobtype, +c.id, +c.name, +c.class, +c.level, +c.timelaston, +c.zoneid +from character_ as c +union all +select 'B' as mobtype, +b.BotID as id, +b.Name as name, +b.Class as class, +b.BotLevel as level, +0 as timelaston, +0 as zoneid +from bots as b; + +-- FILE: +-- source player_tables/botpetstatepersists.sql; +DROP TABLE IF EXISTS `botpetinventory`; +DROP TABLE IF EXISTS `botpetbuffs`; +DROP TABLE IF EXISTS `botpets`; + +CREATE TABLE IF NOT EXISTS `botpets` ( + `BotPetsId` integer unsigned NOT NULL AUTO_INCREMENT, + `PetId` integer unsigned NOT NULL DEFAULT '0', + `BotId` integer unsigned NOT NULL DEFAULT '0', + `Name` varchar(64) NULL, + `Mana` integer NOT NULL DEFAULT '0', + `HitPoints` integer NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetsId`), + KEY `FK_botpets_1` (`BotId`), + CONSTRAINT `FK_botpets_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`), + CONSTRAINT `U_botpets_1` UNIQUE (`BotId`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `botpetbuffs` ( + `BotPetBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `BotPetsId` int(10) unsigned NOT NULL DEFAULT '0', + `SpellId` int(10) unsigned NOT NULL DEFAULT '0', + `CasterLevel` int(10) unsigned NOT NULL DEFAULT '0', + `Duration` int(11) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetBuffId`), + KEY `FK_botpetbuffs_1` (`BotPetsId`), + CONSTRAINT `FK_botpetbuffs_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `botpetinventory` ( + `BotPetInventoryId` integer unsigned NOT NULL AUTO_INCREMENT, + `BotPetsId` integer unsigned NOT NULL DEFAULT '0', + `ItemId` integer unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetInventoryId`), + KEY `FK_botpetinventory_1` (`BotPetsId`), + CONSTRAINT `FK_botpetinventory_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +-- FILE: +-- source player_tables/botinventory.sql; +CREATE TABLE IF NOT EXISTS botinventory ( + BotInventoryID integer unsigned NOT NULL auto_increment, + BotID integer unsigned NOT NULL DEFAULT '0', + SlotID integer signed NOT NULL DEFAULT '0', + ItemID integer unsigned NOT NULL DEFAULT '0', + charges tinyint(3) unsigned DEFAULT 0, + color integer unsigned NOT NULL DEFAULT 0, + augslot1 mediumint(7) unsigned NOT NULL DEFAULT 0, + augslot2 mediumint(7) unsigned NOT NULL DEFAULT 0, + augslot3 mediumint(7) unsigned NOT NULL DEFAULT 0, + augslot4 mediumint(7) unsigned NOT NULL DEFAULT 0, + augslot5 mediumint(7) unsigned DEFAULT 0, + instnodrop tinyint(1) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (BotInventoryID), + KEY FK_botinventory_1 (BotID), + CONSTRAINT FK_botinventory_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) +) ENGINE=InnoDB; + +-- FILE: +-- source player_tables/botbuffs.sql; +DROP TABLE IF EXISTS `botbuffs`; +CREATE TABLE `botbuffs` ( + `BotBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `BotId` int(10) unsigned NOT NULL DEFAULT '0', + `SpellId` int(10) unsigned NOT NULL DEFAULT '0', + `CasterLevel` int(10) unsigned NOT NULL DEFAULT '0', + `DurationFormula` int(10) unsigned NOT NULL DEFAULT '0', + `TicsRemaining` int(11) unsigned NOT NULL DEFAULT '0', + `PoisonCounters` int(11) unsigned NOT NULL DEFAULT '0', + `DiseaseCounters` int(11) unsigned NOT NULL DEFAULT '0', + `CurseCounters` int(11) unsigned NOT NULL DEFAULT '0', + `CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `HitCount` int(10) unsigned NOT NULL DEFAULT '0', + `MeleeRune` int(10) unsigned NOT NULL DEFAULT '0', + `MagicRune` int(10) unsigned NOT NULL DEFAULT '0', + `DeathSaveSuccessChance` int(10) unsigned NOT NULL DEFAULT '0', + `CasterAARank` int(10) unsigned NOT NULL DEFAULT '0', + `Persistent` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`BotBuffId`), + KEY `FK_botbuff_1` (`BotId`), + CONSTRAINT `FK_botbuff_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +-- FILE: +-- source player_tables/botadventuring.sql; +DELIMITER $$ + +DROP FUNCTION IF EXISTS `GetMobType` $$ +CREATE FUNCTION `GetMobType` (mobname VARCHAR(64)) RETURNS CHAR(1) +BEGIN + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (select count(*) from character_ where name = mobname) > 0 THEN + SET Result = 'C'; + ELSEIF (select count(*) from bots where Name = mobname) > 0 THEN + SET Result = 'B'; + END IF; + + RETURN Result; +END $$ + +DELIMITER ; + +DROP VIEW IF EXISTS `vwGroups`; +CREATE VIEW `vwGroups` AS + select g.groupid as groupid, +GetMobType(g.name) as mobtype, +g.name as name, +g.charid as mobid, +ifnull(c.level, b.BotLevel) as level +from group_id as g +left join character_ as c on g.name = c.name +left join bots as b on g.name = b.Name; + +-- FILE: +-- source player_tables/botgroups.sql; +DROP TABLE IF EXISTS `botgroupmembers`; +DROP TABLE IF EXISTS `botgroup`; + +CREATE TABLE IF NOT EXISTS `botgroup` ( + `BotGroupId` integer unsigned NOT NULL AUTO_INCREMENT, + `BotGroupLeaderBotId` integer unsigned NOT NULL DEFAULT '0', + `BotGroupName` varchar(64) NOT NULL, + PRIMARY KEY (`BotGroupId`), + KEY FK_botgroup_1 (BotGroupLeaderBotId), + CONSTRAINT FK_botgroup_1 FOREIGN KEY (BotGroupLeaderBotId) REFERENCES bots (BotID) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `botgroupmembers` ( + `BotGroupMemberId` integer unsigned NOT NULL AUTO_INCREMENT, + `BotGroupId` integer unsigned NOT NULL DEFAULT '0', + `BotId` integer unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`BotGroupMemberId`), + KEY FK_botgroupmembers_1 (BotGroupId), + CONSTRAINT FK_botgroupmembers_1 FOREIGN KEY (BotGroupId) REFERENCES botgroup (BotGroupId), + KEY FK_botgroupmembers_2 (BotId), + CONSTRAINT FK_botgroupmembers_2 FOREIGN KEY (BotId) REFERENCES bots (BotID) +) ENGINE=InnoDB; + +DROP VIEW IF EXISTS `vwBotGroups`; +CREATE VIEW `vwBotGroups` AS +select g.BotGroupId, +g.BotGroupName, +g.BotGroupLeaderBotId, +b.Name as BotGroupLeaderName, +b.BotOwnerCharacterId, +c.name as BotOwnerCharacterName +from botgroup as g +join bots as b on g.BotGroupLeaderBotId = b.BotID +join character_ as c on b.BotOwnerCharacterID = c.id +order by b.BotOwnerCharacterId, g.BotGroupName; + +-- FILE: +-- source player_tables/botstances.sql; +CREATE TABLE botstances ( + BotID int(10) unsigned NOT NULL default '0', + StanceID tinyint unsigned NOT NULL default '0', + PRIMARY KEY (BotID), + CONSTRAINT FK_botstances_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) +); + +-- FILE: +-- source player_tables/bottimers.sql; +CREATE TABLE bottimers ( +BotID int(10) unsigned NOT NULL default '0', +TimerID int(10) unsigned NOT NULL default '0', +Value int(10) unsigned NOT NULL default '0', +PRIMARY KEY (BotID), +CONSTRAINT FK_bottimers_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) +) diff --git a/utils/sql/git/bots/required/2014_03_22_BotGuildMember_ScriptFailureFix.sql b/utils/sql/git/bots/required/2014_03_22_BotGuildMember_ScriptFailureFix.sql new file mode 100644 index 000000000..47b735045 --- /dev/null +++ b/utils/sql/git/bots/required/2014_03_22_BotGuildMember_ScriptFailureFix.sql @@ -0,0 +1,27 @@ +ALTER TABLE `botguildmembers` ADD `alt` TINYINT UNSIGNED NOT NULL DEFAULT '0' AFTER `public_note`; + +DROP VIEW IF EXISTS `vwGuildMembers`; +CREATE VIEW `vwGuildMembers` AS + select 'C' as mobtype, +cm.char_id, +cm.guild_id, +cm.rank, +cm.tribute_enable, +cm.total_tribute, +cm.last_tribute, +cm.banker, +cm.public_note, +cm.alt +from guild_members as cm +union all +select 'B' as mobtype, +bm.char_id, +bm.guild_id, +bm.rank, +bm.tribute_enable, +bm.total_tribute, +bm.last_tribute, +bm.banker, +bm.public_note, +bm.alt +from botguildmembers as bm; \ No newline at end of file From 6906125725440f7b3ba7d6e2cc6020cc9a128aa9 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 27 Mar 2014 02:41:39 -0400 Subject: [PATCH 34/82] Fix for wizard innate crits when they have no chance otherwise --- zone/effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index c5e9dc20a..72bc518f5 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -84,7 +84,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation; - if (chance > 0){ + if (chance > 0 || (GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) { int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. From 41903e8f0924af4e0502a90f0e51d358365a53b5 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 27 Mar 2014 05:14:54 -0400 Subject: [PATCH 35/82] Spell Effect Updates. Implemented fail chances for SE, Gate, Succor, FeignDeath. Minor fixes to haste bonuses to allow for negatives. Rule added for Succor failure rate. --- changelog.txt | 7 +++ common/ruletypes.h | 1 + common/spdat.h | 10 ++-- .../optional/2014_03_27_SuccorFailRule.sql | 1 + zone/bonuses.cpp | 27 ++++++++-- zone/spell_effects.cpp | 52 +++++++++++++------ 6 files changed, 73 insertions(+), 25 deletions(-) create mode 100644 utils/sql/git/optional/2014_03_27_SuccorFailRule.sql diff --git a/changelog.txt b/changelog.txt index 0403a5c7f..767db2ea0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/27/2014 == +Kayen: SE_Gate will now use have a fail chance as defined by its base value in the spell data. +Kayen: SE_Succor will now have a baseline fail chance of (2%). Rule added to adjust this as needed. +Kayen: SE_FeignDeath will now have a fail chance as defined by its base value in the spell data. + +Optional SQL: utils/sql/git/optional/2014_03_27_SuccorFailRule.sql + == 03/22/2014 == Uleat: Moved the existing 'load_bots' and 'drop_bots' sqls into the emu git repository for the time being. Look to the /utils/sql/git/bots/ folder to find them. The 'load_bots' sql has been updated to include the below fix, as well as diff --git a/common/ruletypes.h b/common/ruletypes.h index fd50d1514..e800a14e8 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -302,6 +302,7 @@ RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest 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. RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick. +RULE_INT ( Spells, SuccorFailChance, 2) //Determines chance for a succor spell not to teleport an invidual player RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/common/spdat.h b/common/spdat.h index 347da070b..49f990537 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -159,8 +159,8 @@ typedef enum { #define SE_WIS 9 // implemented #define SE_CHA 10 // implemented - used as a spacer #define SE_AttackSpeed 11 // implemented -#define SE_Invisibility 12 // implemented -#define SE_SeeInvis 13 // implemented +#define SE_Invisibility 12 // implemented - TO DO: Implemented Invisiblity Levels +#define SE_SeeInvis 13 // implemented - TO DO: Implemented See Invisiblity Levels #define SE_WaterBreathing 14 // implemented #define SE_CurrentMana 15 // implemented //#define SE_NPCFrenzy 16 // not used @@ -172,7 +172,7 @@ typedef enum { #define SE_Charm 22 // implemented #define SE_Fear 23 // implemented #define SE_Stamina 24 // implemented - Invigor and such -#define SE_BindAffinity 25 // implemented +#define SE_BindAffinity 25 // implemented - TO DO: Implement 2nd and 3rd Recall (value 2,3 ect). Sets additional bind points. #define SE_Gate 26 // implemented - Gate to bind point #define SE_CancelMagic 27 // implemented #define SE_InvisVsUndead 28 // implemented @@ -211,7 +211,7 @@ typedef enum { #define SE_Identify 61 // implemented //#define SE_ItemID 62 // not used #define SE_WipeHateList 63 // implemented -#define SE_SpinTarget 64 // implemented +#define SE_SpinTarget 64 // implemented - TO DO: Not sure stun portion is working correctly #define SE_InfraVision 65 // implemented #define SE_UltraVision 66 // implemented #define SE_EyeOfZomm 67 // implemented @@ -456,7 +456,7 @@ typedef enum { //#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds //#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor. #define SE_SuspendMinion 308 // not implemented as bonus -#define SE_YetAnotherGate 309 // implemented +#define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point #define SE_ReduceReuseTimer 310 // implemented #define SE_LimitCombatSkills 311 // implemented - Excludes focus from procs (except if proc is a memorizable spell) //#define SE_Sanctuary 312 // *not implemented diff --git a/utils/sql/git/optional/2014_03_27_SuccorFailRule.sql b/utils/sql/git/optional/2014_03_27_SuccorFailRule.sql new file mode 100644 index 000000000..9d60f8bcb --- /dev/null +++ b/utils/sql/git/optional/2014_03_27_SuccorFailRule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:SuccorFailChance', '2', 'Determines chance for a succor spell not to teleport an invidual player.'); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 99941b893..c89424a3c 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1358,16 +1358,27 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_AttackSpeed2: { if ((effect_value - 100) > 0) { // Haste V2 - Stacks with V1 but does not Overcap + if (newbon->hastetype2 < 0) break; //Slowed - Don't apply haste2 if ((effect_value - 100) > newbon->hastetype2) { newbon->hastetype2 = effect_value - 100; } } + else if ((effect_value - 100) < 0) { // Slow + int real_slow_value = (100 - effect_value) * -1; + if (real_slow_value < newbon->hastetype2) + newbon->hastetype2 = real_slow_value; + } break; } case SE_AttackSpeed3: { - if (effect_value > 0) { // Haste V3 - Stacks and Overcaps + if (effect_value < 0){ //Slow + if (effect_value < newbon->hastetype3) + newbon->hastetype3 = effect_value; + } + + else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps if (effect_value > newbon->hastetype3) { newbon->hastetype3 = effect_value; } @@ -1377,18 +1388,24 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_AttackSpeed4: { - if (effect_value > 0) { + if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow) + effect_value = effect_value * -1; + + if (effect_value > 0 && effect_value > newbon->inhibitmelee) { + if (slow_mitigation){ int new_effect_value = SlowMitigation(false,caster,effect_value); if (new_effect_value > newbon->inhibitmelee) { - newbon->inhibitmelee = new_effect_value; - SlowMitigation(true,caster); + newbon->inhibitmelee = new_effect_value; + SlowMitigation(true,caster); } } + else if (effect_value > newbon->inhibitmelee) { - newbon->inhibitmelee = effect_value; + newbon->inhibitmelee = effect_value; } } + break; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 7fd2f4332..6ea2d30a9 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -396,10 +396,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) } case SE_Succor: - { + { + float x, y, z, heading; const char *target_zone; - + x = spell.base[1]; y = spell.base[0]; z = spell.base[2]; @@ -426,6 +427,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(IsClient()) { + if(MakeRandomInt(0, 99) < RuleI(Spells, SuccorFailChance)) { //2% Fail chance by default + + if(IsClient()) { + CastToClient()->Message(MT_SpellFailure,"Your portal collapses before you can make your escape!"); + } + break; + } + // Below are the spellid's for known evac/succor spells that send player // to the current zone's safe points. @@ -441,10 +450,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM LogFile->write(EQEMuLog::Debug, "Succor/Evacuation Spell In Same Zone."); #endif - if(IsClient()) - CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading, 0, EvacToSafeCoords); - else - GMMove(x, y, z, heading); + if(IsClient()) + CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading, 0, EvacToSafeCoords); + else + GMMove(x, y, z, heading); } else { #ifdef SPELL_EFFECT_SPAM @@ -457,7 +466,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - case SE_YetAnotherGate: //Shin: Used on Teleport Bind. + case SE_GateCastersBindpoint: //Shin: Used on Teleport Bind. case SE_Teleport: // gates, rings, circles, etc case SE_Teleport2: { @@ -489,7 +498,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) } } - if (effect == SE_YetAnotherGate && caster->IsClient()) + if (effect == SE_GateCastersBindpoint && caster->IsClient()) { //Shin: Teleport Bind uses caster's bind point x = caster->CastToClient()->GetBindX(); y = caster->CastToClient()->GetBindY(); @@ -857,7 +866,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - case SE_BindAffinity: + case SE_BindAffinity: //TO DO: Add support for secondary and tertiary gate abilities { #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Bind Affinity"); @@ -989,13 +998,18 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - case SE_Gate: + case SE_Gate: //TO DO: Add support for secondary and tertiary gate abilities (base2) { #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Gate"); #endif - if(!spellbonuses.AntiGate) - Gate(); + if(!spellbonuses.AntiGate){ + + if(MakeRandomInt(0, 99) < effect_value) + Gate(); + else + caster->Message(MT_SpellFailure,"Your portal has collapsed."); + } break; } @@ -1378,7 +1392,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) ( spell.base[i], Mob::GetDefaultGender(spell.base[i], GetGender()), - spell.base2[i] + spell.base2[i], + spell.max[i] ); if(spell.base[i] == OGRE){ SendAppearancePacket(AT_Size, 9); @@ -1554,8 +1569,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(spell_id == 2488) //Dook- Lifeburn fix break; - if(IsClient()) - CastToClient()->SetFeigned(true); + if(IsClient()) { + + if (MakeRandomInt(0, 99) > spells[spell_id].base[i]) { + CastToClient()->SetFeigned(false); + entity_list.MessageClose_StringID(this, false, 200, 10, STRING_FEIGNFAILED, GetName()); + } + else + CastToClient()->SetFeigned(true); + } break; } From 079d1ca870fad7551616e434daf80ce762adb78d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 27 Mar 2014 19:17:05 -0400 Subject: [PATCH 36/82] String updates Level requirment for Summon Corpse spells --- zone/StringIDs.h | 3 +++ zone/spell_effects.cpp | 32 +++++++++++++++++++------------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/zone/StringIDs.h b/zone/StringIDs.h index afa598e1c..d34bb4148 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -80,6 +80,7 @@ #define CANNOT_AFFECT_NPC 251 //That spell can not affect this target NPC. #define SUSPEND_MINION_HAS_AGGRO 256 //Your pet is the focus of something's attention. #define NO_PET 255 //You do not have a pet. +#define GATE_FAIL 260 //Your gate is too unstable, and collapses. #define CORPSE_CANT_SENSE 262 //You cannot sense any corpses for this PC in this zone. #define SPELL_NO_HOLD 263 //Your spell did not take hold. #define CANNOT_CHARM 267 //This NPC cannot be charmed. @@ -208,6 +209,7 @@ #define AA_POINTS 1215 //points #define SPELL_FIZZLE_OTHER 1218 //%1's spell fizzles! #define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close! +#define SPELL_LEVEL_REQ 1226 //This spell only works on people who are level %1 and under. #define CORPSE_DECAY_NOW 1227 //This corpse is waiting to expire. #define SURNAME_REJECTED 1374 //Your new surname was rejected. Please try a different name. #define DUEL_DECLINE 1383 //%1 has declined your challenge to duel to the death. @@ -253,6 +255,7 @@ #define GAIN_RAIDEXP 5085 //You gained raid experience! #define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there. #define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. +#define SUCCOR_FAIL 5169 //The portal collapes before you can escape! #define PET_ATTACKING 5501 //%1 tells you, 'Attacking %2 Master.' #define FATAL_BOW_SHOT 5745 //%1 performs a FATAL BOW SHOT!! #define MELEE_SILENCE 5806 //You *CANNOT* use this melee ability, you are suffering from amnesia! diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 6ea2d30a9..1292fab89 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -430,7 +430,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(MakeRandomInt(0, 99) < RuleI(Spells, SuccorFailChance)) { //2% Fail chance by default if(IsClient()) { - CastToClient()->Message(MT_SpellFailure,"Your portal collapses before you can make your escape!"); + CastToClient()->Message_StringID(MT_SpellFailure,SUCCOR_FAIL); } break; } @@ -1008,7 +1008,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(MakeRandomInt(0, 99) < effect_value) Gate(); else - caster->Message(MT_SpellFailure,"Your portal has collapsed."); + caster->Message_StringID(MT_SpellFailure,GATE_FAIL); } break; } @@ -1713,19 +1713,25 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) // Now we should either be casting this on self or its being cast on a valid group member if(TargetClient) { - Corpse *corpse = entity_list.GetCorpseByOwner(TargetClient); - if(corpse) { - if(TargetClient == this->CastToClient()) - Message_StringID(4, SUMMONING_CORPSE, TargetClient->CastToMob()->GetCleanName()); - else - Message_StringID(4, SUMMONING_CORPSE_OTHER, TargetClient->CastToMob()->GetCleanName()); - corpse->Summon(CastToClient(), true, true); - } - else { - // No corpse found in the zone - Message_StringID(4, CORPSE_CANT_SENSE); + if (TargetClient->GetLevel() <= effect_value){ + + Corpse *corpse = entity_list.GetCorpseByOwner(TargetClient); + if(corpse) { + if(TargetClient == this->CastToClient()) + Message_StringID(4, SUMMONING_CORPSE, TargetClient->CastToMob()->GetCleanName()); + else + Message_StringID(4, SUMMONING_CORPSE_OTHER, TargetClient->CastToMob()->GetCleanName()); + + corpse->Summon(CastToClient(), true, true); + } + else { + // No corpse found in the zone + Message_StringID(4, CORPSE_CANT_SENSE); + } } + else + caster->Message_StringID(MT_SpellFailure, SPELL_LEVEL_REQ); } else { Message_StringID(4, TARGET_NOT_FOUND); From cbe0e94ca7770ae79ff90617c3836971746dff7b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 27 Mar 2014 23:23:15 -0400 Subject: [PATCH 37/82] Implemented SE_NegateIfCombat --- common/spdat.h | 2 +- zone/attack.cpp | 6 ++++++ zone/bonuses.cpp | 3 +++ zone/bot.cpp | 6 ++++++ zone/common.h | 1 + zone/special_attacks.cpp | 9 +++++++++ zone/spells.cpp | 3 +++ 7 files changed, 29 insertions(+), 1 deletion(-) diff --git a/common/spdat.h b/common/spdat.h index 49f990537..99de59c83 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -241,7 +241,7 @@ typedef enum { #define SE_SummonCorpse 91 // implemented #define SE_InstantHate 92 // implemented - add hate #define SE_StopRain 93 // implemented - Wake of Karana -#define SE_NegateIfCombat 94 // *not implemented? - Works client side but there is comment todo in spell effects...Component of Spirit of Scale +#define SE_NegateIfCombat 94 // implemented #define SE_Sacrifice 95 // implemented #define SE_Silence 96 // implemented #define SE_ManaPool 97 // implemented diff --git a/zone/attack.cpp b/zone/attack.cpp index be099adc6..91ce521d5 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1371,6 +1371,9 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b invisible_animals = false; } + if (spellbonuses.NegateIfCombat) + BuffFadeByEffect(SE_NegateIfCombat); + if(hidden || improved_hidden){ hidden = false; improved_hidden = false; @@ -1983,6 +1986,9 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool invisible_animals = false; } + if (spellbonuses.NegateIfCombat) + BuffFadeByEffect(SE_NegateIfCombat); + if(hidden || improved_hidden) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index c89424a3c..b6f341f56 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2585,6 +2585,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->AbsorbMagicAtt[1] = buffslot; } break; + + case SE_NegateIfCombat: + newbon->NegateIfCombat = true; } } } diff --git a/zone/bot.cpp b/zone/bot.cpp index 425402b65..347dd0bc6 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3237,6 +3237,9 @@ void Bot::BotRangedAttack(Mob* other) { invisible_animals = false; } + if (spellbonuses.NegateIfCombat) + BuffFadeByEffect(SE_NegateIfCombat); + if(hidden || improved_hidden){ hidden = false; improved_hidden = false; @@ -6640,6 +6643,9 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b safe_delete(outapp); } + if (spellbonuses.NegateIfCombat) + BuffFadeByEffect(SE_NegateIfCombat); + if(GetTarget()) TriggerDefensiveProcs(weapon, other, Hand, damage); diff --git a/zone/common.h b/zone/common.h index 581d11430..11b9f8919 100644 --- a/zone/common.h +++ b/zone/common.h @@ -344,6 +344,7 @@ struct StatBonuses { int16 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana. uint16 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot uint16 MeleeRune[2]; // 0 = rune value 1 = buff slot + bool NegateIfCombat; // Bool Drop buff if cast or melee // AAs int8 Packrat; //weight reduction for items, 1 point = 10% diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index d551962ea..8d95524fe 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -839,6 +839,9 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { invisible_animals = false; } + if (spellbonuses.NegateIfCombat) + BuffFadeByEffect(SE_NegateIfCombat); + if(hidden || improved_hidden){ hidden = false; improved_hidden = false; @@ -1085,6 +1088,9 @@ void NPC::RangedAttack(Mob* other) invisible_animals = false; } + if (spellbonuses.NegateIfCombat) + BuffFadeByEffect(SE_NegateIfCombat); + if(hidden || improved_hidden){ hidden = false; improved_hidden = false; @@ -1227,6 +1233,9 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 invisible_animals = false; } + if (spellbonuses.NegateIfCombat) + BuffFadeByEffect(SE_NegateIfCombat); + if(hidden || improved_hidden){ hidden = false; improved_hidden = false; diff --git a/zone/spells.cpp b/zone/spells.cpp index 86d21ea7a..2963172c5 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -211,6 +211,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, return(false); } + if (spellbonuses.NegateIfCombat) + BuffFadeByEffect(SE_NegateIfCombat); + if(IsClient() && GetTarget() && IsHarmonySpell(spell_id)) { for(int i = 0; i < EFFECT_COUNT; i++) { From d939820918c775964628a8618c1d14574261d213 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 31 Mar 2014 03:21:22 -0400 Subject: [PATCH 38/82] Fix for unconscious ability skillups. Fix for zone crash related to item==nullptr in Client::SummonItem(). --- changelog.txt | 6 ++++++ common/ruletypes.h | 1 + zone/client.cpp | 6 ++++++ zone/client.h | 1 + zone/inventory.cpp | 27 ++++++++++++++++++--------- zone/spells.cpp | 18 ++++++++++++++++++ 6 files changed, 50 insertions(+), 9 deletions(-) diff --git a/changelog.txt b/changelog.txt index 767db2ea0..0d8f82041 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== xx/xx/2014 == +Uleat: Fix for unconscious skillups. +Uleat: Fix for crash issue with nullptr reference in recent Client::SummonItem() work. +Uleat: Added rule for GM Status check code in Client::SummonItem(). +Note: Rule default is set to 250..but, implementation is on hold until load item code handles the database 'minstatus' field. + == 03/27/2014 == Kayen: SE_Gate will now use have a fail chance as defined by its base value in the spell data. Kayen: SE_Succor will now have a baseline fail chance of (2%). Rule added to adjust this as needed. diff --git a/common/ruletypes.h b/common/ruletypes.h index e800a14e8..d275595fe 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -138,6 +138,7 @@ RULE_BOOL( Pets, UnTargetableSwarmPet, false ) RULE_CATEGORY_END() RULE_CATEGORY( GM ) +RULE_INT ( GM, MinStatusToSummonItem, 250) RULE_INT ( GM, MinStatusToZoneAnywhere, 250 ) RULE_CATEGORY_END() diff --git a/zone/client.cpp b/zone/client.cpp index ebf5d806c..827a3f3f8 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2306,6 +2306,8 @@ uint64 Client::GetAllMoney() { } bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int chancemodi) { + if (IsDead() || IsUnconscious()) + return false; if (IsAIControlled()) // no skillups while chamred =p return false; if (skillid > HIGHEST_SKILL) @@ -2349,6 +2351,10 @@ bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int cha } void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) { + if (IsDead() || IsUnconscious()) + return; + if (IsAIControlled()) + return; if (langid >= MAX_PP_LANGUAGE) return; // do nothing if langid is an invalid language diff --git a/zone/client.h b/zone/client.h index 97ee1b32f..76b2c6774 100644 --- a/zone/client.h +++ b/zone/client.h @@ -305,6 +305,7 @@ public: void SetHideMe(bool hm); inline uint16 GetPort() const { return port; } bool IsDead() const { return(dead); } + bool IsUnconscious() const { return ((cur_hp <= 0) ? true : false); } inline bool IsLFP() { return LFP; } void UpdateLFP(); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index b18514056..5292dd1c0 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -209,7 +209,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if(item == nullptr) { Message(13, "Item %u does not exist.", item_id); mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5); + GetName(), account_name, item_id, aug1, aug2, aug3, aug4, aug5); return false; } @@ -228,14 +228,20 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, return false; } + + // This code is ready to implement once the item load code is changed to process the 'minstatus' field. + // Checking #iteminfo in-game verfies that item->MinStatus is set to '0' regardless of field value. + // An optional sql script will also need to be added, once this goes live, to allow changing of the min status. + // check to make sure we are a GM if the item is GM-only /* - else if(item->gm && (this->Admin() < 100)) - Message(13, "You are not a GM and can not summon this item."); - mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only item with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - GetName(), account_name, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5); + else if(item->MinStatus && ((this->Admin() < item->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) { + Message(13, "You are not a GM or do not have the status to summon this item."); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only item with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, MinStatus: %u)\n", + GetName(), account_name, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, item->MinStatus); return false; + } */ uint32 augments[MAX_AUGMENT_SLOTS] = { aug1, aug2, aug3, aug4, aug5 }; @@ -276,12 +282,15 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, return false; } + + // Same as GM check above + // check to make sure we are a GM if the augment is GM-only /* - else if(augtest->gm && (this->Admin() < 100)) { - Message(13, "You are not a GM and can not summon this augment."); - mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only augment (Aug%i) with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n", - GetName(), account_name, (iter + 1), this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5); + else if(augtest->MinStatus && ((this->Admin() < augtest->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) { + Message(13, "You are not a GM or do not have the status to summon this augment."); + mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only augment (Aug%i) with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, MinStatus: %u)\n", + GetName(), account_name, (iter + 1), this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, item->MinStatus); return false; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 2963172c5..c8903c537 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -557,6 +557,15 @@ uint16 Mob::GetSpecializeSkillValue(uint16 spell_id) const { } void Client::CheckSpecializeIncrease(uint16 spell_id) { + // These are not active because CheckIncreaseSkill() already does so. + // It's such a rare occurance that adding them here is wasted..(ref only) + /* + if (IsDead() || IsUnconscious()) + return; + if (IsAIControlled()) + return; + */ + switch(spells[spell_id].skill) { case SkillAbjuration: CheckIncreaseSkill(SkillSpecializeAbjure, nullptr); @@ -580,6 +589,15 @@ void Client::CheckSpecializeIncrease(uint16 spell_id) { } void Client::CheckSongSkillIncrease(uint16 spell_id){ + // These are not active because CheckIncreaseSkill() already does so. + // It's such a rare occurance that adding them here is wasted..(ref only) + /* + if (IsDead() || IsUnconscious()) + return; + if (IsAIControlled()) + return; + */ + switch(spells[spell_id].skill) { case SkillSinging: From 955f164efb706f35c279fb3a92b0f77885e0d21d Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 31 Mar 2014 09:31:49 -0400 Subject: [PATCH 39/82] Added optional rule for allowing bots to auto-update with their owner. (Run the optional sql script to activate this feature) --- changelog.txt | 7 +++- common/ruletypes.h | 1 + .../2014_03_31_BotLevelsWithOwnerRule.sql | 4 +++ zone/bot.cpp | 22 +++++++++++++ zone/bot.h | 1 + zone/command.cpp | 32 ++++++++++++++----- zone/exp.cpp | 11 +++++++ 7 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 utils/sql/git/bots/optional/2014_03_31_BotLevelsWithOwnerRule.sql diff --git a/changelog.txt b/changelog.txt index 0d8f82041..ad9699fb4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,11 +1,16 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- -== xx/xx/2014 == +== 03/31/2014 == Uleat: Fix for unconscious skillups. Uleat: Fix for crash issue with nullptr reference in recent Client::SummonItem() work. Uleat: Added rule for GM Status check code in Client::SummonItem(). Note: Rule default is set to 250..but, implementation is on hold until load item code handles the database 'minstatus' field. +Uleat: Added RuleB(Bots, BotLevelsWithOwner). Bots will auto-update as their owner levels/de-levels. Appearance packets are sent to show the 'leveling effect' as well as updating client entities. + +Optional Bot SQL: utils/sql/git/bot/optional/2014_03_31_BotLevelsWithOwnerRule.sql +Note: This sql is required to activate the optional behavior. + == 03/27/2014 == Kayen: SE_Gate will now use have a fail chance as defined by its base value in the spell data. Kayen: SE_Succor will now have a baseline fail chance of (2%). Rule added to adjust this as needed. diff --git a/common/ruletypes.h b/common/ruletypes.h index d275595fe..32775f316 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -448,6 +448,7 @@ RULE_BOOL ( Bots, BotSpellQuest, false ) // Anita Thrall's (Anita_Thrall.pl) Bot RULE_INT ( Bots, BotAAExpansion, 8 ) // Bots get AAs through this expansion RULE_BOOL ( Bots, BotGroupXP, false ) // Determines whether client gets xp for bots outside their group. RULE_BOOL ( Bots, BotBardUseOutOfCombatSongs, true) // Determines whether bard bots use additional out of combat songs. +RULE_BOOL ( Bots, BotLevelsWithOwner, false) // Auto-updates spawned bots as owner levels/de-levels (false is original behavior) RULE_CATEGORY_END() #endif diff --git a/utils/sql/git/bots/optional/2014_03_31_BotLevelsWithOwnerRule.sql b/utils/sql/git/bots/optional/2014_03_31_BotLevelsWithOwnerRule.sql new file mode 100644 index 000000000..4700d9abf --- /dev/null +++ b/utils/sql/git/bots/optional/2014_03_31_BotLevelsWithOwnerRule.sql @@ -0,0 +1,4 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (2, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (4, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (10, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.'); diff --git a/zone/bot.cpp b/zone/bot.cpp index 347dd0bc6..1526beddc 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5284,6 +5284,28 @@ uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) { return Result; } +void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) { + // This essentially performs a '#bot update,' with appearance packets, based on the current methods. + // This should not be called outside of Client::SetEXP() due to it's lack of rule checks. + if(client) { + std::list blist = entity_list.GetBotsByBotOwnerCharacterID(client->CharacterID()); + + for(std::list::iterator biter = blist.begin(); biter != blist.end(); ++biter) { + Bot* bot = *biter; + if(bot && (bot->GetLevel() != client->GetLevel())) { + bot->SetPetChooser(false); // not sure what this does, but was in bot 'update' code + bot->CalcBotStats(false); + if(sendlvlapp) + bot->SendLevelAppearance(); + // modified from Client::SetLevel() + bot->SendAppearancePacket(AT_WhoLevel, level, true, true); // who level change + } + } + + blist.clear(); + } +} + std::string Bot::ClassIdToString(uint16 classId) { std::string Result; diff --git a/zone/bot.h b/zone/bot.h index b0ff52845..485d37377 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -352,6 +352,7 @@ public: static uint32 CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage); static uint32 AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage); static uint32 GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage); + static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp); //static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage); static std::string ClassIdToString(uint16 classId); static std::string RaceIdToString(uint16 raceId); diff --git a/zone/command.cpp b/zone/command.cpp index 40e37045e..c55fa4542 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2638,19 +2638,35 @@ void command_makepet(Client *c, const Seperator *sep) void command_level(Client *c, const Seperator *sep) { uint16 level = atoi(sep->arg[1]); - if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap)) ) + + if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap))) { c->Message(0, "Error: #Level: Invalid Level"); - else if (c->Admin() < 100) + } + else if (c->Admin() < 100) { c->SetLevel(level, true); - else if (!c->GetTarget()) +#ifdef BOTS + if(RuleB(Bots, BotLevelsWithOwner)) + Bot::LevelBotWithClient(c, level, true); +#endif + } + else if (!c->GetTarget()) { c->Message(0, "Error: #Level: No target"); - else - if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel)))) + } + else { + if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel)))) { c->Message(0, "Error: #Level: Invalid Level"); - else + } + else { c->GetTarget()->SetLevel(level, true); - if(c->GetTarget() && c->GetTarget()->IsClient()) - c->GetTarget()->CastToClient()->SendLevelAppearance(); + if(c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->SendLevelAppearance(); +#ifdef BOTS + if(RuleB(Bots, BotLevelsWithOwner)) + Bot::LevelBotWithClient(c->GetTarget()->CastToClient(), level, true); +#endif + } + } + } } void command_spawn(Client *c, const Seperator *sep) diff --git a/zone/exp.cpp b/zone/exp.cpp index cbf7f5f6d..5e8eb49f8 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -326,7 +326,18 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { } else Message(15, "Welcome to level %i!", check_level); + +#ifdef BOTS + uint8 myoldlevel = GetLevel(); +#endif + SetLevel(check_level); + +#ifdef BOTS + if(RuleB(Bots, BotLevelsWithOwner)) + // hack way of doing this..but, least invasive... (same criteria as gain level for sendlvlapp) + Bot::LevelBotWithClient(this, GetLevel(), (myoldlevel==check_level-1)); +#endif } //If were at max level then stop gaining experience if we make it to the cap From 174cb1876ab9c14e063d4a26debaa72507774e31 Mon Sep 17 00:00:00 2001 From: Trevius Date: Mon, 31 Mar 2014 22:03:11 -0500 Subject: [PATCH 40/82] Prevented an endless loop crash related to EVENT_TASK_STAGE_COMPLETE. --- changelog.txt | 2 +- zone/tasks.cpp | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/changelog.txt b/changelog.txt index ad9699fb4..94ed3789f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,8 +5,8 @@ Uleat: Fix for unconscious skillups. Uleat: Fix for crash issue with nullptr reference in recent Client::SummonItem() work. Uleat: Added rule for GM Status check code in Client::SummonItem(). Note: Rule default is set to 250..but, implementation is on hold until load item code handles the database 'minstatus' field. - Uleat: Added RuleB(Bots, BotLevelsWithOwner). Bots will auto-update as their owner levels/de-levels. Appearance packets are sent to show the 'leveling effect' as well as updating client entities. +Trevius: Prevented an endless loop crash related to EVENT_TASK_STAGE_COMPLETE. Optional Bot SQL: utils/sql/git/bot/optional/2014_03_31_BotLevelsWithOwnerRule.sql Note: This sql is required to activate the optional behavior. diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 44d4e1e69..221085d4e 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -1972,14 +1972,6 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T Task->Activity[ActivityID].GoalCount, ActivityID); - if(Task->Activity[ActivityID].GoalMethod != METHODQUEST) - { - char buf[24]; - snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID); - buf[23] = '\0'; - parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, c, buf, 0); - } - // Flag the activity as complete ActiveTasks[TaskIndex].Activity[ActivityID].State = ActivityCompleted; // Unlock subsequent activities for this task @@ -1991,6 +1983,15 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T taskmanager->SendSingleActiveTaskToClient(c, TaskIndex, TaskComplete, false); // Inform the client the task has been updated, both by a chat message c->Message(0, "Your task '%s' has been updated.", Task->Title); + + if(Task->Activity[ActivityID].GoalMethod != METHODQUEST) + { + char buf[24]; + snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID); + buf[23] = '\0'; + parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, c, buf, 0); + } + // If this task is now complete, the Completed tasks will have been // updated in UnlockActivities. Send the completed task list to the // client. This is the same sequence the packets are sent on live. From 8866b3170e23e4573a0d8e39798fa45fbec00ec3 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 1 Apr 2014 21:03:49 -0400 Subject: [PATCH 41/82] Implement ability for NPC Merchants to open and close shop --- changelog.txt | 6 ++++++ zone/client_packet.cpp | 6 ++++++ zone/command.cpp | 29 ++++++++++++++++++++++++++++- zone/command.h | 2 ++ zone/lua_npc.cpp | 14 +++++++++++++- zone/lua_npc.h | 4 +++- zone/npc.cpp | 1 + zone/npc.h | 4 ++++ 8 files changed, 63 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index 94ed3789f..4afe9c941 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/01/2014 == +demonstar55: Implemented ability for a merchant to open and close shop. + Lua quest functions: e.self:MerchantOpenShop() and e.self:MerchantCloseShop() + GM Commands: #merchant_open_shop (short: #open_shop) and #merchant_close_shop (short: #close_shop) + default to status 100, just in case you need to force the merchants status + == 03/31/2014 == Uleat: Fix for unconscious skillups. Uleat: Fix for crash issue with nullptr reference in recent Client::SummonItem() work. diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 441681fd0..cde2b143f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5421,6 +5421,12 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) action = 0; } + // 1199 I don't have time for that now. etc + if (!tmp->CastToNPC()->IsMerchantOpen()) { + tmp->Say_StringID(MakeRandomInt(1199, 1202)); + action = 0; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; diff --git a/zone/command.cpp b/zone/command.cpp index c55fa4542..8c2192c87 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -449,7 +449,11 @@ int command_init(void) { command_add("questerrors", "Shows quest errors.", 100, command_questerrors) || command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) || command_add("disablerecipe", "[recipe_id] - Disables a recipe using the recipe id.", 80, command_disablerecipe) || - command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) + command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) || + command_add("merchant_open_shop", "Opens a merchants shop", 100, command_merchantopenshop) || + command_add("open_shop", nullptr, 100, command_merchantopenshop) || + command_add("merchant_close_shop", "Closes a merchant shop", 100, command_merchantcloseshop) || + command_add("close_shop", nullptr, 100, command_merchantcloseshop) ) { command_deinit(); @@ -11497,3 +11501,26 @@ void command_npctype_cache(Client *c, const Seperator *sep) c->Message(0, "#npctype_cache all"); } } + +void command_merchantopenshop(Client *c, const Seperator *sep) +{ + Mob *merchant = c->GetTarget(); + if (!merchant || merchant->GetClass() != MERCHANT) { + c->Message(0, "You must target a merchant to open their shop."); + return; + } + + merchant->CastToNPC()->MerchantOpenShop(); +} + +void command_merchantcloseshop(Client *c, const Seperator *sep) +{ + Mob *merchant = c->GetTarget(); + if (!merchant || merchant->GetClass() != MERCHANT) { + c->Message(0, "You must target a merchant to close their shop."); + return; + } + + merchant->CastToNPC()->MerchantCloseShop(); +} + diff --git a/zone/command.h b/zone/command.h index 48ca51b58..a36009309 100644 --- a/zone/command.h +++ b/zone/command.h @@ -324,6 +324,8 @@ void command_enablerecipe(Client *c, const Seperator *sep); void command_disablerecipe(Client *c, const Seperator *sep); void command_showspellslist(Client *c, const Seperator *sep); void command_npctype_cache(Client *c, const Seperator *sep); +void command_merchantopenshop(Client *c, const Seperator *sep); +void command_merchantcloseshop(Client *c, const Seperator *sep); #ifdef EQPROFILE void command_profiledump(Client *c, const Seperator *sep); diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index e9d1ae0ec..1dd61cc91 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -432,6 +432,16 @@ int Lua_NPC::GetScore() { return self->GetScore(); } +void Lua_NPC::MerchantOpenShop() { + Lua_Safe_Call_Void(); + self->MerchantOpenShop(); +} + +void Lua_NPC::MerchantCloseShop() { + Lua_Safe_Call_Void(); + self->MerchantCloseShop(); +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") @@ -520,7 +530,9 @@ luabind::scope lua_register_npc() { .def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed) .def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating) .def("GetSpawnKillCount", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnKillCount) - .def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore); + .def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore) + .def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop) + .def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 1dbd33253..8c34ed17d 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -112,7 +112,9 @@ public: int GetAccuracyRating(); int GetSpawnKillCount(); int GetScore(); + void MerchantOpenShop(); + void MerchantCloseShop(); }; #endif -#endif \ No newline at end of file +#endif diff --git a/zone/npc.cpp b/zone/npc.cpp index 2c21c94a7..861a4359e 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -199,6 +199,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float SetMana(GetMaxMana()); MerchantType = d->merchanttype; + merchant_open = GetClass() == MERCHANT; adventure_template_id = d->adventure_template; org_x = x; org_y = y; diff --git a/zone/npc.h b/zone/npc.h index f374c8fe6..e8877172b 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -209,6 +209,10 @@ public: void SetSecSkill(uint8 skill_type) { sec_melee_type = skill_type; } uint32 MerchantType; + bool merchant_open; + inline void MerchantOpenShop() { merchant_open = true; } + inline void MerchantCloseShop() { merchant_open = false; } + inline bool IsMerchantOpen() { return merchant_open; } void Depop(bool StartSpawnTimer = false); void Stun(int duration); void UnStun(); From bb541eeb60a65f1af919f6b1093d9655f0b06dab Mon Sep 17 00:00:00 2001 From: Trevius Date: Tue, 1 Apr 2014 23:00:15 -0500 Subject: [PATCH 42/82] Fixed potential endless quest loop with EVENT_COMBAT and WipeHateList(). --- changelog.txt | 1 + zone/mob.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 4afe9c941..6931fac1d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,6 +5,7 @@ demonstar55: Implemented ability for a merchant to open and close shop. Lua quest functions: e.self:MerchantOpenShop() and e.self:MerchantCloseShop() GM Commands: #merchant_open_shop (short: #open_shop) and #merchant_close_shop (short: #close_shop) default to status 100, just in case you need to force the merchants status +Trevius: Fixed potential endless quest loop with EVENT_COMBAT and WipeHateList(). == 03/31/2014 == Uleat: Fix for unconscious skillups. diff --git a/zone/mob.cpp b/zone/mob.cpp index 3c1500d19..6267f5bcc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2450,13 +2450,18 @@ bool Mob::RemoveFromHateList(Mob* mob) return bFound; } + void Mob::WipeHateList() { if(IsEngaged()) { + hate_list.Wipe(); AI_Event_NoLongerEngaged(); } - hate_list.Wipe(); + else + { + hate_list.Wipe(); + } } uint32 Mob::RandomTimer(int min,int max) { From 2cdd50b9e9493282eedbe5b60e68cfe80556d62e Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 3 Apr 2014 04:25:45 -0400 Subject: [PATCH 43/82] -Implemented live like spell projectiles (ie mage bolts). -See function in spells.cpp for more info on bolt behavior. -This works reasonably well, but still room for improvements. -Rules are for setting what item id is used for the projectile since live uses an item id from SOF+ I added alternate item graphic for titanium clients. -Note: Max number of projectiles (set at 10) is a made up value in most situations it would be nearly impossible to have more than 3 bolts in the air at the same time. This values gives enough wiggle room that no server should have an issue though. -Small fix to SE_CompleteHeal --- changelog.txt | 6 ++ common/ruletypes.h | 3 + .../2014_04_03_SpellProjectileRules.sql | 3 + zone/client_process.cpp | 3 + zone/common.h | 1 + zone/mob.cpp | 51 ++++++++++++++ zone/mob.h | 7 ++ zone/npc.cpp | 3 + zone/spell_effects.cpp | 8 ++- zone/spells.cpp | 66 ++++++++++++++++++- 10 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql diff --git a/changelog.txt b/changelog.txt index 6931fac1d..62e33d3e7 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/03/2014 == +Kayen: Implemented live like spell projectiles (ie. Mage Bolts). + +Optional SQL: utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql +Note: The rules in this SQL are for setting the item id for the graphic used by the projectile on different clients. + == 04/01/2014 == demonstar55: Implemented ability for a merchant to open and close shop. Lua quest functions: e.self:MerchantOpenShop() and e.self:MerchantCloseShop() diff --git a/common/ruletypes.h b/common/ruletypes.h index 32775f316..39886c9f5 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -304,6 +304,9 @@ RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low le RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick. RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick. RULE_INT ( Spells, SuccorFailChance, 2) //Determines chance for a succor spell not to teleport an invidual player +RULE_INT ( Spells, FRProjectileItem_Titanium, 1113) // Item id for Titanium clients for Fire 'spell projectile'. +RULE_INT ( Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for Fire 'spell projectile'. +RULE_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql b/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql new file mode 100644 index 000000000..70d8594b6 --- /dev/null +++ b/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql @@ -0,0 +1,3 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_Titanium', '1113', 'Item id for Titanium clients for Fire spell projectile.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'FRProjectileItem_SOF', '80684', 'Item id for Titanium clients for Fire spell projectile.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'FRProjectileItem_NPC', '80684', 'Item id for Titanium clients for Fire spell projectile.'); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index f1e25226a..c28affd28 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -570,6 +570,9 @@ bool Client::Process() { viral_timer_counter = 0; } + if(projectile_timer.Check()) + SpellProjectileEffect(); + if(spellbonuses.GravityEffect == 1) { if(gravity_timer.Check()) DoGravityEffect(); diff --git a/zone/common.h b/zone/common.h index 11b9f8919..ee87bd164 100644 --- a/zone/common.h +++ b/zone/common.h @@ -5,6 +5,7 @@ #include "../common/spdat.h" #define HIGHEST_RESIST 9 //Max resist type value +#define MAX_SPELL_PROJECTILE 10 //Max amount of spell projectiles that can be active by a single mob. /* solar: macros for IsAttackAllowed, IsBeneficialAllowed */ #define _CLIENT(x) (x && x->IsClient() && !x->CastToClient()->IsBecomeNPC()) diff --git a/zone/mob.cpp b/zone/mob.cpp index 6267f5bcc..d3199d0e6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -276,6 +276,14 @@ Mob::Mob(const char* in_name, casting_spell_inventory_slot = 0; target = 0; + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_spell_id[i] = 0; } + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_target_id[i] = 0; } + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_increment[i] = 0; } + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_x[i] = 0; } + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_y[i] = 0; } + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_z[i] = 0; } + projectile_timer.Disable(); + memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&spellbonuses, 0, sizeof(StatBonuses)); memset(&aabonuses, 0, sizeof(StatBonuses)); @@ -4358,6 +4366,49 @@ bool Mob::TryReflectSpell(uint32 spell_id) return false; } +void Mob::SpellProjectileEffect() +{ + bool time_disable = false; + + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { + + if (projectile_increment[i] == 0){ + continue; + } + + Mob* target = entity_list.GetMobID(projectile_target_id[i]); + + float dist = 0; + + if (target) + dist = target->CalculateDistance(projectile_x[i], projectile_y[i], projectile_z[i]); + + int increment_end = 0; + increment_end = (dist / 10) - 1; //This pretty accurately determines end time for speed for 1.5 and timer of 250 ms + + if (increment_end <= projectile_increment[i]){ + + if (target && IsValidSpell(projectile_spell_id[i])) + SpellOnTarget(projectile_spell_id[i], target, false, true, spells[projectile_spell_id[i]].ResistDiff, true); + + projectile_spell_id[i] = 0; + projectile_target_id[i] = 0; + projectile_x[i] = 0, projectile_y[i] = 0, projectile_z[i] = 0; + projectile_increment[i] = 0; + time_disable = true; + } + + else { + projectile_increment[i]++; + time_disable = false; + } + } + + if (time_disable) + projectile_timer.Disable(); +} + + void Mob::DoGravityEffect() { Mob *caster = nullptr; diff --git a/zone/mob.h b/zone/mob.h index c8453d301..03e160f12 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -252,6 +252,7 @@ public: void SendBuffsToClient(Client *c); inline Buffs_Struct* GetBuffs() { return buffs; } void DoGravityEffect(); + void SpellProjectileEffect(); void DamageShield(Mob* other, bool spell_ds = false); int32 RuneAbsorb(int32 damage, uint16 type); bool FindBuff(uint16 spellid); @@ -1040,6 +1041,12 @@ protected: uint8 bardsong_slot; uint32 bardsong_target_id; + Timer projectile_timer; + uint32 projectile_spell_id[MAX_SPELL_PROJECTILE]; + uint16 projectile_target_id[MAX_SPELL_PROJECTILE]; + uint8 projectile_increment[MAX_SPELL_PROJECTILE]; + float projectile_x[MAX_SPELL_PROJECTILE], projectile_y[MAX_SPELL_PROJECTILE], projectile_z[MAX_SPELL_PROJECTILE]; + float rewind_x; float rewind_y; float rewind_z; diff --git a/zone/npc.cpp b/zone/npc.cpp index 861a4359e..7a64b3cac 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -659,6 +659,9 @@ bool NPC::Process() viral_timer_counter = 0; } + if(projectile_timer.Check()) + SpellProjectileEffect(); + if(spellbonuses.GravityEffect == 1) { if(gravity_timer.Check()) DoGravityEffect(); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 1292fab89..1901fcc09 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -326,7 +326,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(inuse) break; - Heal(); + int32 val = 0; + val = 7500*effect_value; + val = caster->GetActSpellHealing(spell_id, val, this); + + if (val > 0) + HealDamage(val, caster); + break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index c8903c537..730971a97 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1830,7 +1830,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } // check line of sight to target if it's a detrimental spell - if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id)) + if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional) { mlog(SPELLS__CASTING, "Spell %d: cannot see target %s", spell_target->GetName()); Message_StringID(13,CANT_SEE_TARGET); @@ -1868,6 +1868,70 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } } + /*For mage 'Bolt' line and other various spells. + -This is mostly accurate for how the modern clients handle this effect. + -It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic) + -The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+ + -There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier + and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target. + -If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles). + -The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly + and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement). + When bolt hits its predicted point the damage is then done to target. + Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant. + Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units. + Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check + because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play) + */ + if (spell_target && spells[spell_id].targettype == ST_TargetOptional){ + + uint8 anim = spells[spell_id].CastingAnim; + int bolt_id = -1; + + //Make sure there is an avialable bolt to be cast. + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { + if (projectile_spell_id[i] == 0){ + bolt_id = i; + break; + } + } + + if (bolt_id < 0) + return false; + + if (CheckLosFN(spell_target)) { + + projectile_spell_id[bolt_id] = spell_id; + projectile_target_id[bolt_id] = spell_target->GetID(); + projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ(); + projectile_increment[bolt_id] = 1; + projectile_timer.Start(250); + } + + //Only use fire graphic for fire spells. + if (spells[spell_id].resisttype == RESIST_FIRE) { + + if (IsClient()){ + if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic. + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5); + else + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5); + } + else + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5); + + if (spells[spell_id].CastingAnim == 64) + anim = 44; //Corrects for animation error. + } + + //Pending other types of projectile graphics. (They will function but with a default arrow graphic for now) + else + ProjectileAnimation(spell_target,0, 1, 1.5); + + DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); + return true; + } + // // Switch #2 - execute the spell // From f6046477b407c7bc777a8bd9af5609769d1751c3 Mon Sep 17 00:00:00 2001 From: Corysia Taware Date: Thu, 3 Apr 2014 14:10:03 -0700 Subject: [PATCH 44/82] Changes for intel Mac OSX build --- CMakeLists.txt | 4 ++++ client_files/export/CMakeLists.txt | 4 +++- client_files/import/CMakeLists.txt | 4 +++- common/TCPConnection.cpp | 4 ++++ eqlaunch/CMakeLists.txt | 4 +++- loginserver/CMakeLists.txt | 4 +++- queryserv/CMakeLists.txt | 4 +++- shared_memory/CMakeLists.txt | 4 +++- tests/CMakeLists.txt | 4 +++- ucs/CMakeLists.txt | 4 +++- world/CMakeLists.txt | 4 +++- world/net.cpp | 2 +- zone/CMakeLists.txt | 4 +++- 13 files changed, 39 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e39033656..e567b1de5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,10 @@ IF(UNIX) ADD_DEFINITIONS(-DFREEBSD) SET(FREEBSD TRUE) ENDIF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + IF(CMAKE_SYSTEM_NAME MATCHES "Darwin") + ADD_DEFINITIONS(-DDARWIN) + SET(DARWIN TRUE) + ENDIF(CMAKE_SYSTEM_NAME MATCHES "Darwin") ENDIF(UNIX) #use stdint.h types if they exist for this platform (we have to guess otherwise) diff --git a/client_files/export/CMakeLists.txt b/client_files/export/CMakeLists.txt index 191184013..851aa05fb 100644 --- a/client_files/export/CMakeLists.txt +++ b/client_files/export/CMakeLists.txt @@ -26,7 +26,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(export_client_files "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(export_client_files "z") TARGET_LINK_LIBRARIES(export_client_files "m") - TARGET_LINK_LIBRARIES(export_client_files "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(export_client_files "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(export_client_files "pthread") ADD_DEFINITIONS(-fPIC) ENDIF(UNIX) diff --git a/client_files/import/CMakeLists.txt b/client_files/import/CMakeLists.txt index 94875302d..0b6c45b57 100644 --- a/client_files/import/CMakeLists.txt +++ b/client_files/import/CMakeLists.txt @@ -26,7 +26,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(import_client_files "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(import_client_files "z") TARGET_LINK_LIBRARIES(import_client_files "m") - TARGET_LINK_LIBRARIES(import_client_files "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(import_client_files "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(import_client_files "pthread") ADD_DEFINITIONS(-fPIC) ENDIF(UNIX) diff --git a/common/TCPConnection.cpp b/common/TCPConnection.cpp index e576f953a..1e5f49600 100644 --- a/common/TCPConnection.cpp +++ b/common/TCPConnection.cpp @@ -30,6 +30,10 @@ #ifdef FREEBSD //Timothy Whitman - January 7, 2003 #define MSG_NOSIGNAL 0 #endif +#ifdef DARWIN + #define MSG_NOSIGNAL SO_NOSIGPIPE // Corysia Taware - Sept. 27, 2013 + // See http://lists.apple.com/archives/macnetworkprog/2002/Dec/msg00091.html +#endif // DARWIN #ifdef _WINDOWS InitWinsock winsock; diff --git a/eqlaunch/CMakeLists.txt b/eqlaunch/CMakeLists.txt index 0f0114c88..b636a18cf 100644 --- a/eqlaunch/CMakeLists.txt +++ b/eqlaunch/CMakeLists.txt @@ -30,7 +30,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(eqlaunch "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(eqlaunch "z") TARGET_LINK_LIBRARIES(eqlaunch "m") - TARGET_LINK_LIBRARIES(eqlaunch "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(eqlaunch "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(eqlaunch "pthread") ADD_DEFINITIONS(-fPIC) ENDIF(UNIX) diff --git a/loginserver/CMakeLists.txt b/loginserver/CMakeLists.txt index ba96b1f61..9ded859e5 100644 --- a/loginserver/CMakeLists.txt +++ b/loginserver/CMakeLists.txt @@ -58,7 +58,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(loginserver "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(loginserver "z") TARGET_LINK_LIBRARIES(loginserver "m") - TARGET_LINK_LIBRARIES(loginserver "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(loginserver "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(loginserver "pthread") TARGET_LINK_LIBRARIES(loginserver "EQEmuAuthCrypto") TARGET_LINK_LIBRARIES(loginserver "cryptopp") diff --git a/queryserv/CMakeLists.txt b/queryserv/CMakeLists.txt index a8480ca94..33be865e0 100644 --- a/queryserv/CMakeLists.txt +++ b/queryserv/CMakeLists.txt @@ -36,7 +36,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(queryserv "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(queryserv "z") TARGET_LINK_LIBRARIES(queryserv "m") - TARGET_LINK_LIBRARIES(queryserv "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(queryserv "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(queryserv "pthread") ADD_DEFINITIONS(-fPIC) ENDIF(UNIX) diff --git a/shared_memory/CMakeLists.txt b/shared_memory/CMakeLists.txt index 863b5633f..3d23a1b01 100644 --- a/shared_memory/CMakeLists.txt +++ b/shared_memory/CMakeLists.txt @@ -38,7 +38,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(shared_memory "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(shared_memory "z") TARGET_LINK_LIBRARIES(shared_memory "m") - TARGET_LINK_LIBRARIES(shared_memory "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(shared_memory "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(shared_memory "pthread") ADD_DEFINITIONS(-fPIC) ENDIF(UNIX) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 80f6a2f1d..aef5124c9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,7 +32,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(tests "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(tests "z") TARGET_LINK_LIBRARIES(tests "m") - TARGET_LINK_LIBRARIES(tests "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(loginserver "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(tests "pthread") ADD_DEFINITIONS(-fPIC) ENDIF(UNIX) diff --git a/ucs/CMakeLists.txt b/ucs/CMakeLists.txt index 27a8a072d..44681831c 100644 --- a/ucs/CMakeLists.txt +++ b/ucs/CMakeLists.txt @@ -38,7 +38,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(ucs "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(ucs "z") TARGET_LINK_LIBRARIES(ucs "m") - TARGET_LINK_LIBRARIES(ucs "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(ucs "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(ucs "pthread") ADD_DEFINITIONS(-fPIC) ENDIF(UNIX) diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index 2decbf4c9..3ac008267 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -84,7 +84,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(world "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(world "z") TARGET_LINK_LIBRARIES(world "m") - TARGET_LINK_LIBRARIES(world "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(world "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(world "pthread") ADD_DEFINITIONS(-fPIC) ENDIF(UNIX) diff --git a/world/net.cpp b/world/net.cpp index 057367b00..8be3283f0 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -56,7 +56,7 @@ #include #include #include - #ifndef FREEBSD + #if not defined (FREEBSD) && not defined (DARWIN) union semun { int val; struct semid_ds *buf; diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 49410f0f2..e582e0c45 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -230,7 +230,9 @@ IF(UNIX) TARGET_LINK_LIBRARIES(zone "${CMAKE_DL_LIBS}") TARGET_LINK_LIBRARIES(zone "z") TARGET_LINK_LIBRARIES(zone "m") - TARGET_LINK_LIBRARIES(zone "rt") + IF(NOT DARWIN) + TARGET_LINK_LIBRARIES(zone "rt") + ENDIF(NOT DARWIN) TARGET_LINK_LIBRARIES(zone "pthread") ADD_DEFINITIONS(-fPIC) ENDIF(UNIX) From 4b14ec53f1f2f242e28907713103b04cbf3300f9 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 4 Apr 2014 01:59:55 -0400 Subject: [PATCH 45/82] Implemented Physical Resists consistent with live. SQL to add new column 'PhR' to npc_types Values to populate table based on extensive parsing. Fixes for spell projectile code. --- changelog.txt | 6 + common/spdat.h | 2 +- .../required/2014_04_04_PhysicalResist.txt | 7 + zone/StringIDs.h | 1 + zone/mob.h | 6 +- zone/npc.cpp | 3 + zone/spell_effects.cpp | 70 ++++++ zone/spells.cpp | 230 +++++++++--------- zone/zonedb.cpp | 2 + zone/zonedump.h | 1 + 10 files changed, 212 insertions(+), 116 deletions(-) create mode 100644 utils/sql/git/required/2014_04_04_PhysicalResist.txt diff --git a/changelog.txt b/changelog.txt index 62e33d3e7..d9a3c2106 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/04/2014 == +Kayen: Implemented 'Physical Resists' (Resist Type 9) to be consistent with live based on extensive parsing. + SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations. + +Optional SQL: utils/sql/git/optional/2014_04_04_PhysicalResists.sql + == 04/03/2014 == Kayen: Implemented live like spell projectiles (ie. Mage Bolts). diff --git a/common/spdat.h b/common/spdat.h index 99de59c83..fcbb02aaf 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -258,7 +258,7 @@ typedef enum { #define SE_Familiar 108 // implemented #define SE_SummonItemIntoBag 109 // implemented - summons stuff into container //#define SE_IncreaseArchery 110 // not used -#define SE_ResistAll 111 // implemented +#define SE_ResistAll 111 // implemented - Note: Physical Resists are not modified by this effect. #define SE_CastingLevel 112 // implemented #define SE_SummonHorse 113 // implemented #define SE_ChangeAggro 114 // implemented - Hate modifing buffs(ie horrifying visage) diff --git a/utils/sql/git/required/2014_04_04_PhysicalResist.txt b/utils/sql/git/required/2014_04_04_PhysicalResist.txt new file mode 100644 index 000000000..f748f0b9f --- /dev/null +++ b/utils/sql/git/required/2014_04_04_PhysicalResist.txt @@ -0,0 +1,7 @@ +ALTER TABLE `npc_types` ADD `PhR` smallint( 5 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `Corrup`; + +-- Approximate baseline live npc values based on extensive parsing. +UPDATE npc_types SET PhR = 10 WHERE PhR = 0 AND level <= 50; +UPDATE npc_types SET PhR = (10 + (level - 50)) WHERE PhR = 0 AND (level > 50 AND level <= 60); +UPDATE npc_types SET PhR = (20 + ((level - 60)*4)) WHERE PhR = 0 AND level > 60; + diff --git a/zone/StringIDs.h b/zone/StringIDs.h index d34bb4148..95227ae07 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -262,6 +262,7 @@ #define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds. #define DISCIPLINE_REUSE_MSG2 5808 //You can use the ability %1 again in %2 minute(s) %3 seconds. #define FAILED_TAUNT 5811 //You have failed to taunt your target. +#define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability. #define AA_NO_TARGET 5825 //You must first select a target for this ability! #define FORAGE_MASTERY 6012 //Your forage mastery has enabled you to find something else! #define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited. diff --git a/zone/mob.h b/zone/mob.h index 03e160f12..4ee5da5a8 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -192,6 +192,7 @@ public: virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false, int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false); + int ResistPhysical(int level_diff, uint8 caster_level); uint16 GetSpecializeSkillValue(uint16 spell_id) const; void SendSpellBarDisable(); void SendSpellBarEnable(uint16 spellid); @@ -222,6 +223,8 @@ public: uint16 CastingSpellID() const { return casting_spell_id; } bool DoCastingChecks(); bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier); + void SpellProjectileEffect(); + bool TrySpellProjectile(Mob* spell_target, uint16 spell_id); //Buff void BuffProcess(); @@ -252,7 +255,6 @@ public: void SendBuffsToClient(Client *c); inline Buffs_Struct* GetBuffs() { return buffs; } void DoGravityEffect(); - void SpellProjectileEffect(); void DamageShield(Mob* other, bool spell_ds = false); int32 RuneAbsorb(int32 damage, uint16 type); bool FindBuff(uint16 spellid); @@ -338,6 +340,7 @@ public: inline virtual int16 GetPR() const { return PR + itembonuses.PR + spellbonuses.PR; } inline virtual int16 GetCR() const { return CR + itembonuses.CR + spellbonuses.CR; } inline virtual int16 GetCorrup() const { return Corrup + itembonuses.Corrup + spellbonuses.Corrup; } + inline virtual int16 GetPhR() const { return PhR; } inline StatBonuses GetItemBonuses() const { return itembonuses; } inline StatBonuses GetSpellBonuses() const { return spellbonuses; } inline StatBonuses GetAABonuses() const { return aabonuses; } @@ -916,6 +919,7 @@ protected: int16 DR; int16 PR; int16 Corrup; + int16 PhR; bool moving; int targeted; bool findable; diff --git a/zone/npc.cpp b/zone/npc.cpp index 7a64b3cac..f83d1d31b 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -158,6 +158,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float FR = d->FR; PR = d->PR; Corrup = d->Corrup; + PhR = d->PhR; STR = d->STR; STA = d->STA; @@ -2062,6 +2063,8 @@ void NPC::CalcNPCResists() { PR = (GetLevel() * 11)/10; if (!Corrup) Corrup = 15; + if (!PhR) + PhR = 10; return; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 1901fcc09..ce299ed36 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6020,3 +6020,73 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama return false; } +bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){ + + /*For mage 'Bolt' line and other various spells. + -This is mostly accurate for how the modern clients handle this effect. + -It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic) + -The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+ + -There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier + and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target. + -If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles). + -The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly + and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement). + When bolt hits its predicted point the damage is then done to target. + Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant. + Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units. + Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check + because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play) + */ + + if (!spell_target) + return false; + + uint8 anim = spells[spell_id].CastingAnim; + int bolt_id = -1; + + //Make sure there is an avialable bolt to be cast. + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { + if (projectile_spell_id[i] == 0){ + bolt_id = i; + break; + } + } + + if (bolt_id < 0) + return false; + + if (CheckLosFN(spell_target)) { + + projectile_spell_id[bolt_id] = spell_id; + projectile_target_id[bolt_id] = spell_target->GetID(); + projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ(); + projectile_increment[bolt_id] = 1; + projectile_timer.Start(250); + } + + //Only use fire graphic for fire spells. + if (spells[spell_id].resisttype == RESIST_FIRE) { + + if (IsClient()){ + if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic. + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5); + else + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5); + } + + else + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5); + + if (spells[spell_id].CastingAnim == 64) + anim = 44; //Corrects for animation error. + } + + //Pending other types of projectile graphics. (They will function but with a default arrow graphic for now) + else + ProjectileAnimation(spell_target,0, 1, 1.5); + + DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation. + return true; +} + + diff --git a/zone/spells.cpp b/zone/spells.cpp index 730971a97..e00ece3c0 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1868,70 +1868,6 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } } - /*For mage 'Bolt' line and other various spells. - -This is mostly accurate for how the modern clients handle this effect. - -It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic) - -The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+ - -There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier - and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target. - -If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles). - -The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly - and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement). - When bolt hits its predicted point the damage is then done to target. - Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant. - Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units. - Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check - because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play) - */ - if (spell_target && spells[spell_id].targettype == ST_TargetOptional){ - - uint8 anim = spells[spell_id].CastingAnim; - int bolt_id = -1; - - //Make sure there is an avialable bolt to be cast. - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { - if (projectile_spell_id[i] == 0){ - bolt_id = i; - break; - } - } - - if (bolt_id < 0) - return false; - - if (CheckLosFN(spell_target)) { - - projectile_spell_id[bolt_id] = spell_id; - projectile_target_id[bolt_id] = spell_target->GetID(); - projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ(); - projectile_increment[bolt_id] = 1; - projectile_timer.Start(250); - } - - //Only use fire graphic for fire spells. - if (spells[spell_id].resisttype == RESIST_FIRE) { - - if (IsClient()){ - if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic. - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5); - else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5); - } - else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5); - - if (spells[spell_id].CastingAnim == 64) - anim = 44; //Corrects for animation error. - } - - //Pending other types of projectile graphics. (They will function but with a default arrow graphic for now) - else - ProjectileAnimation(spell_target,0, 1, 1.5); - - DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); - return true; - } - // // Switch #2 - execute the spell // @@ -1959,7 +1895,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 if (isproc) { SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true); } else { - if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) { + if (spells[spell_id].targettype == ST_TargetOptional){ + if (!TrySpellProjectile(spell_target, spell_id)) + return false; + } + + else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) { if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) { // Prevent mana usage/timers being set for beneficial buffs if(casting_spell_type == 1) @@ -1968,6 +1909,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } } } + if(IsPlayerIllusionSpell(spell_id) && IsClient() && CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){ @@ -3504,8 +3446,15 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) ) { mlog(SPELLS__RESISTS, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName()); - Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name); - spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name); + + if (spells[spell_id].resisttype == RESIST_PHYSICAL){ + Message_StringID(MT_SpellFailure, PHYSICAL_RESIST_FAIL,spells[spell_id].name); + spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name); + } + else { + Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name); + spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name); + } if(spelltar->IsAIControlled()){ int32 aggro = CheckAggroAmount(spell_id); @@ -4254,67 +4203,83 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } break; case RESIST_PHYSICAL: + { + if (IsNPC()) + target_resist = GetPhR(); + else + target_resist = 0; + } default: - //This is guessed but the others are right - target_resist = (GetSTA() / 4); + + target_resist = 0; } //Setup our base resist chance. int resist_chance = 0; + int level_mod = 0; //Adjust our resist chance based on level modifiers int temp_level_diff = GetLevel() - caster->GetLevel(); - if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff)) - { - int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel(); - if(a > 0) + + //Physical Resists are calclated using their own formula derived from extensive parsing. + if (resist_type == RESIST_PHYSICAL) { + level_mod = ResistPhysical(temp_level_diff, caster->GetLevel()); + } + + else { + + if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff)) { - temp_level_diff = a; - } - else - { - temp_level_diff = 0; - } - } - - if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15) - { - temp_level_diff = 15; - } - - if(IsNPC() && temp_level_diff < -9) - { - temp_level_diff = -9; - } - - int level_mod = temp_level_diff * temp_level_diff / 2; - if(temp_level_diff < 0) - { - level_mod = -level_mod; - } - - if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20) - { - level_mod = 1000; - } - - //Even more level stuff this time dealing with damage spells - if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17) - { - int level_diff; - if(GetLevel() >= RuleI(Casting,ResistFalloff)) - { - level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel(); - if(level_diff < 0) + int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel(); + if(a > 0) { - level_diff = 0; + temp_level_diff = a; + } + else + { + temp_level_diff = 0; } } - else + + if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15) { - level_diff = GetLevel() - caster->GetLevel(); + temp_level_diff = 15; + } + + if(IsNPC() && temp_level_diff < -9) + { + temp_level_diff = -9; + } + + level_mod = temp_level_diff * temp_level_diff / 2; + if(temp_level_diff < 0) + { + level_mod = -level_mod; + } + + if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20) + { + level_mod = 1000; + } + + //Even more level stuff this time dealing with damage spells + if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17) + { + int level_diff; + if(GetLevel() >= RuleI(Casting,ResistFalloff)) + { + level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel(); + if(level_diff < 0) + { + level_diff = 0; + } + } + else + { + level_diff = GetLevel() - caster->GetLevel(); + } + level_mod += (2 * level_diff); } - level_mod += (2 * level_diff); } if (CharismaCheck) @@ -4461,6 +4426,43 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } } +int Mob::ResistPhysical(int level_diff, uint8 caster_level) +{ + /* Physical resists use the standard level mod calculation in + conjunction with a resist fall off formula that greatly prevents you + from landing abilities on mobs that are higher level than you. + After level 12, every 4 levels gained the max level you can hit + your target without a sharp resist penalty is raised by 1. + Extensive parsing confirms this, along with baseline phyiscal resist rates used. + */ + + + if (level_diff == 0) + return level_diff; + + int level_mod = 0; + + if (level_diff > 0) { + + int ResistFallOff = 0; + + if (caster_level <= 12) + ResistFallOff = 3; + else + ResistFallOff = caster_level/4; + + if (level_diff > ResistFallOff || level_diff >= 15) + level_mod = ((level_diff * 10) + level_diff)*2; + else + level_mod = level_diff * level_diff / 2; + } + + else + level_mod = -(level_diff * level_diff / 2); + + return level_mod; +} + int16 Mob::CalcResistChanceBonus() { int resistchance = spellbonuses.ResistSpellChance + itembonuses.ResistSpellChance; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 6c3b8ce6e..568780e7d 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1047,6 +1047,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { "npc_types.FR," "npc_types.PR," "npc_types.Corrup," + "npc_types.PhR," "npc_types.mindmg," "npc_types.maxdmg," "npc_types.attack_count," @@ -1143,6 +1144,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { tmpNPCType->FR = atoi(row[r++]); tmpNPCType->PR = atoi(row[r++]); tmpNPCType->Corrup = atoi(row[r++]); + tmpNPCType->PhR = atoi(row[r++]); tmpNPCType->min_dmg = atoi(row[r++]); tmpNPCType->max_dmg = atoi(row[r++]); tmpNPCType->attack_count = atoi(row[r++]); diff --git a/zone/zonedump.h b/zone/zonedump.h index 726041d5b..2c036be61 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -75,6 +75,7 @@ struct NPCType int16 PR; int16 DR; int16 Corrup; + int16 PhR; uint8 haircolor; uint8 beardcolor; uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye? From d1ecb3265298bcede93719fcdb0117ffe9b31fcd Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 4 Apr 2014 12:27:18 -0700 Subject: [PATCH 46/82] Fix for crash in EntityList::MobInZone(Mob *who) when a dangling pointer is passed to the function. Which used to work without crashing but was changed at some point which can be triggered by quests in some situations. --- zone/entity.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index f5533cb84..ed5ee33f0 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3415,9 +3415,14 @@ void EntityList::ReloadAllClientsTaskState(int TaskID) bool EntityList::IsMobInZone(Mob *who) { - auto it = mob_list.find(who->GetID()); - if (it != mob_list.end()) - return who == it->second; + //We don't use mob_list.find(who) because this code needs to be able to handle dangling pointers for the quest code. + auto it = mob_list.begin(); + while(it != mob_list.end()) { + if(it->second == who) { + return true; + } + ++it; + } return false; } From 1d6e9473873663d1edf0d687ab20fcf46f60288d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 4 Apr 2014 21:32:56 -0400 Subject: [PATCH 47/82] Change log correction. --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index d9a3c2106..7b4a8c72a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,7 +4,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) Kayen: Implemented 'Physical Resists' (Resist Type 9) to be consistent with live based on extensive parsing. SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations. -Optional SQL: utils/sql/git/optional/2014_04_04_PhysicalResists.sql +Required SQL: utils/sql/git/optional/2014_04_04_PhysicalResists.sql == 04/03/2014 == Kayen: Implemented live like spell projectiles (ie. Mage Bolts). From 2c69dd7c9336fe026d48d24b12e9eaf18a18888a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 4 Apr 2014 22:03:32 -0400 Subject: [PATCH 48/82] Implemented proper functionality of SE_Screech If you have a buff with SE_Screech with value of 1 it will block any other buff with SE_Screen that has a value of -1, giving you an immunity message. Example: 1383 Screech and 2785 Screech Immunity --- common/spdat.h | 2 +- zone/StringIDs.h | 1 + zone/bonuses.cpp | 6 ++++++ zone/common.h | 1 + zone/spells.cpp | 10 +++++++++- 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index fcbb02aaf..c7452426d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -270,7 +270,7 @@ typedef enum { #define SE_HealRate 120 // implemented - reduces healing by a % #define SE_ReverseDS 121 // implemented //#define SE_ReduceSkill 122 // not used -#define SE_Screech 123 // implemented? Spell Blocker(can only have one buff with this effect at one time) +#define SE_Screech 123 // implemented Spell Blocker(If have buff with value +1 will block any effect with -1) #define SE_ImprovedDamage 124 // implemented #define SE_ImprovedHeal 125 // implemented #define SE_SpellResistReduction 126 // implemented diff --git a/zone/StringIDs.h b/zone/StringIDs.h index 95227ae07..05bbffdec 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -334,6 +334,7 @@ #define ALREADY_CASTING 12442 //You are already casting a spell! #define SENSE_CORPSE_NOT_NAME 12446 //You don't sense any corpses of that name. #define SENSE_CORPSE_NONE 12447 //You don't sense any corpses. +#define SCREECH_BUFF_BLOCK 12448 //Your immunity buff protected you from the spell %1! #define NOT_HOLDING_ITEM 12452 //You are not holding an item! #define SENSE_UNDEAD 12471 //You sense undead in this direction. #define SENSE_ANIMAL 12472 //You sense an animal in this direction. diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index b6f341f56..56f8b9f5f 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2588,6 +2588,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_NegateIfCombat: newbon->NegateIfCombat = true; + break; + + case SE_Screech: + newbon->Screech = effect_value; + break; + } } } diff --git a/zone/common.h b/zone/common.h index ee87bd164..d720f3d43 100644 --- a/zone/common.h +++ b/zone/common.h @@ -346,6 +346,7 @@ struct StatBonuses { uint16 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot uint16 MeleeRune[2]; // 0 = rune value 1 = buff slot bool NegateIfCombat; // Bool Drop buff if cast or melee + int8 Screech; // -1 = Will be blocked if another Screech is +(1) // AAs int8 Packrat; //weight reduction for items, 1 point = 10% diff --git a/zone/spells.cpp b/zone/spells.cpp index e00ece3c0..43a0d2b19 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2613,6 +2613,14 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, { effect1 = sp1.effectid[i]; effect2 = sp2.effectid[i]; + + if (spellbonuses.Screech == 1) { + if (effect2 == SE_Screech && sp2.base[i] == -1) { + Message_StringID(MT_SpellFailure, SCREECH_BUFF_BLOCK, sp2.name); + return -1; + } + } + if(effect2 == SE_StackingCommand_Overwrite) { overwrite_effect = sp2.base[i]; @@ -2657,7 +2665,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d, but we do not have that effect on that slot. Ignored.", sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value); } - } + } } } else { mlog(SPELLS__STACKING, "%s (%d) and %s (%d) appear to be in the same line, skipping Stacking Overwrite/Blocking checks", From fb3c6365e19ce541ae004a02041dcdfc410b652f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 5 Apr 2014 01:58:16 -0500 Subject: [PATCH 49/82] Test --- changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.txt b/changelog.txt index 6931fac1d..4bd4d8eaa 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/05/2014 == +Akkadius: BOOGABOOGABOOGA + == 04/01/2014 == demonstar55: Implemented ability for a merchant to open and close shop. Lua quest functions: e.self:MerchantOpenShop() and e.self:MerchantCloseShop() From 7cfc5b085e9efe72cd64eaf4e3c3a2a53a869f76 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 5 Apr 2014 03:38:58 -0500 Subject: [PATCH 50/82] (Performance Adjustment) Removed AsyncLoadVariables from InterserverTimer.Check() in both zone and world. By watching the MySQL general.log file on mass zone idle activity, you can see that the query 'SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= timestamp' is called every 10 seconds. This function is loading variables that are initially loaded on World and Zone bootup. When running a large amount of zone servers, the amount of MySQL chatter that is produced is enormous and unnecessary. For example, if I ran 400 zone servers, I would see 3,456,000 unnecessary queries from all idle or active zone processes in a 24 hour interval. --- changelog.txt | 5 ++++- world/net.cpp | 2 +- zone/net.cpp | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index 4bd4d8eaa..87229d5dc 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 04/05/2014 == -Akkadius: BOOGABOOGABOOGA +Akkadius: (Performance Adjustment) Removed AsyncLoadVariables from InterserverTimer.Check() in both zone and world. By watching the MySQL general.log file on mass zone idle activity, you can + see that the query 'SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= timestamp' is called every 10 seconds. This function is loading + variables that are initially loaded on World and Zone bootup. When running a large amount of zone servers, the amount of MySQL chatter that is produced is enormous and + unnecessary. For example, if I ran 400 zone servers, I would see 3,456,000 unnecessary queries from all idle or active zone processes in a 24 hour interval. == 04/01/2014 == demonstar55: Implemented ability for a merchant to open and close shop. diff --git a/world/net.cpp b/world/net.cpp index 057367b00..3c5df74a2 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -461,7 +461,7 @@ int main(int argc, char** argv) { if (InterserverTimer.Check()) { InterserverTimer.Start(); database.ping(); - AsyncLoadVariables(dbasync, &database); + // AsyncLoadVariables(dbasync, &database); ReconnectCounter++; if (ReconnectCounter >= 12) { // only create thread to reconnect every 10 minutes. previously we were creating a new thread every 10 seconds ReconnectCounter = 0; diff --git a/zone/net.cpp b/zone/net.cpp index d870ee550..15e108e42 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -447,7 +447,7 @@ int main(int argc, char** argv) { if (InterserverTimer.Check()) { InterserverTimer.Start(); database.ping(); - AsyncLoadVariables(dbasync, &database); + // AsyncLoadVariables(dbasync, &database); entity_list.UpdateWho(); if (worldserver.TryReconnect() && (!worldserver.Connected())) worldserver.AsyncConnect(); From 0f67e93a0fd26b2ee50a920dbb5559db41ea0294 Mon Sep 17 00:00:00 2001 From: Corysia Taware Date: Sat, 5 Apr 2014 10:24:28 -0700 Subject: [PATCH 51/82] Backward port to OSX 10.6 (Snow Leopard) --- common/ipc_mutex.cpp | 11 +++++++++-- zone/client_packet.cpp | 26 +++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/common/ipc_mutex.cpp b/common/ipc_mutex.cpp index 1ac88538c..331144eca 100644 --- a/common/ipc_mutex.cpp +++ b/common/ipc_mutex.cpp @@ -55,10 +55,17 @@ namespace EQEmu { std::string final_name = name; final_name += ".lock"; +#ifdef __DARWIN +#if __DARWIN_C_LEVEL < 200809L imp_->fd_ = open(final_name.c_str(), - O_RDWR | O_CREAT | O_CLOEXEC, + O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); - +#else + imp_->fd_ = open(final_name.c_str(), + O_RDWR | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR); +#endif +#endif if(imp_->fd_ == -1) { EQ_EXCEPT("IPC Mutex", "Could not create mutex."); } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index cde2b143f..0a50d8cdc 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12483,7 +12483,15 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) // char *GuildName = (char *)app->pBuffer; +#ifdef DARWIN +#if __DARWIN_C_LEVEL < 200809L + if (strlen(GuildName) > 60) +#else if(strnlen(GuildName, 64) > 60) +#endif // __DARWIN_C_LEVEL +#else + if(strnlen(GuildName, 64) > 60) +#endif // DARWIN { Message(clientMessageError, "Guild name too long."); return; @@ -12941,7 +12949,15 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct); LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer; +#ifdef DARWIN +#if __DARWIN_C_LEVEL < 200809L + if (strlen(pts->Comment) > 256) +#else if(strnlen(pts->Comment, 256) > 256) +#endif // __DARWIN_C_LEVEL +#else + if(strnlen(pts->Comment, 256) > 256) +#endif // DARWIN return; ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38); @@ -12968,7 +12984,15 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct); LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer; - if(strnlen(gts->Comment, 256) > 256) +#ifdef DARWIN +#if __DARWIN_C_LEVEL < 200809L + if (strlen(gts->Comment) > 256) +#else + if(strnlen(gts->Comment, 256) > 256) +#endif // __DARWIN_C_LEVEL +#else + if(strnlen(gts->Comment, 256) > 256) +#endif // __DARWIN return; ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43); From 35fad4d5a71435525a31172d9b29f0f8f89ed519 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Sat, 5 Apr 2014 17:18:19 -0400 Subject: [PATCH 52/82] Added a rule to enable multiple procs from the same weapon's other slots if a proc is deemed to trigger, Defaults to true. --- changelog.txt | 7 ++++++- common/ruletypes.h | 1 + utils/sql/git/optional/2014_04_05_ProcRules.sql | 1 + zone/attack.cpp | 4 ++++ 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 utils/sql/git/optional/2014_04_05_ProcRules.sql diff --git a/changelog.txt b/changelog.txt index b16c283ad..67f2311e4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,7 +5,12 @@ Akkadius: (Performance Adjustment) Removed AsyncLoadVariables from InterserverTi see that the query 'SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= timestamp' is called every 10 seconds. This function is loading variables that are initially loaded on World and Zone bootup. When running a large amount of zone servers, the amount of MySQL chatter that is produced is enormous and unnecessary. For example, if I ran 400 zone servers, I would see 3,456,000 unnecessary queries from all idle or active zone processes in a 24 hour interval. - +Secrets: Added a rule to enable multiple procs from the same weapon's other slots if a proc is deemed to trigger, Defaults to true. + If Combat:OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not. + This is for some servers that may want to have as many procs triggering from weapons as possible in a single round. + +Optional SQL: utils/sql/git/optional/2014_04_05_ProcRules.sql + == 04/04/2014 == Kayen: Implemented 'Physical Resists' (Resist Type 9) to be consistent with live based on extensive parsing. SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations. diff --git a/common/ruletypes.h b/common/ruletypes.h index 39886c9f5..dec5a6a8f 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -396,6 +396,7 @@ RULE_BOOL ( Combat, UseArcheryBonusRoll, false) //Make the 51+ archery bonus req RULE_INT ( Combat, ArcheryBonusChance, 50) RULE_INT ( Combat, BerserkerFrenzyStart, 35) RULE_INT ( Combat, BerserkerFrenzyEnd, 45) +RULE_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round RULE_CATEGORY_END() RULE_CATEGORY( NPC ) diff --git a/utils/sql/git/optional/2014_04_05_ProcRules.sql b/utils/sql/git/optional/2014_04_05_ProcRules.sql new file mode 100644 index 000000000..4566f45fd --- /dev/null +++ b/utils/sql/git/optional/2014_04_05_ProcRules.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:OneProcPerWeapon', 'true', 'If OneProcPerWeapon is not enabled, we reset the proc try for that weapon regardless of if we procced or not.'); diff --git a/zone/attack.cpp b/zone/attack.cpp index 91ce521d5..2d26f1c2b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4085,6 +4085,10 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on } } } + //If OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not. + //This is for some servers that may want to have as many procs triggering from weapons as possible in a single round. + if(!RuleB(Combat, OneProcPerWeapon)) + proced = false; if (!proced && inst) { for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) { From 07625336fd1c7cece494ae762678596a0f21c36a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 5 Apr 2014 18:47:14 -0400 Subject: [PATCH 53/82] Allow multiple aug procs if Combat:OneProcPerWeapon is false --- zone/attack.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 2d26f1c2b..3964fd934 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4113,7 +4113,8 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on } } else { ExecWeaponProc(aug_i, aug->Proc.Effect, on); - break; + if (!RuleB(Combat, OneProcPerWeapon)) + break; } } } From 8db606008955efc839267a36d7b55da69776068a Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 5 Apr 2014 16:07:13 -0700 Subject: [PATCH 54/82] Renamed errant sql file. --- ...014_04_04_PhysicalResist.txt => 2014_04_04_PhysicalResist.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename utils/sql/git/required/{2014_04_04_PhysicalResist.txt => 2014_04_04_PhysicalResist.sql} (100%) diff --git a/utils/sql/git/required/2014_04_04_PhysicalResist.txt b/utils/sql/git/required/2014_04_04_PhysicalResist.sql similarity index 100% rename from utils/sql/git/required/2014_04_04_PhysicalResist.txt rename to utils/sql/git/required/2014_04_04_PhysicalResist.sql From 11d5e4b6ca970f595af3bc4f4070b91a96348557 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 5 Apr 2014 18:09:12 -0500 Subject: [PATCH 55/82] Fix for the Fix for the Fix: Rule Combat:OneProcPerWeapon was created so that you can revert to the original proc functionality for custom servers that have balanced their content around having more than 1 aug proc on weapons. By having this rule set to 'false' you revert this functionality. This rule is set to 'true' by default as the original functionality from Live was intended to be --- changelog.txt | 20 +++----------------- zone/attack.cpp | 2 +- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/changelog.txt b/changelog.txt index 67f2311e4..3b4bd984d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,27 +1,13 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 04/05/2014 == +Akkadius: Fix for the Fix for the Fix: Rule Combat:OneProcPerWeapon was created so that you can revert to the original proc functionality + for custom servers that have balanced their content around having more than 1 aug proc on weapons. By having this rule set to 'false' you revert this functionality. + This rule is set to 'true' by default as the original functionality from Live was intended to be Akkadius: (Performance Adjustment) Removed AsyncLoadVariables from InterserverTimer.Check() in both zone and world. By watching the MySQL general.log file on mass zone idle activity, you can see that the query 'SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= timestamp' is called every 10 seconds. This function is loading variables that are initially loaded on World and Zone bootup. When running a large amount of zone servers, the amount of MySQL chatter that is produced is enormous and unnecessary. For example, if I ran 400 zone servers, I would see 3,456,000 unnecessary queries from all idle or active zone processes in a 24 hour interval. -Secrets: Added a rule to enable multiple procs from the same weapon's other slots if a proc is deemed to trigger, Defaults to true. - If Combat:OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not. - This is for some servers that may want to have as many procs triggering from weapons as possible in a single round. - -Optional SQL: utils/sql/git/optional/2014_04_05_ProcRules.sql - -== 04/04/2014 == -Kayen: Implemented 'Physical Resists' (Resist Type 9) to be consistent with live based on extensive parsing. - SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations. - -Required SQL: utils/sql/git/optional/2014_04_04_PhysicalResists.sql - -== 04/03/2014 == -Kayen: Implemented live like spell projectiles (ie. Mage Bolts). - -Optional SQL: utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql -Note: The rules in this SQL are for setting the item id for the graphic used by the projectile on different clients. == 04/01/2014 == demonstar55: Implemented ability for a merchant to open and close shop. diff --git a/zone/attack.cpp b/zone/attack.cpp index 3964fd934..324f5942d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4113,7 +4113,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on } } else { ExecWeaponProc(aug_i, aug->Proc.Effect, on); - if (!RuleB(Combat, OneProcPerWeapon)) + if (RuleB(Combat, OneProcPerWeapon)) break; } } From 8ad1c1d8a99b858f04eeabb59b09edb6ae94aaba Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 5 Apr 2014 18:12:58 -0500 Subject: [PATCH 56/82] changelog.txt --- changelog.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/changelog.txt b/changelog.txt index 3b4bd984d..4a7b1a43f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,7 +8,24 @@ Akkadius: (Performance Adjustment) Removed AsyncLoadVariables from InterserverTi see that the query 'SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= timestamp' is called every 10 seconds. This function is loading variables that are initially loaded on World and Zone bootup. When running a large amount of zone servers, the amount of MySQL chatter that is produced is enormous and unnecessary. For example, if I ran 400 zone servers, I would see 3,456,000 unnecessary queries from all idle or active zone processes in a 24 hour interval. +Secrets: Added a rule to enable multiple procs from the same weapon's other slots if a proc is deemed to trigger, Defaults to true. + If Combat:OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not. + This is for some servers that may want to have as many procs triggering from weapons as possible in a single round. + +Optional SQL: utils/sql/git/optional/2014_04_05_ProcRules.sql + +== 04/04/2014 == +Kayen: Implemented 'Physical Resists' (Resist Type 9) to be consistent with live based on extensive parsing. + SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations. +Required SQL: utils/sql/git/optional/2014_04_04_PhysicalResists.sql + +== 04/03/2014 == +Kayen: Implemented live like spell projectiles (ie. Mage Bolts). + +Optional SQL: utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql +Note: The rules in this SQL are for setting the item id for the graphic used by the projectile on different clients. + == 04/01/2014 == demonstar55: Implemented ability for a merchant to open and close shop. Lua quest functions: e.self:MerchantOpenShop() and e.self:MerchantCloseShop() From 7e75f7559a516c91f5149d1040edd86f3791e28b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 6 Apr 2014 03:03:18 -0400 Subject: [PATCH 57/82] Fix issue with optional SQL --- utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql b/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql index 70d8594b6..3601a5fef 100644 --- a/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql +++ b/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql @@ -1,3 +1,3 @@ INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_Titanium', '1113', 'Item id for Titanium clients for Fire spell projectile.'); -INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'FRProjectileItem_SOF', '80684', 'Item id for Titanium clients for Fire spell projectile.'); -INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'FRProjectileItem_NPC', '80684', 'Item id for Titanium clients for Fire spell projectile.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_SOF', '80684', 'Item id for Titanium clients for Fire spell projectile.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_NPC', '80684', 'Item id for Titanium clients for Fire spell projectile.'); From 8e55b6618e4f160023cb2f489a32a0fc05840031 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 6 Apr 2014 05:39:37 -0400 Subject: [PATCH 58/82] Dual Wield changes - see posting linked in changelog.txt --- changelog.txt | 6 ++++++ zone/mob.cpp | 38 +++++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/changelog.txt b/changelog.txt index 4a7b1a43f..dafc65268 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/06/2014 == +Uleat: Changed Mob::CanThisClassDualWield() behavior. This should let non-monk/beastlord dual-wielding classes attack with either fist as long as the other hand is occupied. +Notes: + See this thread for more information and to provide feedback: http://www.eqemulator.org/forums/showthread.php?p=229328#post229328 + + == 04/05/2014 == Akkadius: Fix for the Fix for the Fix: Rule Combat:OneProcPerWeapon was created so that you can revert to the original proc functionality for custom servers that have balanced their content around having more than 1 aug proc on weapons. By having this rule set to 'false' you revert this functionality. diff --git a/zone/mob.cpp b/zone/mob.cpp index d3199d0e6..87486714c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2088,27 +2088,35 @@ void Mob::SetAttackTimer() { } -bool Mob::CanThisClassDualWield(void) const -{ - if (!IsClient()) { +bool Mob::CanThisClassDualWield(void) const { + if(!IsClient()) { return(GetSkill(SkillDualWield) > 0); - } else { - const ItemInst* inst = CastToClient()->GetInv().GetItem(SLOT_PRIMARY); + } + else if(CastToClient()->HasSkill(SkillDualWield)) { + const ItemInst* pinst = CastToClient()->GetInv().GetItem(SLOT_PRIMARY); + const ItemInst* sinst = CastToClient()->GetInv().GetItem(SLOT_SECONDARY); + // 2HS, 2HB, or 2HP - if (inst && inst->IsType(ItemClassCommon)) { - const Item_Struct* item = inst->GetItem(); - if ((item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HPiercing)) + if(pinst && pinst->IsWeapon()) { + const Item_Struct* item = pinst->GetItem(); + + if((item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HPiercing)) return false; - } else { - //No weapon in hand... using hand-to-hand... - //only monks and beastlords? can dual wield their fists. - if(class_ != MONK && class_ != MONKGM && class_ != BEASTLORD && class_ != BEASTLORDGM) { - return false; - } } - return (CastToClient()->HasSkill(SkillDualWield)); // No skill = no chance + // OffHand Weapon + if(sinst && !sinst->IsWeapon()) + return false; + + // Dual-Wielding Empty Fists + if(!pinst && !sinst) + if(class_ != MONK && class_ != MONKGM && class_ != BEASTLORD && class_ != BEASTLORDGM) + return false; + + return true; } + + return false; } bool Mob::CanThisClassDoubleAttack(void) const From 8b2f325cd0621b4d943a96e244555712abecf627 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 7 Apr 2014 21:21:08 -0400 Subject: [PATCH 59/82] Fix wiz innate crit issue --- zone/effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 72bc518f5..48e30639c 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -99,7 +99,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (MakeRandomInt(1,100) <= RuleI(Spells, WizCritChance))) { - ratio = MakeRandomInt(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. + ratio += MakeRandomInt(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. Critical = true; } From 35cd98c7a763f62d22fa7d0e2f9029ef3a168e1a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 9 Apr 2014 05:17:36 -0400 Subject: [PATCH 60/82] -Implemented the ability to properly use live spell projectile graphics. This data is found in the player_1 field of the spells_new table. -Rule for this set to be disabled by default. -Enable IF your server uses an UF+ spell file and your players use UF+ clients -Otherwise your better off with alternative method/rules already implemented so that all players can see the effect. -Added ability for PERL ProjectileAnim function to only need an IT#### and not an actual item id. -If you want it in LUA somebody needs to add it. - Change to wizard innate critical ratios based on parse data. --- changelog.txt | 12 ++++- common/ruletypes.h | 1 + .../2014_04_03_SpellProjectileRules.sql | 4 +- .../2014_04_09_SpellProjectileRule.sql | 7 +++ zone/effects.cpp | 4 +- zone/mob.h | 2 +- zone/perl_mob.cpp | 9 ++-- zone/special_attacks.cpp | 9 ++-- zone/spell_effects.cpp | 50 +++++++++++-------- 9 files changed, 64 insertions(+), 34 deletions(-) create mode 100644 utils/sql/git/optional/2014_04_09_SpellProjectileRule.sql diff --git a/changelog.txt b/changelog.txt index dafc65268..08f8df3c4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,10 +1,20 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/09/2014 == +Kayen: Implemented ability to use the actual live spell projectile graphics that are defined in the modern spell file. + *This is disabled by default. Recommend enabling if your server uses an UF+ spell file AND most of your players use UF+ clients. +Kayen: Expanded the PERL ProjectileAnim(mob, item_id, [IsArrow?, speed, angle, tilt, arc, IDFile]) so you can now just set the weapon model graphic IT#### +Example: ProjectileAnim($npc, 0, 0, 1, 0, 0, 0, "IT10747") This will shoot an SK 2.0 sword. +Kayen: Updated wizard innate critical damage modifier to be from 20-70% of base damage (based on live parses) + + +Optional SQL: utils/sql/git/optional/2014_04_09_SpellProjectileRule.sql +Note: This sql also contains a query to check if your spell file is compatible. + == 04/06/2014 == Uleat: Changed Mob::CanThisClassDualWield() behavior. This should let non-monk/beastlord dual-wielding classes attack with either fist as long as the other hand is occupied. Notes: See this thread for more information and to provide feedback: http://www.eqemulator.org/forums/showthread.php?p=229328#post229328 - == 04/05/2014 == Akkadius: Fix for the Fix for the Fix: Rule Combat:OneProcPerWeapon was created so that you can revert to the original proc functionality diff --git a/common/ruletypes.h b/common/ruletypes.h index dec5a6a8f..5612a90c0 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -307,6 +307,7 @@ RULE_INT ( Spells, SuccorFailChance, 2) //Determines chance for a succor spell n RULE_INT ( Spells, FRProjectileItem_Titanium, 1113) // Item id for Titanium clients for Fire 'spell projectile'. RULE_INT ( Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for Fire 'spell projectile'. RULE_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'. +RULE_BOOL ( Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql b/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql index 3601a5fef..01950cc1a 100644 --- a/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql +++ b/utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql @@ -1,3 +1,3 @@ INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_Titanium', '1113', 'Item id for Titanium clients for Fire spell projectile.'); -INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_SOF', '80684', 'Item id for Titanium clients for Fire spell projectile.'); -INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_NPC', '80684', 'Item id for Titanium clients for Fire spell projectile.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_SOF', '80684', 'Item id for SOF clients for Fire spell projectile.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_NPC', '80684', 'Item id for NPC to use for Fire spell projectile.'); diff --git a/utils/sql/git/optional/2014_04_09_SpellProjectileRule.sql b/utils/sql/git/optional/2014_04_09_SpellProjectileRule.sql new file mode 100644 index 000000000..1a4c9352e --- /dev/null +++ b/utils/sql/git/optional/2014_04_09_SpellProjectileRule.sql @@ -0,0 +1,7 @@ +-- Recommend enabling if your server uses an UF+ spell file and your players use UF+ client. This will give the proper graphics for all spell projectiles. +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:UseLiveSpellProjectileGFX', false, ' Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file.'); + +-- Use this query to check if your spell file is compatible +-- If it returns in the player_1 field IT##### it will work. +SELECT id,name,player_1 from spells_new WHERE targettype = 1; + diff --git a/zone/effects.cpp b/zone/effects.cpp index 48e30639c..329c69084 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -79,7 +79,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch - int chance = RuleI(Spells, BaseCritChance); + int chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level) chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation; @@ -99,7 +99,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (MakeRandomInt(1,100) <= RuleI(Spells, WizCritChance))) { - ratio += MakeRandomInt(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. + ratio += MakeRandomInt(20,70); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed) Critical = true; } diff --git a/zone/mob.h b/zone/mob.h index 4ee5da5a8..a5ce86903 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -161,7 +161,7 @@ public: virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color); void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone); void ProjectileAnimation(Mob* to, int item_id, bool IsArrow = false, float speed = 0, - float angle = 0, float tilt = 0, float arc = 0); + float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr); void ChangeSize(float in_size, bool bNoRestriction = false); inline uint8 SeeInvisible() const { return see_invis; } inline bool SeeInvisibleUndead() const { return see_invis_undead; } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index a2958db27..86cf58981 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -6856,7 +6856,7 @@ XS(XS_Mob_ProjectileAnim); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_ProjectileAnim) { dXSARGS; - if (items < 3 || items > 8) + if (items < 3 || items > 9) Perl_croak(aTHX_ "Usage: Mob::ProjectileAnim(THIS, mob, item_id, IsArrow?, speed, angle, tilt, arc)"); { @@ -6868,6 +6868,7 @@ XS(XS_Mob_ProjectileAnim) float angle = 0; float tilt = 0; float arc = 0; + char * IDFile = nullptr; if (sv_derived_from(ST(0), "Mob")) { IV tmp = SvIV((SV*)SvRV(ST(0))); @@ -6903,7 +6904,9 @@ XS(XS_Mob_ProjectileAnim) arc = (float)SvNV(ST(7)); } - THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc); + if (items > 8) { IDFile = (char *)SvPV_nolen(ST(8)); } + + THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc, IDFile); } XSRETURN_EMPTY; @@ -8389,7 +8392,7 @@ XS(boot_Mob) newXSproto(strcpy(buf, "CheckLoS"), XS_Mob_CheckLoS, file, "$$"); newXSproto(strcpy(buf, "CheckLoSToLoc"), XS_Mob_CheckLoSToLoc, file, "$$$$;$"); newXSproto(strcpy(buf, "FindGroundZ"), XS_Mob_FindGroundZ, file, "$$$;$"); - newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$"); + newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$$"); newXSproto(strcpy(buf, "HasNPCSpecialAtk"), XS_Mob_HasNPCSpecialAtk, file, "$$"); newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$"); newXSproto(strcpy(buf, "SetFlyMode"), XS_Mob_SetFlyMode, file, "$$"); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 8d95524fe..54e70f3d0 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1346,7 +1346,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil safe_delete(outapp); } -void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc) { +void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile) { const Item_Struct* item = nullptr; uint8 item_type = 0; @@ -1380,6 +1380,10 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f arc = 50; } + const char *item_IDFile = item->IDFile; + + if (IDFile && (strncmp(IDFile, "IT", 2) == 0)) + item_IDFile = IDFile; // See SendItemAnimation() for some notes on this struct EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct)); @@ -1393,7 +1397,7 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f as->item_id = item->ID; as->item_type = item_type; as->skill = 0; // Doesn't seem to have any effect - strn0cpy(as->model_name, item->IDFile, 16); + strn0cpy(as->model_name, item_IDFile, 16); as->velocity = speed; as->launch_angle = angle; as->tilt = tilt; @@ -1406,7 +1410,6 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f } - void NPC::DoClassAttacks(Mob *target) { if(target == nullptr) return; //gotta have a target for all these diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index ce299ed36..ad7e273fd 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3351,10 +3351,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste case SE_Root: { /* Root formula derived from extensive personal live parses - Kayen - ROOT has a 40% chance to do a resist check to break. - Resist check has NO LOWER bounds. - If multiple roots on target. Root in first slot will be checked first to break from nukes. - If multiple roots on target and broken by spell. Roots are removed ONE at a time in order of buff slot. + ROOT has a 70% chance to do a resist check to break. */ if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){ @@ -6064,26 +6061,35 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){ projectile_timer.Start(250); } - //Only use fire graphic for fire spells. - if (spells[spell_id].resisttype == RESIST_FIRE) { - - if (IsClient()){ - if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic. - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5); - else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5); - } - - else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5); - - if (spells[spell_id].CastingAnim == 64) - anim = 44; //Corrects for animation error. + //This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files. + if (RuleB(Spells, UseLiveSpellProjectileGFX)) { + ProjectileAnimation(spell_target,0, false, 1.5,0,0,0, spells[spell_id].player_1); } - //Pending other types of projectile graphics. (They will function but with a default arrow graphic for now) - else - ProjectileAnimation(spell_target,0, 1, 1.5); + //This allows limited support for server using older spell files that do not contain data for bolt graphics. + else { + //Only use fire graphic for fire spells. + if (spells[spell_id].resisttype == RESIST_FIRE) { + + if (IsClient()){ + if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic. + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5); + else + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5); + } + + else + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5); + + } + + //Default to an arrow if not using a mage bolt (Use up to date spell file and enable above rules for best results) + else + ProjectileAnimation(spell_target,0, 1, 1.5); + } + + if (spells[spell_id].CastingAnim == 64) + anim = 44; //Corrects for animation error. DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation. return true; From f0a0f0677f4a32d16f34e2aa1518e57f49fa2acd Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 10 Apr 2014 00:47:20 -0400 Subject: [PATCH 61/82] Rule to make player cast swarm pets untargetable with F8. New npc_types field to allow any NPC to be untargetable with F8 Swarm pets will now be heal/buffable like live. See change log for more details. Note: The method used here to prevent targeting is a hack but the only side effect it turns affected NPC's names Yellow. --- changelog.txt | 9 +++++++++ common/ruletypes.h | 1 + zone/mob.cpp | 5 +++-- zone/mob.h | 3 ++- zone/npc.cpp | 24 ++++++++++++++++++++++++ zone/npc.h | 1 + zone/zonedb.cpp | 8 +++++--- zone/zonedump.h | 1 + 8 files changed, 46 insertions(+), 6 deletions(-) diff --git a/changelog.txt b/changelog.txt index 08f8df3c4..d21af0a8b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,14 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/10/2014 == +Kayen: Added 'no_target_hotkey' field to npc_types table. This will prevent the NPC from being targeted with F8 (Warning: Will also turn it's name YELLOW) +Kayen: Added a rule to make all (Player cast) Swarm Pets not targetable with F8 (nearest NPC) by default (Warning: Will also turn pets names YELLOW). This is semi-hack but it works. +Kayen: Player cast swarm pets can now be healed and buffed consistent with live. + +Optional SQL: utils/sql/git/optional/2014_04_10_SwarmPetNotTargetableWithHotKey.sql +Required SQL: utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql +Note: For the required new npc_types field you DO NOT need to set values for swarm pets if you enable the above rule. + == 04/09/2014 == Kayen: Implemented ability to use the actual live spell projectile graphics that are defined in the modern spell file. *This is disabled by default. Recommend enabling if your server uses an UF+ spell file AND most of your players use UF+ clients. diff --git a/common/ruletypes.h b/common/ruletypes.h index 5612a90c0..fa07c752e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -135,6 +135,7 @@ RULE_CATEGORY_END() RULE_CATEGORY( Pets ) RULE_REAL( Pets, AttackCommandRange, 150 ) RULE_BOOL( Pets, UnTargetableSwarmPet, false ) +RULE_BOOL( Pets, SwarmPetNotTargetableWithHotKey, false ) //On SOF+ clients this a semi-hack to make swarm pets not F8 targetable. RULE_CATEGORY_END() RULE_CATEGORY( GM ) diff --git a/zone/mob.cpp b/zone/mob.cpp index 87486714c..2538a3a49 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -891,7 +891,8 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players ns->spawn.NPC = IsClient() ? 0 : 1; - ns->spawn.IsMercenary = IsMerc() ? 1 : 0; + ns->spawn.IsMercenary = (IsMerc() || no_target_hotkey) ? 1 : 0; + ns->spawn.petOwnerId = ownerid; ns->spawn.haircolor = haircolor; @@ -1626,7 +1627,7 @@ void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 la->parm4 = parm4; la->parm5 = parm5; // Note that setting the b values to 0 will disable the related effect from the corresponding parameter. - // Setting the a value appears to have no affect at all. + // Setting the a value appears to have no affect at all.s la->value1a = 1; la->value1b = 1; la->value2a = 1; diff --git a/zone/mob.h b/zone/mob.h index a5ce86903..bf3399251 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -170,7 +170,7 @@ public: bool IsInvisible(Mob* other = 0) const; void SetInvisible(uint8 state); bool AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* weapon); - + //Song bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1); bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot); @@ -945,6 +945,7 @@ protected: int16 petpower; uint32 follow; uint32 follow_dist; + bool no_target_hotkey; uint8 gender; uint16 race; diff --git a/zone/npc.cpp b/zone/npc.cpp index f83d1d31b..20bc025fc 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -224,6 +224,8 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float p_depop = false; loottable_id = d->loottable_id; + no_target_hotkey = d->no_target_hotkey; + primary_faction = 0; SetNPCFactionID(d->npc_faction_id); @@ -1719,6 +1721,22 @@ bool Mob::HasNPCSpecialAtk(const char* parse) { void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { Mob::FillSpawnStruct(ns, ForWho); + + //Basic settings to make sure swarm pets work properly. + if (GetSwarmOwner()) { + Client *c = entity_list.GetClientByID(GetSwarmOwner()); + if(c) { + SetAllowBeneficial(1); //Allow client cast swarm pets to be heal/buffed. + //This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'! + if (RuleB(Pets, SwarmPetNotTargetableWithHotKey)) + ns->spawn.IsMercenary = 1; + } + //NPC cast swarm pets should still be targetable with F8. + else + ns->spawn.IsMercenary = 0; + } + + //Not recommended if using above (However, this will work better on older clients). if (RuleB(Pets, UnTargetableSwarmPet)) { if(GetOwnerID() || GetSwarmOwner()) { ns->spawn.is_pet = 1; @@ -1867,6 +1885,12 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue) return; } + if(id == "PhR") + { + PhR = atoi(val.c_str()); + return; + } + if(id == "runspeed") { runspeed = (float)atof(val.c_str()); diff --git a/zone/npc.h b/zone/npc.h index e8877172b..406be550f 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -245,6 +245,7 @@ public: void AddLootDrop(const Item_Struct*dbitem, ItemList* itemlistconst, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange = false); virtual void DoClassAttacks(Mob *target); void CheckSignal(); + inline bool IsTargetableWithHotkey() const { return no_target_hotkey; } //waypoint crap int GetMaxWp() const { return max_wp; } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 568780e7d..119d83cc2 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1099,7 +1099,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { "npc_types.underwater," "npc_types.emoteid," "npc_types.spellscale," - "npc_types.healscale"; + "npc_types.healscale," + "npc_types.no_target_hotkey"; MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id); @@ -1191,7 +1192,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF) << 8; tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF); tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; - + int i; if (armor_tint_id > 0) { @@ -1281,7 +1282,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { tmpNPCType->emoteid = atoi(row[r++]); tmpNPCType->spellscale = atoi(row[r++]); tmpNPCType->healscale = atoi(row[r++]); - + tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false; + // If NPC with duplicate NPC id already in table, // free item we attempted to add. if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end()) diff --git a/zone/zonedump.h b/zone/zonedump.h index 2c036be61..ab8f88d23 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -120,6 +120,7 @@ struct NPCType uint32 emoteid; float spellscale; float healscale; + bool no_target_hotkey; }; /* From 48a9f05efbd258ae0e39164b72b9331ef684f0c3 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 10 Apr 2014 00:55:31 -0400 Subject: [PATCH 62/82] SQL --- .../optional/2014_04_10_SwarmPetNotTargetableWithHotKey.sql | 4 ++++ utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql | 3 +++ 2 files changed, 7 insertions(+) create mode 100644 utils/sql/git/optional/2014_04_10_SwarmPetNotTargetableWithHotKey.sql create mode 100644 utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql diff --git a/utils/sql/git/optional/2014_04_10_SwarmPetNotTargetableWithHotKey.sql b/utils/sql/git/optional/2014_04_10_SwarmPetNotTargetableWithHotKey.sql new file mode 100644 index 000000000..25274e53b --- /dev/null +++ b/utils/sql/git/optional/2014_04_10_SwarmPetNotTargetableWithHotKey.sql @@ -0,0 +1,4 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Pets:SwarmPetNotTargetableWithHotKey', 'false', ' On SOF+ clients this a semi-hack to make swarm pets not F8 targetable. Warning: Turns pet names Yellow'); + + + diff --git a/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql b/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql new file mode 100644 index 000000000..12e9963a5 --- /dev/null +++ b/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql @@ -0,0 +1,3 @@ +ALTER TABLE `npc_types` ADD `no_target_hotkey` tinyint( 1 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `healscale`; + + From f9b46b46b1a1d83ce9ad3abba139c782fe127d5f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 12 Apr 2014 00:18:19 -0400 Subject: [PATCH 63/82] Revision of slow mitigation code. Fix for the slow mitigation spam messages Converted value from FLOAT to INT Use SQL to update your npc_types table --- changelog.txt | 8 +++++ .../required/2014_04_12_SlowMitigation.sql | 7 +++++ zone/StringIDs.h | 14 +++++---- zone/bonuses.cpp | 30 +++++-------------- zone/lua_npc.cpp | 4 +-- zone/mob.cpp | 27 +++++++++++++++-- zone/mob.h | 5 ++-- zone/npc.cpp | 2 +- zone/npc.h | 2 +- zone/perl_npc.cpp | 4 +-- zone/spell_effects.cpp | 27 ++++++++++++++--- zone/zonedb.cpp | 2 +- zone/zonedump.h | 2 +- 13 files changed, 90 insertions(+), 44 deletions(-) create mode 100644 utils/sql/git/required/2014_04_12_SlowMitigation.sql diff --git a/changelog.txt b/changelog.txt index d21af0a8b..083250403 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,13 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- + +== 04/12/2014 == +Kayen: Fixed an with the slow mitigation code that would cause it to spam the message. Optimized the way the variable is handled for slow mitigation. + +Required SQL: utils/sql/git/required/2014_04_12_SlowMitigation.sql +Note: This changes the variable type in the sql table from FLOAT to INT and updates your database. +(When setting slow mitigation 50 = 50%, 100 = 100% ect. You can also set > 100 which will cause slow to become haste now with appropriate in game msg given) + == 04/10/2014 == Kayen: Added 'no_target_hotkey' field to npc_types table. This will prevent the NPC from being targeted with F8 (Warning: Will also turn it's name YELLOW) Kayen: Added a rule to make all (Player cast) Swarm Pets not targetable with F8 (nearest NPC) by default (Warning: Will also turn pets names YELLOW). This is semi-hack but it works. diff --git a/utils/sql/git/required/2014_04_12_SlowMitigation.sql b/utils/sql/git/required/2014_04_12_SlowMitigation.sql new file mode 100644 index 000000000..f2ac0d68d --- /dev/null +++ b/utils/sql/git/required/2014_04_12_SlowMitigation.sql @@ -0,0 +1,7 @@ +-- Convert all values from FLOAT to INT +UPDATE npc_types SET slow_mitigation = slow_mitigation * 100; + +-- Change variable type from FLOAT TO INT +ALTER TABLE npc_types MODIFY slow_mitigation smallint(4); + + diff --git a/zone/StringIDs.h b/zone/StringIDs.h index 05bbffdec..c916b1de0 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -243,12 +243,12 @@ #define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1! #define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end. #define REWIND_WAIT 4059 //You must wait a bit longer before using the rewind command again. -#define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can! -#define CORPSEDRAG_ALREADY 4062 //You are already dragging %1. +#define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can! +#define CORPSEDRAG_ALREADY 4062 //You are already dragging %1. #define CORPSEDRAG_SOMEONE_ELSE 4063 //Someone else is dragging %1. -#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1. -#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses. -#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse. +#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1. +#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses. +#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse. #define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters. #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. @@ -300,6 +300,10 @@ #define GAIN_RAID_LEADERSHIP_EXP 8789 // #define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining) #define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell. +#define SLOW_MOSTLY_SUCCESSFUL 9029 //Your spell was mostly successful. +#define SLOW_PARTIALLY_SUCCESSFUL 9030 // Your spell was partially successful. +#define SLOW_SLIGHTLY_SUCCESSFUL 9031 //Your spell was slightly successful. +#define SPELL_OPPOSITE_EFFECT 9032 //Your spell may have had the opposite effect of what you desired. #define YOU_HEAL 9068 //You have healed %1 for %2 points of damage. #define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3. #define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage. diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 56f8b9f5f..c726333f2 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1338,19 +1338,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } } else if ((effect_value - 100) < 0) { // Slow - //Slow Mitigation works by taking the amount that would be slowed, and adding a multiplied version of the difference. int real_slow_value = (100 - effect_value) * -1; - if (slow_mitigation){ - int new_effect_value = SlowMitigation(false,caster,real_slow_value); - if (new_effect_value < newbon->haste) { - newbon->haste = new_effect_value; - SlowMitigation(true,caster); - } - } - else { - if (real_slow_value < newbon->haste) - newbon->haste = real_slow_value; - } + real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); + if (real_slow_value < newbon->haste) + newbon->haste = real_slow_value; } break; } @@ -1365,6 +1356,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } else if ((effect_value - 100) < 0) { // Slow int real_slow_value = (100 - effect_value) * -1; + real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); if (real_slow_value < newbon->hastetype2) newbon->hastetype2 = real_slow_value; } @@ -1374,6 +1366,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_AttackSpeed3: { if (effect_value < 0){ //Slow + effect_value -= ((effect_value * GetSlowMitigation()/100)); if (effect_value < newbon->hastetype3) newbon->hastetype3 = effect_value; } @@ -1392,18 +1385,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne effect_value = effect_value * -1; if (effect_value > 0 && effect_value > newbon->inhibitmelee) { - - if (slow_mitigation){ - int new_effect_value = SlowMitigation(false,caster,effect_value); - if (new_effect_value > newbon->inhibitmelee) { - newbon->inhibitmelee = new_effect_value; - SlowMitigation(true,caster); - } - } - - else if (effect_value > newbon->inhibitmelee) { + effect_value -= ((effect_value * GetSlowMitigation()/100)); + if (effect_value > newbon->inhibitmelee) newbon->inhibitmelee = effect_value; - } } break; diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index 1dd61cc91..41524af15 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -408,7 +408,7 @@ void Lua_NPC::SetSpellFocusHeal(int focus) { } float Lua_NPC::GetSlowMitigation() { - Lua_Safe_Call_Real(); + Lua_Safe_Call_Int(); return self->GetSlowMitigation(); } @@ -526,7 +526,7 @@ luabind::scope lua_register_npc() { .def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell) .def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG) .def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal) - .def("GetSlowMitigation", (float(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation) + .def("GetSlowMitigation", (int(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation) .def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed) .def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating) .def("GetSpawnKillCount", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnKillCount) diff --git a/zone/mob.cpp b/zone/mob.cpp index 2538a3a49..74c44c7cc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4651,8 +4651,12 @@ void Mob::CastOnNumHitFade(uint32 spell_id) } } +/* int Mob::SlowMitigation(bool slow_msg, Mob *caster, int slow_value) { + if (slow_mitigation) + return 0; + float int_slow_mitigation = slow_mitigation * 100.0f; if (int_slow_mitigation > 100.0f) @@ -4663,10 +4667,10 @@ int Mob::SlowMitigation(bool slow_msg, Mob *caster, int slow_value) if (caster && caster->IsClient()) { if ((int_slow_mitigation > 0.0f) && (int_slow_mitigation < 26.0f)) - caster->Message(262, "Your spell was mostly successful"); + caster->Message_StringID(MT_Spells, SLOW_MOSTLY_SUCCESSFUL); else if ((int_slow_mitigation >= 26.0f) && (int_slow_mitigation < 74.0f)) - caster->Message(262, "Your spell was partially successful"); + caster->Message_StringID(MT_Spells, SLOW_PARTIALLY_SUCCESSFUL); else if ((int_slow_mitigation >= 74.0f) && (int_slow_mitigation < 101.0f)) caster->Message(262, "Your spell was slightly successful"); @@ -4680,6 +4684,25 @@ int Mob::SlowMitigation(bool slow_msg, Mob *caster, int slow_value) return slow_value; } } +*/ + +void Mob::SlowMitigation(Mob* caster) +{ + if (GetSlowMitigation() && caster && caster->IsClient()) + { + if ((GetSlowMitigation() > 0) && (GetSlowMitigation() < 26)) + caster->Message_StringID(MT_SpellFailure, SLOW_MOSTLY_SUCCESSFUL); + + else if ((GetSlowMitigation() >= 26) && (GetSlowMitigation() < 74)) + caster->Message_StringID(MT_SpellFailure, SLOW_PARTIALLY_SUCCESSFUL); + + else if ((GetSlowMitigation() >= 74) && (GetSlowMitigation() < 101)) + caster->Message_StringID(MT_SpellFailure, SLOW_SLIGHTLY_SUCCESSFUL); + + else if (GetSlowMitigation() > 100) + caster->Message_StringID(MT_SpellFailure, SPELL_OPPOSITE_EFFECT); + } +} uint16 Mob::GetSkillByItemType(int ItemType) { diff --git a/zone/mob.h b/zone/mob.h index bf3399251..d79f30867 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -580,7 +580,7 @@ public: void CastOnCurer(uint32 spell_id); void CastOnCure(uint32 spell_id); void CastOnNumHitFade(uint32 spell_id); - int SlowMitigation(bool slow_msg=false, Mob *caster = nullptr,int slow_value = 0); + void SlowMitigation(Mob* caster); int16 GetCritDmgMob(uint16 skill); int16 GetMeleeDamageMod_SE(uint16 skill); int16 GetMeleeMinDamageMod_SE(uint16 skill); @@ -598,6 +598,7 @@ public: bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true); bool ImprovedTaunt(); bool TryRootFadeByDamage(int buffslot, Mob* attacker); + int16 GetSlowMitigation() const {return slow_mitigation;} void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); @@ -1023,7 +1024,7 @@ protected: Timer attack_dw_timer; Timer ranged_timer; float attack_speed; //% increase/decrease in attack speed (not haste) - float slow_mitigation; // Allows for a slow mitigation based on a % in decimal form. IE, 1 = 100% mitigation, .5 is 50% + float slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; diff --git a/zone/npc.cpp b/zone/npc.cpp index 20bc025fc..2611300d8 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -2004,7 +2004,7 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue) if(id == "slow_mitigation") { - slow_mitigation = atof(val.c_str()); + slow_mitigation = atoi(val.c_str()); return; } if(id == "loottable_id") diff --git a/zone/npc.h b/zone/npc.h index 406be550f..55bed4abc 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -233,7 +233,7 @@ public: uint32 GetMaxDMG() const {return max_dmg;} uint32 GetMinDMG() const {return min_dmg;} - float GetSlowMitigation() const {return slow_mitigation;} + int16 GetSlowMitigation() const {return slow_mitigation;} float GetAttackSpeed() const {return attack_speed;} bool IsAnimal() const { return(bodytype == BT_Animal); } uint16 GetPetSpellID() const {return pet_spell_id;} diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 2ab0ca036..891709113 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -2023,7 +2023,7 @@ XS(XS_NPC_GetSlowMitigation) Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)"); { NPC * THIS; - float RETVAL; + int16 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "NPC")) { @@ -2036,7 +2036,7 @@ XS(XS_NPC_GetSlowMitigation) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); RETVAL = THIS->GetSlowMitigation(); - XSprePUSH; PUSHn((double)RETVAL); + XSprePUSH; PUSHn((UV)RETVAL); } XSRETURN(1); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index ad7e273fd..61e220d2d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2624,14 +2624,37 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) HealDamage(dmg, caster); } } + + break; } case SE_Taunt: { if (IsNPC()) caster->Taunt(this->CastToNPC(), false, spell.base[i]); + + break; } + case SE_AttackSpeed: + if (spell.base[i] < 100) + SlowMitigation(caster); + break; + + case SE_AttackSpeed2: + if (spell.base[i] < 100) + SlowMitigation(caster); + break; + + case SE_AttackSpeed3: + if (spell.base[i] < 0) + SlowMitigation(caster); + break; + + case SE_AttackSpeed4: + SlowMitigation(caster); + break; + // Handled Elsewhere case SE_ImmuneFleeing: case SE_NegateSpellEffect: @@ -2717,10 +2740,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_DivineSave: case SE_Accuracy: case SE_Flurry: - case SE_AttackSpeed: - case SE_AttackSpeed2: - case SE_AttackSpeed3: - case SE_AttackSpeed4: case SE_ImprovedDamage: case SE_ImprovedHeal: case SE_IncreaseSpellHaste: diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 119d83cc2..893a1a27a 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1273,7 +1273,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true; tmpNPCType->ATK = atoi(row[r++]); tmpNPCType->accuracy_rating = atoi(row[r++]); - tmpNPCType->slow_mitigation = atof(row[r++]); + tmpNPCType->slow_mitigation = atoi(row[r++]); tmpNPCType->maxlevel = atoi(row[r++]); tmpNPCType->scalerate = atoi(row[r++]); tmpNPCType->private_corpse = atoi(row[r++]) == 1 ? true : false; diff --git a/zone/zonedump.h b/zone/zonedump.h index ab8f88d23..39c6b39d7 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -111,7 +111,7 @@ struct NPCType int accuracy_rating; //10 = 1% accuracy bool findable; //can be found with find command bool trackable; - float slow_mitigation; // Slow mitigation % in decimal form. + int16 slow_mitigation; uint8 maxlevel; uint32 scalerate; bool private_corpse; From aedd70f5faee97019ad735ed848295e54bb4120f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 12 Apr 2014 03:31:30 -0400 Subject: [PATCH 64/82] removed commented out code. --- zone/mob.cpp | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 74c44c7cc..48452f26a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4651,41 +4651,6 @@ void Mob::CastOnNumHitFade(uint32 spell_id) } } -/* -int Mob::SlowMitigation(bool slow_msg, Mob *caster, int slow_value) -{ - if (slow_mitigation) - return 0; - - float int_slow_mitigation = slow_mitigation * 100.0f; - - if (int_slow_mitigation > 100.0f) - return 0; - - if (slow_msg) - { - if (caster && caster->IsClient()) - { - if ((int_slow_mitigation > 0.0f) && (int_slow_mitigation < 26.0f)) - caster->Message_StringID(MT_Spells, SLOW_MOSTLY_SUCCESSFUL); - - else if ((int_slow_mitigation >= 26.0f) && (int_slow_mitigation < 74.0f)) - caster->Message_StringID(MT_Spells, SLOW_PARTIALLY_SUCCESSFUL); - - else if ((int_slow_mitigation >= 74.0f) && (int_slow_mitigation < 101.0f)) - caster->Message(262, "Your spell was slightly successful"); - } - return 0; - } - - else - { - slow_value -= (slow_value * static_cast(int_slow_mitigation) / 100); - return slow_value; - } -} -*/ - void Mob::SlowMitigation(Mob* caster) { if (GetSlowMitigation() && caster && caster->IsClient()) From 272bbdb4d217911ce8c2572c4fdfb62af9d97013 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 12 Apr 2014 21:15:13 -0400 Subject: [PATCH 65/82] Revision of SE_PercentHeal Will now apply focus properly Can now do damage if set to negative value --- zone/spell_effects.cpp | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 61e220d2d..6b48627d4 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -284,20 +284,31 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Percental Heal: %+i (%d%% max)", spell.max[i], effect_value); #endif - //im not 100% sure about this implementation. - //the spell value forumula dosent work for these... at least spell 3232 anyways - int32 val = spell.max[i]; + int32 val = GetMaxHP() * spell.base[i] / 100; - if(caster) - val = caster->GetActSpellHealing(spell_id, val, this); + //This effect can also do damage by percent. + if (val < 0) { - int32 mhp = GetMaxHP(); - int32 cap = mhp * spell.base[i] / 100; + if (-val > spell.max[i]) + val = -spell.max[i]; - if(cap < val) - val = cap; + if (caster) + val = caster->GetActSpellDamage(spell_id, val, this); - if(val > 0) + } + + else + { + if (val > spell.max[i]) + val = spell.max[i]; + + if(caster) + val = caster->GetActSpellHealing(spell_id, val, this); + } + + if (val < 0) + Damage(caster, -val, spell_id, spell.skill, false, buffslot, false); + else HealDamage(val, caster); break; From 884b0291f7caca1d2dd7ac467e15b27337a220b7 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 15 Apr 2014 12:00:33 -0500 Subject: [PATCH 66/82] Exported $client->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg) - Will be available for simple plugin use Exported $client->ExpeditionMessage(THIS, ExpdID, Message) - In use with custom expedition mod that will be released soon --- changelog.txt | 3 +++ zone/client.cpp | 27 ++++++++++++++++++++ zone/client.h | 1 + zone/perl_client.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/changelog.txt b/changelog.txt index 083250403..eff2f9460 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/15/2014 == +Akkadius: Exported $client->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg) - Will be available for simple plugin use +Akkadius: Exported $client->ExpeditionMessage(THIS, ExpdID, Message) - In use with custom expedition mod that will be released soon == 04/12/2014 == Kayen: Fixed an with the slow mitigation code that would cause it to spam the message. Optimized the way the variable is handled for slow mitigation. diff --git a/zone/client.cpp b/zone/client.cpp index 827a3f3f8..0f1314211 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8213,3 +8213,30 @@ void Client::PlayMP3(const char* fname) QueuePacket(outapp); safe_delete(outapp); } + +void Client::ExpeditionSay(const char *str, int ExpID) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (!database.RunQuery(query,MakeAnyLenString(&query, "SELECT `player_name` FROM `cust_inst_players` WHERE `inst_id` = %i", ExpID),errbuf,&result)){ + safe_delete_array(query); + return; + } + + safe_delete_array(query); + + if(result) + this->Message(14, "You say to the expedition, '%s'", str); + + while((row = mysql_fetch_row(result))) { + const char* CharName = row[0]; + if(strcmp(CharName, this->GetCleanName()) != 0) + worldserver.SendEmoteMessage(CharName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str); + // ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3])); + } + + mysql_free_result(result); + +} \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index 76b2c6774..87f085a44 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1170,6 +1170,7 @@ public: std::string GetAccountFlag(std::string flag); float GetDamageMultiplier(SkillUseTypes); void Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_consume); void PlayMP3(const char* fname); + void Client::ExpeditionSay(const char *str, int ExpID); int mod_client_damage(int damage, SkillUseTypes skillinuse, int hand, const ItemInst* weapon, Mob* other); bool mod_client_message(char* message, uint8 chan_num); bool mod_can_increase_skill(SkillUseTypes skillid, Mob* against_who); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index da81c03d7..0bab3cfb0 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5898,6 +5898,64 @@ XS(XS_Client_PlayMP3) XSRETURN_EMPTY; } +XS(XS_Client_ExpeditionMessage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ExpeditionMessage) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::ExpeditionMessage(THIS, ExpdID, Message)"); + { + Client * THIS; + int ExpdID = (int)SvUV(ST(1)); + const char * Message = (const char *)SvPV_nolen(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ExpeditionSay(Message, ExpdID); + } + XSRETURN_EMPTY; +} + +//Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg) + +XS(XS_Client_SendMarqueeMessage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendMarqueeMessage) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: Client::SendMarqueeMessage(THIS, type, priority, fade_in, fade_out, duration, msg)"); + { + Client * THIS; + uint32 type = (uint32)SvUV(ST(1)); + uint32 priority = (uint32)SvUV(ST(2)); + uint32 fade_in = (uint32)SvUV(ST(3)); + uint32 fade_out = (uint32)SvUV(ST(4)); + uint32 duration = (uint32)SvUV(ST(5)); + std::string msg = (std::string)SvPV_nolen(ST(6)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6134,6 +6192,8 @@ XS(boot_Client) newXSproto(strcpy(buf, "SilentMessage"), XS_Client_SilentMessage, file, "$$"); newXSproto(strcpy(buf, "PlayMP3"), XS_Client_PlayMP3, file, "$;$"); newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$"); + newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$"); + newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$"); XSRETURN_YES; } From fc9e7a3a3bc31b35cab5883be9364341c0917d38 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 16 Apr 2014 12:44:02 -0400 Subject: [PATCH 67/82] Fix linux compile issue with 884b0291 --- zone/client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client.h b/zone/client.h index 87f085a44..8eb31ba1e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1170,7 +1170,7 @@ public: std::string GetAccountFlag(std::string flag); float GetDamageMultiplier(SkillUseTypes); void Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_consume); void PlayMP3(const char* fname); - void Client::ExpeditionSay(const char *str, int ExpID); + void ExpeditionSay(const char *str, int ExpID); int mod_client_damage(int damage, SkillUseTypes skillinuse, int hand, const ItemInst* weapon, Mob* other); bool mod_client_message(char* message, uint8 chan_num); bool mod_can_increase_skill(SkillUseTypes skillid, Mob* against_who); From 0b2281967b89e9b604ccc37690c75046729fc650 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 18 Apr 2014 19:37:26 -0500 Subject: [PATCH 68/82] Added #command error message suppression for those who don't want to see 'Command is not recognized' constantly - You need to have rule 'Chat:SuppressCommandErrors' set to true, this is set to false by default - Required SQL: 2014_04_18_Suppress_Command_Error.sql --- changelog.txt | 5 +++++ common/ruletypes.h | 1 + utils/sql/git/required/2014_04_18_Suppress_Command_Error.sql | 1 + zone/client.cpp | 5 +++-- 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 utils/sql/git/required/2014_04_18_Suppress_Command_Error.sql diff --git a/changelog.txt b/changelog.txt index eff2f9460..2e314b440 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/18/2014 == +Akkadius: Added #command error message suppression for those who don't want to see 'Command is not recognized' constantly + - You need to have rule 'Chat:SuppressCommandErrors' set to true, this is set to false by default + - Required SQL: 2014_04_18_Suppress_Command_Error.sql + == 04/15/2014 == Akkadius: Exported $client->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg) - Will be available for simple plugin use Akkadius: Exported $client->ExpeditionMessage(THIS, ExpdID, Message) - In use with custom expedition mod that will be released soon diff --git a/common/ruletypes.h b/common/ruletypes.h index fa07c752e..20bd0ce78 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -464,6 +464,7 @@ RULE_BOOL ( Chat, ServerWideAuction, true) RULE_BOOL ( Chat, EnableVoiceMacros, true) RULE_BOOL ( Chat, EnableMailKeyIPVerification, true) RULE_BOOL ( Chat, EnableAntiSpam, true) +RULE_BOOL ( Chat, SuppressCommandErrors, false) // Do not suppress by default RULE_INT ( Chat, MinStatusToBypassAntiSpam, 100) RULE_INT ( Chat, MinimumMessagesPerInterval, 4) RULE_INT ( Chat, MaximumMessagesPerInterval, 12) diff --git a/utils/sql/git/required/2014_04_18_Suppress_Command_Error.sql b/utils/sql/git/required/2014_04_18_Suppress_Command_Error.sql new file mode 100644 index 000000000..2d10ea458 --- /dev/null +++ b/utils/sql/git/required/2014_04_18_Suppress_Command_Error.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('0', 'Chat:SuppressCommandErrors', 'true', 'This will suppress "Command is not recognized"'); \ No newline at end of file diff --git a/zone/client.cpp b/zone/client.cpp index 0f1314211..702303c5c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1022,11 +1022,12 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s if(command_dispatch(this, message) == -2) { if(parse->PlayerHasQuestSub(EVENT_COMMAND)) { int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0); - if(i == 0) { + if(i == 0 && !RuleB(Chat, SuppressCommandErrors)) { Message(13, "Command '%s' not recognized.", message); } } else { - Message(13, "Command '%s' not recognized.", message); + if(!RuleB(Chat, SuppressCommandErrors)) + Message(13, "Command '%s' not recognized.", message); } } break; From 87994ebfba8511892a12b442228d32981937302a Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Sun, 20 Apr 2014 00:26:54 -0400 Subject: [PATCH 69/82] smoke commits everyday --- changelog.txt | 3 +++ common/debug.cpp | 6 ------ zone/client.cpp | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/changelog.txt b/changelog.txt index 2e314b440..e1cbd7f80 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/20/2014 == +Secrets: Changed the functionality of EQDEBUG cmake flag. It now suppresses logs, but shows crashes in any scenario when set to 1. It will also now show crashes even if the log system is disabled. + == 04/18/2014 == Akkadius: Added #command error message suppression for those who don't want to see 'Command is not recognized' constantly - You need to have rule 'Chat:SuppressCommandErrors' set to true, this is set to false by default diff --git a/common/debug.cpp b/common/debug.cpp index badd337c1..632ca7b01 100644 --- a/common/debug.cpp +++ b/common/debug.cpp @@ -42,22 +42,16 @@ EQEMuLog::EQEMuLog() { for (int i=0; i= 2 pLogStatus[i] = 1 | 2; -#else - pLogStatus[i] = 0; -#endif logCallbackFmt[i] = nullptr; logCallbackBuf[i] = nullptr; logCallbackPva[i] = nullptr; } // TODO: Make this read from an ini or something, everyone has different opinions on what it should be -#if EQDEBUG < 2 pLogStatus[Status] = 2; pLogStatus[Error] = 2; pLogStatus[Quest] = 2; pLogStatus[Commands] = 1; -#endif logFileValid = true; } diff --git a/zone/client.cpp b/zone/client.cpp index 702303c5c..7aa7bd748 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8162,7 +8162,7 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_ if(!auto_consume) //no message if the client consumed for us entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name); -#if EQDEBUG >= 1 +#if EQDEBUG >= 5 LogFile->write(EQEMuLog::Debug, "Eating from slot:%i", (int)slot); #endif } @@ -8179,7 +8179,7 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_ if(!auto_consume) //no message if the client consumed for us entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); -#if EQDEBUG >= 1 +#if EQDEBUG >= 5 LogFile->write(EQEMuLog::Debug, "Drinking from slot:%i", (int)slot); #endif } From f915eed0f4337ca734d40d8a091e49da358b7a10 Mon Sep 17 00:00:00 2001 From: Corysia Taware Date: Sat, 19 Apr 2014 21:45:54 -0700 Subject: [PATCH 70/82] Apparently 0 is a vaid file descriptor in OSX --- common/TCPServer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/TCPServer.cpp b/common/TCPServer.cpp index cfba6e94a..2e3abfe2f 100644 --- a/common/TCPServer.cpp +++ b/common/TCPServer.cpp @@ -97,8 +97,12 @@ void BaseTCPServer::ListenNewConnections() { from.sin_family = AF_INET; fromlen = sizeof(from); LockMutex lock(&MSock); +#ifndef DARWIN // Corysia - On OSX, 0 is a valid fd. if (!sock) return; +#else + if (sock == -1) return; +#endif // Check for pending connects #ifdef _WINDOWS From 635d28cd65a17ae995c2099a132ffd9dc69d9eaa Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 20 Apr 2014 17:26:51 -0700 Subject: [PATCH 71/82] Change to how quest signals work --- changelog.txt | 1 + zone/questmgr.cpp | 13 +++---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/changelog.txt b/changelog.txt index e1cbd7f80..007a4153f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 04/20/2014 == Secrets: Changed the functionality of EQDEBUG cmake flag. It now suppresses logs, but shows crashes in any scenario when set to 1. It will also now show crashes even if the log system is disabled. +KLS: Change to how signals work, signals with no delay will now be added to the signal queue. This addresses an odd timing issue where NPCs are in a state of life/death flux when a signal from event_death goes off. == 04/18/2014 == Akkadius: Added #command error message suppression for those who don't want to see 'Command is not recognized' constantly diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 4e5ceb1da..80fd1ef8c 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1277,16 +1277,9 @@ void QuestManager::signalwith(int npc_id, int signal_id, int wait_ms) { if(wait_ms > 0) { STimerList.push_back(SignalTimer(wait_ms, npc_id, signal_id)); return; - } - - if (npc_id<1) - { - printf("signal() bad npcid=%i\n",npc_id); - } - else - { - //initiator* signalnpc=0; - entity_list.SignalMobsByNPCID(npc_id, signal_id); + } else { + STimerList.push_back(SignalTimer(0, npc_id, signal_id)); + return; } } From c16fe3c8109fff073ff774d8897493b53ddcacb0 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 20 Apr 2014 17:46:43 -0700 Subject: [PATCH 72/82] CMake flags to defining what is logged and where --- CMakeLists.txt | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ changelog.txt | 3 +- common/debug.cpp | 23 +++++--------- 3 files changed, 90 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e39033656..91eff4d10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,78 @@ SET(EQEMU_DEBUG_LEVEL 5 CACHE STRING "EQEmu debug level: 10 - More errors than you ever wanted to see" ) +SET(EQEMU_LOG_LEVEL_STATUS 2 CACHE STRING "EQEmu logging level for [Status]: + 0 - Disabled + 1 - Ouput to File Enabled + 2 - Output to stdout Enabled + 3 - Output to File and stdout Enabled + 8 - Output to stderr Enabled + 9 - Output to File and stderr Enabled + 11 - Output to File, stdout and stderr Enabled" +) + +SET(EQEMU_LOG_LEVEL_NORMAL 3 CACHE STRING "EQEmu logging level for [Normal]: + 0 - Disabled + 1 - Ouput to File Enabled + 2 - Output to stdout Enabled + 3 - Output to File and stdout Enabled + 8 - Output to stderr Enabled + 9 - Output to File and stderr Enabled + 11 - Output to File, stdout and stderr Enabled" +) + +SET(EQEMU_LOG_LEVEL_ERROR 2 CACHE STRING "EQEmu logging level for [Error]: + 0 - Disabled + 1 - Ouput to File Enabled + 2 - Output to stdout Enabled + 3 - Output to File and stdout Enabled + 8 - Output to stderr Enabled + 9 - Output to File and stderr Enabled + 11 - Output to File, stdout and stderr Enabled" +) + +SET(EQEMU_LOG_LEVEL_DEBUG 3 CACHE STRING "EQEmu logging level for [Debug]: + 0 - Disabled + 1 - Ouput to File Enabled + 2 - Output to stdout Enabled + 3 - Output to File and stdout Enabled + 8 - Output to stderr Enabled + 9 - Output to File and stderr Enabled + 11 - Output to File, stdout and stderr Enabled" +) + +SET(EQEMU_LOG_LEVEL_QUEST 2 CACHE STRING "EQEmu logging level for [Quest]: + 0 - Disabled + 1 - Ouput to File Enabled + 2 - Output to stdout Enabled + 3 - Output to File and stdout Enabled + 8 - Output to stderr Enabled + 9 - Output to File and stderr Enabled + 11 - Output to File, stdout and stderr Enabled" +) + +SET(EQEMU_LOG_LEVEL_COMMANDS 1 CACHE STRING "EQEmu logging level for [Commands]: + 0 - Disabled + 1 - Ouput to File Enabled + 2 - Output to stdout Enabled + 3 - Output to File and stdout Enabled + 8 - Output to stderr Enabled + 9 - Output to File and stderr Enabled + 11 - Output to File, stdout and stderr Enabled" +) + +SET(EQEMU_LOG_LEVEL_CRASH 3 CACHE STRING "EQEmu logging level for [Crash]: + 0 - Disabled + 1 - Ouput to File Enabled + 2 - Output to stdout Enabled + 3 - Output to File and stdout Enabled + 8 - Output to stderr Enabled + 9 - Output to File and stderr Enabled + 11 - Output to File, stdout and stderr Enabled" +) + +MARK_AS_ADVANCED(EQEMU_LOG_LEVEL_STATUS EQEMU_LOG_LEVEL_NORMAL EQEMU_LOG_LEVEL_ERROR EQEMU_LOG_LEVEL_DEBUG EQEMU_LOG_LEVEL_QUEST EQEMU_LOG_LEVEL_COMMANDS EQEMU_LOG_LEVEL_CRASH) + SET(EQEMU_STREAM_SEND_RATE 1048576 CACHE STRING "Advanced: Base amount of data stream can send before throttle.") SET(EQEMU_STREAM_DECAY_RATE 78642 CACHE STRING "Advanced: Base amount of data stream recovers per tic.") SET(EQEMU_STREAM_RETRANSMIT_TIMEOUT_MUL 3.0 CACHE STRING "Advanced: Multiplier on retransmit timeout.") @@ -182,6 +254,14 @@ ADD_DEFINITIONS(-DDECAYBASE=${EQEMU_STREAM_DECAY_RATE}) ADD_DEFINITIONS(-DRETRANSMIT_TIMEOUT_MULT=${EQEMU_STREAM_RETRANSMIT_TIMEOUT_MUL}) ADD_DEFINITIONS(-DRETRANSMIT_TIMEOUT_MAX=${EQEMU_STREAM_RETRANSMIT_TIMEOUT_MAX}) ADD_DEFINITIONS(-DAVERAGE_DELTA_MAX=${EQEMU_STREAM_AVERAGE_DELTA_MAX}) +ADD_DEFINITIONS(-DLOG_LEVEL_STATUS=${EQEMU_LOG_LEVEL_STATUS}) +ADD_DEFINITIONS(-DLOG_LEVEL_NORMAL=${EQEMU_LOG_LEVEL_NORMAL}) +ADD_DEFINITIONS(-DLOG_LEVEL_ERROR=${EQEMU_LOG_LEVEL_ERROR}) +ADD_DEFINITIONS(-DLOG_LEVEL_DEBUG=${EQEMU_LOG_LEVEL_DEBUG}) +ADD_DEFINITIONS(-DLOG_LEVEL_QUEST=${EQEMU_LOG_LEVEL_QUEST}) +ADD_DEFINITIONS(-DLOG_LEVEL_COMMANDS=${EQEMU_LOG_LEVEL_COMMANDS}) +ADD_DEFINITIONS(-DLOG_LEVEL_CRASH=${EQEMU_LOG_LEVEL_CRASH}) + IF(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS) ADD_DEFINITIONS(-DRETRANSMIT_ACKED_PACKETS=true) ELSE(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS) diff --git a/changelog.txt b/changelog.txt index 007a4153f..ff2dc1d1c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,7 +2,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 04/20/2014 == Secrets: Changed the functionality of EQDEBUG cmake flag. It now suppresses logs, but shows crashes in any scenario when set to 1. It will also now show crashes even if the log system is disabled. -KLS: Change to how signals work, signals with no delay will now be added to the signal queue. This addresses an odd timing issue where NPCs are in a state of life/death flux when a signal from event_death goes off. +KLS: Change to how quest signals work, signals with no delay will now be added to the signal queue. This addresses an odd timing issue where NPCs are in a state of life/death flux when a signal from event_death goes off. +KLS: Added cmake flags to define how logging behavior works for each different log type. == 04/18/2014 == Akkadius: Added #command error message suppression for those who don't want to see 'Command is not recognized' constantly diff --git a/common/debug.cpp b/common/debug.cpp index 632ca7b01..fa114a338 100644 --- a/common/debug.cpp +++ b/common/debug.cpp @@ -35,23 +35,20 @@ static const char* FileNames[EQEMuLog::MaxLogID] = { "logs/eqemu", "logs/eqemu", static const char* LogNames[EQEMuLog::MaxLogID] = { "Status", "Normal", "Error", "Debug", "Quest", "Command", "Crash" }; EQEMuLog::EQEMuLog() { -// MOpen = new Mutex; -// MLog = new Mutex*[MaxLogID]; -// fp = new FILE*[MaxLogID]; -// pLogStatus = new uint8[MaxLogID]; for (int i=0; i Date: Sun, 20 Apr 2014 20:54:21 -0400 Subject: [PATCH 73/82] Hatelist crash fix --- changelog.txt | 1 + zone/hate_list.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/changelog.txt b/changelog.txt index ff2dc1d1c..08d664a70 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) Secrets: Changed the functionality of EQDEBUG cmake flag. It now suppresses logs, but shows crashes in any scenario when set to 1. It will also now show crashes even if the log system is disabled. KLS: Change to how quest signals work, signals with no delay will now be added to the signal queue. This addresses an odd timing issue where NPCs are in a state of life/death flux when a signal from event_death goes off. KLS: Added cmake flags to define how logging behavior works for each different log type. +Secrets: Crash fix for Hatelist crash observed == 04/18/2014 == Akkadius: Added #command error message suppression for those who don't want to see 'Command is not recognized' constantly diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 7f986949b..2fbc528fc 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -445,6 +445,17 @@ Mob *HateList::GetMostHate(){ Mob *HateList::GetRandom() { int count = list.size(); + if(count == 0) //If we don't have any entries it'll crash getting a random 0, -1 position. + return NULL; + + if(count == 1) //No need to do all that extra work if we only have one hate entry + { + if(*list.begin()) // Just in case tHateEntry is invalidated somehow... + return (*list.begin())->ent; + + return NULL; + } + auto iterator = list.begin(); int random = MakeRandomInt(0, count - 1); for (int i = 0; i < random; i++) From fe542deb89e9ff75ed5baf6d41faa96fa0092f0a Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Sun, 20 Apr 2014 21:00:50 -0400 Subject: [PATCH 74/82] indentation is important --- zone/hate_list.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 2fbc528fc..6980746df 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -451,7 +451,7 @@ Mob *HateList::GetRandom() if(count == 1) //No need to do all that extra work if we only have one hate entry { if(*list.begin()) // Just in case tHateEntry is invalidated somehow... - return (*list.begin())->ent; + return (*list.begin())->ent; return NULL; } From dffee38dc622254a9847e1a09d79c572d1aaf388 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Mon, 21 Apr 2014 22:19:12 -0400 Subject: [PATCH 75/82] It's never ogre with these hatelist crashes.. --- changelog.txt | 3 +++ zone/entity.cpp | 33 +++++++++++++++++++++++++++++++++ zone/entity.h | 1 + zone/hate_list.cpp | 20 +++++++++++++------- zone/mob.cpp | 2 -- 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/changelog.txt b/changelog.txt index 08d664a70..3a6858ffb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/21/2014 == +Secrets: Crash fix for more hatelist crashes. + == 04/20/2014 == Secrets: Changed the functionality of EQDEBUG cmake flag. It now suppresses logs, but shows crashes in any scenario when set to 1. It will also now show crashes even if the log system is disabled. KLS: Change to how quest signals work, signals with no delay will now be added to the signal queue. This addresses an odd timing issue where NPCs are in a state of life/death flux when a signal from event_death goes off. diff --git a/zone/entity.cpp b/zone/entity.cpp index ed5ee33f0..3f6130745 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1283,6 +1283,33 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets) } } +void EntityList::RemoveFromTargets(uint16 MobID, bool RemoveFromXTargets) +{ + Mob* mob = entity_list.GetMob(MobID); + + if(!mob) + return; + + auto it = mob_list.begin(); + while (it != mob_list.end()) { + Mob *m = it->second; + ++it; + + if (!m) + continue; + + m->RemoveFromHateList(mob); + + if (RemoveFromXTargets) { + if (m->IsClient()) + m->CastToClient()->RemoveXTarget(mob, false); + // FadingMemories calls this function passing the client. + else if (mob->IsClient()) + mob->CastToClient()->RemoveXTarget(m, false); + } + } +} + void EntityList::RemoveFromXTargets(Mob *mob) { auto it = client_list.begin(); @@ -2080,6 +2107,9 @@ bool EntityList::RemoveMob(uint16 delete_id) if (delete_id == 0) return true; + //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. + entity_list.RemoveFromTargets(delete_id, true); + auto it = mob_list.find(delete_id); if (it != mob_list.end()) { if (npc_list.count(delete_id)) @@ -2101,6 +2131,9 @@ bool EntityList::RemoveMob(Mob *delete_mob) if (delete_mob == 0) return true; + //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. + entity_list.RemoveFromTargets(delete_mob, true); + auto it = mob_list.begin(); while (it != mob_list.end()) { if (it->second == delete_mob) { diff --git a/zone/entity.h b/zone/entity.h index ede4b3bea..9db343921 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -290,6 +290,7 @@ public: void ExpeditionWarning(uint32 minutes_left); void RemoveFromTargets(Mob* mob, bool RemoveFromXTargets = false); + void RemoveFromTargets(uint16 mob, bool RemoveFromXTargets = false); void RemoveFromXTargets(Mob* mob); void RemoveFromAutoXTargets(Mob* mob); void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 6980746df..abc33866e 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -60,14 +60,16 @@ void HateList::Wipe() while(iterator != list.end()) { Mob* m = (*iterator)->ent; - parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0); - //iterator + if(m) + { + parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0); + if(m->IsClient()) + m->CastToClient()->DecrementAggroCount(); + } delete (*iterator); iterator = list.erase(iterator); - if(m->IsClient()) - m->CastToClient()->DecrementAggroCount(); } } @@ -203,6 +205,9 @@ void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAd bool HateList::RemoveEnt(Mob *ent) { + if (!ent) + return NULL; + bool found = false; auto iterator = list.begin(); @@ -211,14 +216,15 @@ bool HateList::RemoveEnt(Mob *ent) if((*iterator)->ent == ent) { parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "0", 0); - delete (*iterator); - iterator = list.erase(iterator); found = true; if(ent->IsClient()) ent->CastToClient()->DecrementAggroCount(); - } + delete (*iterator); + iterator = list.erase(iterator); + + } else ++iterator; } diff --git a/zone/mob.cpp b/zone/mob.cpp index 48452f26a..0fa0b1c07 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -401,8 +401,6 @@ Mob::~Mob() if(!corpse || (corpse && !corpse->IsPlayerCorpse())) entity_list.QueueClients(this, &app, true); - entity_list.RemoveFromTargets(this, true); - if(trade) { Mob *with = trade->With(); if(with && with->IsClient()) { From f08799f7ff68a9d237471565ab95c7da3ae4ed10 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 22 Apr 2014 00:48:46 -0400 Subject: [PATCH 76/82] Add mez break message --- zone/StringIDs.h | 1 + zone/attack.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/zone/StringIDs.h b/zone/StringIDs.h index c916b1de0..9a222d276 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -304,6 +304,7 @@ #define SLOW_PARTIALLY_SUCCESSFUL 9030 // Your spell was partially successful. #define SLOW_SLIGHTLY_SUCCESSFUL 9031 //Your spell was slightly successful. #define SPELL_OPPOSITE_EFFECT 9032 //Your spell may have had the opposite effect of what you desired. +#define HAS_BEEN_AWAKENED 9037 //%1 has been awakened by %2. #define YOU_HEAL 9068 //You have healed %1 for %2 points of damage. #define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3. #define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage. diff --git a/zone/attack.cpp b/zone/attack.cpp index 324f5942d..e1f6b5135 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3616,6 +3616,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons //fade mez if we are mezzed if (IsMezzed()) { mlog(COMBAT__HITS, "Breaking mez due to attack."); + entity_list.MessageClose_StringID(this, true, 100, MT_WornOff, + HAS_BEEN_AWAKENED, GetCleanName(), attacker->GetCleanName()); BuffFadeByEffect(SE_Mez); } From dd1f5f6a119a2c32fad7aeed39d77d9ae9970f37 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Tue, 22 Apr 2014 13:24:07 -0400 Subject: [PATCH 77/82] More hatelist fixes. Trying to nail the crash :S --- zone/bot.cpp | 1 + zone/client.cpp | 3 ++- zone/entity.cpp | 6 ------ zone/hate_list.cpp | 7 +++---- zone/merc.cpp | 1 + zone/npc.cpp | 2 ++ 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 1526beddc..ae793cd47 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -223,6 +223,7 @@ Bot::~Bot() { GetPet()->Depop(); entity_list.RemoveBot(GetID()); + entity_list.RemoveFromTargets(GetID(), true); } void Bot::SetBotID(uint32 botID) { diff --git a/zone/client.cpp b/zone/client.cpp index 7aa7bd748..b2b9ff023 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -426,7 +426,8 @@ Client::~Client() { //let the stream factory know were done with this stream eqs->Close(); eqs->ReleaseFromUse(); - + //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. + entity_list.RemoveFromTargets(GetID(), true); UninitializeBuffSlots(); } diff --git a/zone/entity.cpp b/zone/entity.cpp index 3f6130745..c09348384 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2107,9 +2107,6 @@ bool EntityList::RemoveMob(uint16 delete_id) if (delete_id == 0) return true; - //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. - entity_list.RemoveFromTargets(delete_id, true); - auto it = mob_list.find(delete_id); if (it != mob_list.end()) { if (npc_list.count(delete_id)) @@ -2131,9 +2128,6 @@ bool EntityList::RemoveMob(Mob *delete_mob) if (delete_mob == 0) return true; - //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. - entity_list.RemoveFromTargets(delete_mob, true); - auto it = mob_list.begin(); while (it != mob_list.end()) { if (it->second == delete_mob) { diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index abc33866e..d7cc9a9e5 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -205,9 +205,6 @@ void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAd bool HateList::RemoveEnt(Mob *ent) { - if (!ent) - return NULL; - bool found = false; auto iterator = list.begin(); @@ -215,10 +212,12 @@ bool HateList::RemoveEnt(Mob *ent) { if((*iterator)->ent == ent) { + if(ent) parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "0", 0); found = true; - if(ent->IsClient()) + + if(ent && ent->IsClient()) ent->CastToClient()->DecrementAggroCount(); delete (*iterator); diff --git a/zone/merc.cpp b/zone/merc.cpp index c8c8da6fa..05cdbb65c 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -76,6 +76,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) Merc::~Merc() { AI_Stop(); entity_list.RemoveMerc(this->GetID()); + entity_list.RemoveFromTargets(GetID(), true); UninitializeBuffSlots(); } diff --git a/zone/npc.cpp b/zone/npc.cpp index 2611300d8..13c60965a 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -392,6 +392,8 @@ NPC::~NPC() safe_delete(reface_timer); safe_delete(swarmInfoPtr); safe_delete(qGlobals); + //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. + entity_list.RemoveFromTargets(GetID(), true); UninitializeBuffSlots(); } From 6aa0c9d694f9a3b9ee23c700792fa69df58b7ad0 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Tue, 22 Apr 2014 13:37:35 -0400 Subject: [PATCH 78/82] hatelist fixes to fixes to fixes --- zone/bot.cpp | 2 +- zone/client.cpp | 2 +- zone/merc.cpp | 2 +- zone/npc.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ae793cd47..940780715 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -223,7 +223,7 @@ Bot::~Bot() { GetPet()->Depop(); entity_list.RemoveBot(GetID()); - entity_list.RemoveFromTargets(GetID(), true); + entity_list.RemoveFromTargets(this, true); } void Bot::SetBotID(uint32 botID) { diff --git a/zone/client.cpp b/zone/client.cpp index b2b9ff023..20b2b1412 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -427,7 +427,7 @@ Client::~Client() { eqs->Close(); eqs->ReleaseFromUse(); //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. - entity_list.RemoveFromTargets(GetID(), true); + entity_list.RemoveFromTargets(this, true); UninitializeBuffSlots(); } diff --git a/zone/merc.cpp b/zone/merc.cpp index 05cdbb65c..a1de41f71 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -76,7 +76,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) Merc::~Merc() { AI_Stop(); entity_list.RemoveMerc(this->GetID()); - entity_list.RemoveFromTargets(GetID(), true); + entity_list.RemoveFromTargets(this, true); UninitializeBuffSlots(); } diff --git a/zone/npc.cpp b/zone/npc.cpp index 13c60965a..0afee9b51 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -393,7 +393,7 @@ NPC::~NPC() safe_delete(swarmInfoPtr); safe_delete(qGlobals); //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. - entity_list.RemoveFromTargets(GetID(), true); + entity_list.RemoveFromTargets(this, true); UninitializeBuffSlots(); } From 564c31c54d60c8b9068e37e96bf8259e2765be32 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 23 Apr 2014 03:54:54 -0400 Subject: [PATCH 79/82] Improved SE_LimitCombatSkills will now more accurately determine if a spell is a combat proc. SE_LimitInstant will now also work when set to include instant spells. Optional SQL: utils/sql/git/optional/2014_04_23_FocusComabtProcs.sql Note: Set to false, if enabled will allow all combat procs to receive spell focuses. --- changelog.txt | 7 +++++++ common/ruletypes.h | 1 + common/spdat.cpp | 8 +++----- zone/mob.h | 1 + zone/spell_effects.cpp | 42 +++++++++++++++++++++++++++++++----------- zone/spells.cpp | 22 ++++++++++++++++++++++ 6 files changed, 65 insertions(+), 16 deletions(-) diff --git a/changelog.txt b/changelog.txt index d9022beda..a72ab499b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/23/2014 == +Kayen: Improved SE_LimitCombatSkills will now more accurately determine if a spell is a combat proc. +Kayen: SE_LimitInstant will now also work when set to include instant spells. + +Optional SQL: utils/sql/git/optional/2014_04_23_FocusComabtProcs.sql +Note: Set to false, if enabled will allow all combat procs to receive spell focuses. + == 04/21/2014 == Secrets: Crash fix for more hatelist crashes. Secrets: Hate list fixes, again. diff --git a/common/ruletypes.h b/common/ruletypes.h index 20bd0ce78..70e4ba0d2 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -309,6 +309,7 @@ RULE_INT ( Spells, FRProjectileItem_Titanium, 1113) // Item id for Titanium clie RULE_INT ( Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for Fire 'spell projectile'. RULE_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'. RULE_BOOL ( Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file. +RULE_BOOL ( Spells, FocusCombatProcs, false) //Allow all combat procs to receive focus effects. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/common/spdat.cpp b/common/spdat.cpp index c75acc056..9bf01d413 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -677,11 +677,9 @@ bool IsCombatSkill(uint16 spell_id) { if (!IsValidSpell(spell_id)) return false; - - //Check if Discipline OR melee proc (from non-castable spell) - if ((spells[spell_id].mana == 0 && - (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep)) || - ((spells[spell_id].cast_time == 0) && (spells[spell_id].recast_time == 0) && (spells[spell_id].recovery_time == 0))) + + //Check if Discipline + if ((spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep))) return true; return false; diff --git a/zone/mob.h b/zone/mob.h index d79f30867..0784b7925 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -500,6 +500,7 @@ public: bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false); bool HasProcs() const; + bool IsCombatProc(uint16 spell_id); //Logging bool IsLoggingEnabled() const { return(logging_enabled); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 6b48627d4..4ecb9d221 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1683,6 +1683,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) { CastToClient()->SummonHorse(spell_id); } + + break; } @@ -3997,6 +3999,15 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) bool LimitFailure = false; bool LimitInclude[MaxLimitInclude] = { false }; + /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells. + 0/1 SE_LimitResist + 2/3 SE_LimitSpell + 4/5 SE_LimitEffect + 6/7 SE_LimitTarget + 8/9 SE_LimitSpellGroup: + 10/11 SE_LimitCastingSkill: + Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes + */ int FocusCount = 0; std::map >::const_iterator find_iter = aa_effects.find(aa_ID); @@ -4011,7 +4022,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) base1 = iter->second.base1; base2 = iter->second.base2; slot = iter->second.slot; - + /* AA Foci's can contain multiple focus effects within the same AA. To handle this we will not automatically return zero if a limit is found. @@ -4065,8 +4076,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) break; case SE_LimitInstant: - if(spell.buffduration) + if(base1 == 1 && spell.buffduration) //Fail if not instant LimitFailure = true; + if(base1 == 0 && (spell.buffduration == 0)) //Fail if instant + LimitFailure = true; + break; case SE_LimitMaxLevel: @@ -4114,13 +4128,14 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) case SE_LimitMinDur: if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) LimitFailure = true; - break; + + break; case SE_LimitEffect: if(base1 < 0){ if(IsEffectInSpell(spell_id,-base1)) //Exclude LimitFailure = true; - } + } else{ LimitInclude[4] = true; if(IsEffectInSpell(spell_id,base1)) //Include @@ -4165,10 +4180,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) break; case SE_LimitCombatSkills: - if (base1 == 0 && IsCombatSkill(spell_id)) //Exclude Discs + if (base1 == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs LimitFailure = true; - else if (base1 == 1 && !IsCombatSkill(spell_id)) //Exclude Spells + else if (base1 == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells LimitFailure = true; + break; case SE_LimitSpellGroup: @@ -4199,7 +4215,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) //Do not use this limit more then once per spell. If multiple class, treat value like items would. if (!PassLimitClass(base1, GetClass())) LimitFailure = true; - break; + break; case SE_LimitRace: if (base1 != GetRace()) @@ -4460,8 +4476,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_LimitInstant: - if(spell.buffduration) + if(focus_spell.base[i] == 1 && spell.buffduration) //Fail if not instant return 0; + if(focus_spell.base[i] == 0 && (spell.buffduration == 0)) //Fail if instant + return 0; + break; case SE_LimitMaxLevel: @@ -4566,10 +4585,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_LimitCombatSkills: - if (focus_spell.base[i] == 0 && IsCombatSkill(spell_id)) //Exclude Disc - return 0; - else if (focus_spell.base[i] == 1 && !IsCombatSkill(spell_id)) //Include Spells + if (focus_spell.base[i] == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs return 0; + else if (focus_spell.base[i] == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells + return 0; + break; case SE_LimitSpellGroup: diff --git a/zone/spells.cpp b/zone/spells.cpp index 43a0d2b19..54e98613e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4966,6 +4966,28 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) { return false; } +bool Mob::IsCombatProc(uint16 spell_id) { + + if (RuleB(Spells, FocusCombatProcs)) + return false; + + if(spell_id == SPELL_UNKNOWN) + return(false); + + if ((spells[spell_id].cast_time == 0) && (spells[spell_id].recast_time == 0) && (spells[spell_id].recovery_time == 0)) + { + + for (int i = 0; i < MAX_PROCS; i++){ + if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id + || SkillProcs[i].spellID == spell_id || RangedProcs[i].spellID == spell_id){ + return true; + } + } + } + + return false; +} + bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id) { if(spell_id == SPELL_UNKNOWN) return(false); From 9502f0aadf5b3dcbf1f85397b76d0da77133ff65 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Wed, 23 Apr 2014 12:50:25 -0400 Subject: [PATCH 80/82] Revert "It's never ogre with these hatelist crashes.." This reverts commit dffee38dc622254a9847e1a09d79c572d1aaf388. --- changelog.txt | 1 + zone/bot.cpp | 1 - zone/client.cpp | 3 +-- zone/entity.cpp | 27 --------------------------- zone/entity.h | 1 - zone/hate_list.cpp | 3 +++ zone/merc.cpp | 1 - zone/mob.cpp | 2 ++ zone/npc.cpp | 2 -- 9 files changed, 7 insertions(+), 34 deletions(-) diff --git a/changelog.txt b/changelog.txt index d9022beda..a96d312e6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 04/21/2014 == Secrets: Crash fix for more hatelist crashes. Secrets: Hate list fixes, again. +Secrets: Revert of hatelist changes. == 04/20/2014 == Secrets: Changed the functionality of EQDEBUG cmake flag. It now suppresses logs, but shows crashes in any scenario when set to 1. It will also now show crashes even if the log system is disabled. diff --git a/zone/bot.cpp b/zone/bot.cpp index 940780715..1526beddc 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -223,7 +223,6 @@ Bot::~Bot() { GetPet()->Depop(); entity_list.RemoveBot(GetID()); - entity_list.RemoveFromTargets(this, true); } void Bot::SetBotID(uint32 botID) { diff --git a/zone/client.cpp b/zone/client.cpp index 20b2b1412..7aa7bd748 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -426,8 +426,7 @@ Client::~Client() { //let the stream factory know were done with this stream eqs->Close(); eqs->ReleaseFromUse(); - //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. - entity_list.RemoveFromTargets(this, true); + UninitializeBuffSlots(); } diff --git a/zone/entity.cpp b/zone/entity.cpp index c09348384..ed5ee33f0 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1283,33 +1283,6 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets) } } -void EntityList::RemoveFromTargets(uint16 MobID, bool RemoveFromXTargets) -{ - Mob* mob = entity_list.GetMob(MobID); - - if(!mob) - return; - - auto it = mob_list.begin(); - while (it != mob_list.end()) { - Mob *m = it->second; - ++it; - - if (!m) - continue; - - m->RemoveFromHateList(mob); - - if (RemoveFromXTargets) { - if (m->IsClient()) - m->CastToClient()->RemoveXTarget(mob, false); - // FadingMemories calls this function passing the client. - else if (mob->IsClient()) - mob->CastToClient()->RemoveXTarget(m, false); - } - } -} - void EntityList::RemoveFromXTargets(Mob *mob) { auto it = client_list.begin(); diff --git a/zone/entity.h b/zone/entity.h index 9db343921..ede4b3bea 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -290,7 +290,6 @@ public: void ExpeditionWarning(uint32 minutes_left); void RemoveFromTargets(Mob* mob, bool RemoveFromXTargets = false); - void RemoveFromTargets(uint16 mob, bool RemoveFromXTargets = false); void RemoveFromXTargets(Mob* mob); void RemoveFromAutoXTargets(Mob* mob); void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index d7cc9a9e5..d83ed4d12 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -205,6 +205,9 @@ void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAd bool HateList::RemoveEnt(Mob *ent) { + if (!ent) + return false; + bool found = false; auto iterator = list.begin(); diff --git a/zone/merc.cpp b/zone/merc.cpp index a1de41f71..c8c8da6fa 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -76,7 +76,6 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) Merc::~Merc() { AI_Stop(); entity_list.RemoveMerc(this->GetID()); - entity_list.RemoveFromTargets(this, true); UninitializeBuffSlots(); } diff --git a/zone/mob.cpp b/zone/mob.cpp index 0fa0b1c07..48452f26a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -401,6 +401,8 @@ Mob::~Mob() if(!corpse || (corpse && !corpse->IsPlayerCorpse())) entity_list.QueueClients(this, &app, true); + entity_list.RemoveFromTargets(this, true); + if(trade) { Mob *with = trade->With(); if(with && with->IsClient()) { diff --git a/zone/npc.cpp b/zone/npc.cpp index 0afee9b51..2611300d8 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -392,8 +392,6 @@ NPC::~NPC() safe_delete(reface_timer); safe_delete(swarmInfoPtr); safe_delete(qGlobals); - //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. - entity_list.RemoveFromTargets(this, true); UninitializeBuffSlots(); } From d8ad337c0ef39b57624e8dacc31400c0903f8a8f Mon Sep 17 00:00:00 2001 From: cavedude00 Date: Fri, 25 Apr 2014 12:40:25 -0700 Subject: [PATCH 81/82] Fixed a zone crash in spawn_conditions. spawn_events changes, fixes, and additions. --- changelog.txt | 11 ++ .../git/required/2014_04_25_spawn_events.sql | 1 + zone/MobAI.cpp | 21 +++ zone/embparser_api.cpp | 9 +- zone/lua_general.cpp | 4 +- zone/questmgr.cpp | 4 +- zone/questmgr.h | 2 +- zone/spawn2.cpp | 158 +++++++++++------- zone/spawn2.h | 4 +- 9 files changed, 140 insertions(+), 74 deletions(-) create mode 100644 utils/sql/git/required/2014_04_25_spawn_events.sql diff --git a/changelog.txt b/changelog.txt index 04221cf97..fe35332b6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,16 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/25/2014 == +cavedude: Corrected a crash in spawn_conditions caused by NPCs on a one way path. +cavedude: Added strict column to spawn_events which will prevent an event from enabling if it's mid-cycle. +cavedude: Prevented disabled or strict spawn_events from enabling when the zone first boots. +cavedude: Fixed the quest function toggle_spawn_event under Perl. + +If you're using the quest function toggle_spawn_event (worked on Lua only) it has changed syntax to: +toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base) + +Required SQL: utils/sql/git/required/2014_04_25_spawn_events.sql + == 04/23/2014 == Kayen: Improved SE_LimitCombatSkills will now more accurately determine if a spell is a combat proc. Kayen: SE_LimitInstant will now also work when set to include instant spells. diff --git a/utils/sql/git/required/2014_04_25_spawn_events.sql b/utils/sql/git/required/2014_04_25_spawn_events.sql new file mode 100644 index 000000000..6a96444e9 --- /dev/null +++ b/utils/sql/git/required/2014_04_25_spawn_events.sql @@ -0,0 +1 @@ +alter table spawn_events add column `strict` tinyint(4) not null default 0; \ No newline at end of file diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index bcb4b131b..681d6031b 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -1627,11 +1627,32 @@ void NPC::AI_DoMovement() { if (gridno > 0 || cur_wp==-2) { if (movetimercompleted==true) { // time to pause at wp is over + + int32 spawn_id = this->GetSpawnPointID(); + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + Spawn2 *found_spawn = nullptr; + + while(iterator.MoreElements()) + { + Spawn2* cur = iterator.GetData(); + iterator.Advance(); + if(cur->GetID() == spawn_id) + { + found_spawn = cur; + break; + } + } + if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { CastToNPC()->Depop(true); //depop and resart spawn timer + if(found_spawn) + found_spawn->SetNPCPointerNull(); } else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { CastToNPC()->Depop(false);//depop without spawn timer + if(found_spawn) + found_spawn->SetNPCPointerNull(); } else { movetimercompleted=false; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 6a7114786..91b6d1836 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1678,14 +1678,15 @@ XS(XS__toggle_spawn_event); XS(XS__toggle_spawn_event) { dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, reset_base)"); + if (items != 4) + Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, strict, reset_base)"); uint32 event_id = (int)SvIV(ST(0)); bool enabled = ((int)SvIV(ST(1))) == 0?false:true; - bool reset_base = ((int)SvIV(ST(1))) == 0?false:true; + bool strict = ((int)SvIV(ST(2))) == 0?false:true; + bool reset_base = ((int)SvIV(ST(3))) == 0?false:true; - quest_manager.toggle_spawn_event(event_id, enabled, reset_base); + quest_manager.toggle_spawn_event(event_id, enabled, strict, reset_base); XSRETURN_EMPTY; } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index c6e551d85..bd788695a 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -372,8 +372,8 @@ int lua_get_spawn_condition(const char *zone, uint32 instance_id, int condition_ return quest_manager.get_spawn_condition(zone, instance_id, condition_id); } -void lua_toggle_spawn_event(int event_id, bool enable, bool reset) { - quest_manager.toggle_spawn_event(event_id, enable, reset); +void lua_toggle_spawn_event(int event_id, bool enable, bool strict, bool reset) { + quest_manager.toggle_spawn_event(event_id, enable, strict, reset); } void lua_summon_burried_player_corpse(uint32 char_id, float x, float y, float z, float h) { diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 80fd1ef8c..ab5e79087 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1733,8 +1733,8 @@ short QuestManager::get_spawn_condition(const char *zone_short, uint32 instance_ } //toggle a spawn event -void QuestManager::toggle_spawn_event(int event_id, bool enable, bool reset_base) { - zone->spawn_conditions.ToggleEvent(event_id, enable, reset_base); +void QuestManager::toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base) { + zone->spawn_conditions.ToggleEvent(event_id, enable, strict, reset_base); } bool QuestManager::has_zone_flag(int zone_id) { diff --git a/zone/questmgr.h b/zone/questmgr.h index 81ccc58c2..89a0eca78 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -150,7 +150,7 @@ public: void showgrid(int gridid); void spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id, short new_value); short get_spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id); - void toggle_spawn_event(int event_id, bool enable, bool reset_base); + void toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base); bool has_zone_flag(int zone_id); void set_zone_flag(int zone_id); void clear_zone_flag(int zone_id); diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index 9b4963fd4..8d2d47fc0 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -303,11 +303,13 @@ void Spawn2::ForceDespawn() { npcthis->Depop(true); IsDespawned = true; + npcthis = nullptr; return; } else { npcthis->Depop(false); + npcthis = nullptr; } } } @@ -555,6 +557,7 @@ SpawnEvent::SpawnEvent() { action = ActionSet; argument = 0; period = 0xFFFFFFFF; + strict = false; memset(&next, 0, sizeof(next)); } @@ -586,26 +589,28 @@ void SpawnConditionManager::Process() { for(; cur != end; ++cur) { SpawnEvent &cevent = *cur; - if(!cevent.enabled) - continue; + if(cevent.enabled) + { + if(EQTime::IsTimeBefore(&tod, &cevent.next)) { + //this event has been triggered. + //execute the event + if(!cevent.strict || (cevent.strict && cevent.next.hour == tod.hour && cevent.next.day == tod.day && cevent.next.month == tod.month && cevent.next.year == tod.year)) + ExecEvent(cevent, true); - if(EQTime::IsTimeBefore(&tod, &cevent.next)) { - //this event has been triggered. - //execute the event - ExecEvent(cevent, true); - //add the period of the event to the trigger time - EQTime::AddMinutes(cevent.period, &cevent.next); - std::string t; - EQTime::ToString(&cevent.next, t); - _log(SPAWNS__CONDITIONS, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str()); - //save the next event time in the DB - UpdateDBEvent(cevent); - //find the next closest event timer. - FindNearestEvent(); - //minor optimization, if theres no more possible events, - //then stop trying... I dunno how worth while this is. - if(EQTime::IsTimeBefore(&next_event, &tod)) - return; + //add the period of the event to the trigger time + EQTime::AddMinutes(cevent.period, &cevent.next); + std::string t; + EQTime::ToString(&cevent.next, t); + _log(SPAWNS__CONDITIONS, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str()); + //save the next event time in the DB + UpdateDBEvent(cevent); + //find the next closest event timer. + FindNearestEvent(); + //minor optimization, if theres no more possible events, + //then stop trying... I dunno how worth while this is. + if(EQTime::IsTimeBefore(&next_event, &tod)) + return; + } } } } @@ -619,6 +624,14 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { return; //unable to find the spawn condition to operate on } + TimeOfDay_Struct tod; + zone->zone_time.getEQTimeOfDay(&tod); + if(event.strict && (event.next.hour != tod.hour || event.next.day != tod.day || event.next.month != tod.month || event.next.year != tod.year)) + { + _log(SPAWNS__CONDITIONS, "Event %d: Unable to execute. Condition is strict, and event time has already passed.", event.id); + return; + } + SpawnCondition &cond = condi->second; int16 new_value = cond.value; @@ -666,10 +679,10 @@ void SpawnConditionManager::UpdateDBEvent(SpawnEvent &event) { len = MakeAnyLenString(&query, "UPDATE spawn_events SET " "next_minute=%d, next_hour=%d, next_day=%d, next_month=%d, " - "next_year=%d, enabled=%d " + "next_year=%d, enabled=%d, strict=%d " "WHERE id=%d", event.next.minute, event.next.hour, event.next.day, event.next.month, - event.next.year, event.enabled?1:0, event.id + event.next.year, event.enabled?1:0, event.strict?1:0,event.id ); if(!database.RunQuery(query, len, errbuf)) { LogFile->write(EQEMuLog::Error, "Unable to update spawn event '%s': %s\n", query, errbuf); @@ -703,7 +716,7 @@ bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std: bool ret = false; len = MakeAnyLenString(&query, - "SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,zone " + "SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict,zone " "FROM spawn_events WHERE id=%d", event_id); if (database.RunQuery(query, len, errbuf, &result)) { safe_delete_array(query); @@ -721,12 +734,13 @@ bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std: event.enabled = atoi(row[8])==0?false:true; event.action = (SpawnEvent::Action) atoi(row[9]); event.argument = atoi(row[10]); - zone_name = row[11]; + event.strict = atoi(row[11])==0?false:true; + zone_name = row[12]; std::string t; EQTime::ToString(&event.next, t); - _log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d. Will trigger at %s", - event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, t.c_str()); + _log(SPAWNS__CONDITIONS, "(LoadDBEvent) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d. Will trigger at %s", + event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, t.c_str()); ret = true; } @@ -794,7 +808,7 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in //load spawn events SpawnEvent event; len = MakeAnyLenString(&query, - "SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument " + "SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict " "FROM spawn_events WHERE zone='%s'", zone_name); if (database.RunQuery(query, len, errbuf, &result)) { safe_delete_array(query); @@ -816,10 +830,11 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in event.enabled = atoi(row[8])==0?false:true; event.action = (SpawnEvent::Action) atoi(row[9]); event.argument = atoi(row[10]); + event.strict = atoi(row[11])==0?false:true; spawn_events.push_back(event); - _log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d", - event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument); + _log(SPAWNS__CONDITIONS, "(LoadSpawnConditions) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d", + event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict); } mysql_free_result(result); } else { @@ -847,34 +862,48 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in for(; cur != end; ++cur) { SpawnEvent &cevent = *cur; - if(!cevent.enabled) - continue; + bool StrictCheck = false; + if(cevent.strict && + cevent.next.hour == tod.hour && + cevent.next.day == tod.day && + cevent.next.month == tod.month && + cevent.next.year == tod.year) + StrictCheck = true; - //watch for special case of all 0s, which means to reset next to now - if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) { - _log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id); - memcpy(&cevent.next, &tod, sizeof(cevent.next)); - //add one period - EQTime::AddMinutes(cevent.period, &cevent.next); - //save it in the db. - UpdateDBEvent(cevent); - continue; //were done with this event. - } + //If event is disabled, or we failed the strict check, set initial spawn_condition to 0. + if(!cevent.enabled || !StrictCheck) + SetCondition(zone->GetShortName(), zone->GetInstanceID(),cevent.condition_id,0); - ran = false; - while(EQTime::IsTimeBefore(&tod, &cevent.next)) { - _log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id); - //this event has been triggered. - //execute the event - ExecEvent(cevent, false); - //add the period of the event to the trigger time - EQTime::AddMinutes(cevent.period, &cevent.next); - ran = true; - } - //only write it out if the event actually ran - if(ran) { - //save the event in the DB - UpdateDBEvent(cevent); + if(cevent.enabled) + { + //watch for special case of all 0s, which means to reset next to now + if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) { + _log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id); + memcpy(&cevent.next, &tod, sizeof(cevent.next)); + //add one period + EQTime::AddMinutes(cevent.period, &cevent.next); + //save it in the db. + UpdateDBEvent(cevent); + continue; //were done with this event. + } + + ran = false; + while(EQTime::IsTimeBefore(&tod, &cevent.next)) { + _log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id); + //this event has been triggered. + //execute the event + if(!cevent.strict || StrictCheck) + ExecEvent(cevent, false); + + //add the period of the event to the trigger time + EQTime::AddMinutes(cevent.period, &cevent.next); + ran = true; + } + //only write it out if the event actually ran + if(ran) { + //save the event in the DB + UpdateDBEvent(cevent); + } } } @@ -894,14 +923,14 @@ void SpawnConditionManager::FindNearestEvent() { int next_id = -1; for(; cur != end; ++cur) { SpawnEvent &cevent = *cur; - - if(!cevent.enabled) - continue; - - //see if this event is before our last nearest - if(EQTime::IsTimeBefore(&next_event, &cevent.next)) { - memcpy(&next_event, &cevent.next, sizeof(next_event)); - next_id = cevent.id; + if(cevent.enabled) + { + //see if this event is before our last nearest + if(EQTime::IsTimeBefore(&next_event, &cevent.next)) + { + memcpy(&next_event, &cevent.next, sizeof(next_event)); + next_id = cevent.id; + } } } if(next_id == -1) @@ -1035,7 +1064,7 @@ void SpawnConditionManager::ReloadEvent(uint32 event_id) { FindNearestEvent(); } -void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool reset_base) { +void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool strict, bool reset_base) { _log(SPAWNS__CONDITIONS, "Request to %s spawn event %d %sresetting trigger time", enabled?"enable":"disable", event_id, reset_base?"":"without "); @@ -1048,8 +1077,9 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool rese if(cevent.id == event_id) { //make sure were actually changing something - if(cevent.enabled != enabled || reset_base) { + if(cevent.enabled != enabled || reset_base || cevent.strict != strict) { cevent.enabled = enabled; + cevent.strict = strict; if(reset_base) { _log(SPAWNS__CONDITIONS, "Spawn event %d located in this zone. State set. Trigger time reset (period %d).", event_id, cevent.period); //start with the time now diff --git a/zone/spawn2.h b/zone/spawn2.h index 42c2153f2..e529bb65e 100644 --- a/zone/spawn2.h +++ b/zone/spawn2.h @@ -67,6 +67,7 @@ public: bool NPCPointerValid() { return (npcthis!=nullptr); } void SetNPCPointer(NPC* n) { npcthis = n; } + void SetNPCPointerNull() { npcthis = nullptr; } void SetTimer(uint32 duration) { timer.Start(duration); } uint32 GetKillCount() { return killcount; } protected: @@ -134,6 +135,7 @@ public: bool enabled; Action action; int16 argument; + bool strict; uint32 period; //eq minutes (3 seconds) between events TimeOfDay_Struct next; //next time this event triggers @@ -148,7 +150,7 @@ public: int16 GetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id); void SetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id, int16 new_value, bool world_update = false); - void ToggleEvent(uint32 event_id, bool enabled, bool reset_base); + void ToggleEvent(uint32 event_id, bool enabled, bool strict, bool reset_base); bool Check(uint16 condition, int16 min_value); void ReloadEvent(uint32 event_id); From 5e81848445d36d62f21d2d24c9a1edcf2836a09f Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 25 Apr 2014 18:45:19 -0700 Subject: [PATCH 82/82] Darwin caused ipc mutex to stop working for non-darwin, non-windows systems. Hopefully fixed --- common/ipc_mutex.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/common/ipc_mutex.cpp b/common/ipc_mutex.cpp index 331144eca..c05fb072c 100644 --- a/common/ipc_mutex.cpp +++ b/common/ipc_mutex.cpp @@ -55,17 +55,22 @@ namespace EQEmu { std::string final_name = name; final_name += ".lock"; -#ifdef __DARWIN +#ifdef __DARWIN #if __DARWIN_C_LEVEL < 200809L imp_->fd_ = open(final_name.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); #else - imp_->fd_ = open(final_name.c_str(), - O_RDWR | O_CREAT | O_CLOEXEC, - S_IRUSR | S_IWUSR); + imp_->fd_ = open(final_name.c_str(), + O_RDWR | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR); #endif +#else + imp_->fd_ = open(final_name.c_str(), + O_RDWR | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR); #endif + if(imp_->fd_ == -1) { EQ_EXCEPT("IPC Mutex", "Could not create mutex."); }