From 0caa1fd40ba646a5c10ab10520eeace52e2b1e8b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 20 Feb 2014 01:04:34 -0500 Subject: [PATCH 01/91] new spell effects buff struct/tables - required sql Removed: death_save_chance and deathsave_aa_chance (no longer used) Added: dot_rune, caston_x, caston_y, caston_z minor fixes --- common/spdat.cpp | 3 +- common/spdat.h | 104 +++++++++--------- .../git/required/2014_02_20_buff_update.txt | 16 +++ zone/attack.cpp | 26 ++++- zone/bonuses.cpp | 30 ++++- zone/bot.cpp | 29 ++--- zone/command.cpp | 3 - zone/common.h | 8 +- zone/spell_effects.cpp | 88 +++++++++------ zone/spells.cpp | 6 +- zone/zonedb.cpp | 35 +++--- 11 files changed, 215 insertions(+), 133 deletions(-) create mode 100644 utils/sql/git/required/2014_02_20_buff_update.txt diff --git a/common/spdat.cpp b/common/spdat.cpp index 807a1ba2d..04af27424 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -391,8 +391,7 @@ bool IsPartialCapableSpell(uint16 spell_id) return false; if (IsPureNukeSpell(spell_id) || IsFearSpell(spell_id) || - IsEffectInSpell(spell_id, SE_Root) || - IsEffectInSpell(spell_id, SE_Charm)) + IsEffectInSpell(spell_id, SE_Root)) return true; return false; diff --git a/common/spdat.h b/common/spdat.h index 237125b90..70f4c4b24 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -293,7 +293,7 @@ typedef enum { #define SE_LimitCastTimeMin 143 // implemented #define SE_LimitCastTimeMax 144 // implemented (*not used in any known live spell) #define SE_Teleport2 145 // implemented - Banishment of the Pantheon -#define SE_ElectricityResist 146 // *not implemented (Lightning Rod: 23233) +//#define SE_ElectricityResist 146 // *not implemented (Lightning Rod: 23233) #define SE_PercentalHeal 147 // implemented #define SE_StackingCommand_Block 148 // implemented? #define SE_StackingCommand_Overwrite 149 // implemented? @@ -349,73 +349,73 @@ typedef enum { #define SE_Taunt 199 // implemented - % chance to taunt the target #define SE_ProcChance 200 // implemented #define SE_RangedProc 201 // implemented -#define SE_IllusionOther 202 // *not implemented as bonus(Project Illusion) -#define SE_MassGroupBuff 203 // *not implemented as bonus +//#define SE_IllusionOther 202 // *not implemented as bonus(Project Illusion) +//#define SE_MassGroupBuff 203 // *not implemented as bonus #define SE_GroupFearImmunity 204 // *not implemented as bonus #define SE_Rampage 205 // implemented #define SE_AETaunt 206 // implemented #define SE_FleshToBone 207 // implemented //#define SE_PurgePoison 208 // not used #define SE_DispelBeneficial 209 // implemented -#define SE_PetShield 210 // *not implemented +//#define SE_PetShield 210 // *not implemented #define SE_AEMelee 211 // implemented -#define SE_CastingSkills 212 // *not implemented -Include/Exclude Casting Skill type. (*no longer used on live) +//#define SE_CastingSkills 212 // *not implemented -Include/Exclude Casting Skill type. (*no longer used on live) #define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet #define SE_MaxHPChange 214 // implemented #define SE_PetAvoidance 215 // implemented[AA] - increases pet ability to avoid melee damage #define SE_Accuracy 216 // implemented -#define SE_HeadShot 217 // not implemented as bonus - ability to head shot (base2 = damage) +//#define SE_HeadShot 217 // not implemented as bonus - ability to head shot (base2 = damage) #define SE_PetCriticalHit 218 // implemented[AA] - gives pets a baseline critical hit chance #define SE_SlayUndead 219 // implemented - Allow extra damage against undead (base1 = rate, base2 = damage mod). #define SE_SkillDamageAmount 220 // implemented -#define SE_Packrat 221 // not implemented as bonus +#define SE_Packrat 221 // implemented as bonus #define SE_BlockBehind 222 // implemented - Chance to block from behind (with our without Shield) //#define SE_DoubleRiposte 223 // not used #define SE_GiveDoubleRiposte 224 // implemented[AA] #define SE_GiveDoubleAttack 225 // implemented[AA] - Allow any class to double attack with set chance. -#define SE_TwoHandBash 226 // not implemented as bonus +#define SE_TwoHandBash 226 // *not implemented as bonus #define SE_ReduceSkillTimer 227 // implemented #define SE_ReduceFallDamage 228 // not implented as bonus - reduce the damage that you take from falling #define SE_PersistantCasting 229 // implemented -#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability +//#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability #define SE_StunBashChance 231 // implemented - increase chance to stun from bash. #define SE_DivineSave 232 // implemented (base1 == % chance on death to insta-res) (base2 == spell cast on save) -#define SE_Metabolism 233 // *not implemented - (Crown of Feathers) Increase metabolism? -#define SE_ReduceApplyPoisonTime 234 // not implemented as bonus - reduces the time to apply poison +//#define SE_Metabolism 233 // *not implemented - (Crown of Feathers) Increase metabolism? +//#define SE_ReduceApplyPoisonTime 234 // not implemented as bonus - reduces the time to apply poison #define SE_ChannelChanceSpells 235 // implemented[AA] - chance to channel from SPELLS *No longer used on live. //#define SE_FreePet 236 // not used #define SE_GivePetGroupTarget 237 // implemented[AA] - (Pet Affinity) -#define SE_IllusionPersistence 238 // *not implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed. -#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you. +//#define SE_IllusionPersistence 238 // *not implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed. +//#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you. //#define SE_StringUnbreakable 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.] #define SE_ImprovedReclaimEnergy 241 // not implemented as bonus - increase the amount of mana returned to you when reclaiming your pet. #define SE_IncreaseChanceMemwipe 242 // implemented - increases the chance to wipe hate with memory blurr #define SE_CharmBreakChance 243 // implemented - Total Domination #define SE_RootBreakChance 244 // implemented[AA] reduce the chance that your root will break. -#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest -#define SE_SetBreathLevel 246 // not implemented as bonus +//#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest +#define SE_SetBreathLevel 246 // *not implemented as bonus #define SE_RaiseSkillCap 247 // *not implemented[AA] - adds skill over the skill cap. -#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100) +//#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100) #define SE_SecondaryDmgInc 249 // implemented[AA] Allows off hand weapon to recieve a damage bonus (Sinister Strikes) #define SE_SpellProcChance 250 // implemented - Increase chance to sympathetic proc by % #define SE_ConsumeProjectile 251 // implemented[AA] - chance to not consume an arrow (ConsumeProjectile = 100) #define SE_FrontalBackstabChance 252 // implemented[AA] - chance to perform a full damage backstab from front. #define SE_FrontalBackstabMinDmg 253 // implemented[AA] - allow a frontal backstab for mininum damage. #define SE_Blank 254 // implemented -#define SE_ShieldDuration 255 // not implemented as bonus - increases duration of /shield -#define SE_ShroudofStealth 256 // not implemented as bonus - rogue improved invs -#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold +//#define SE_ShieldDuration 255 // not implemented as bonus - increases duration of /shield +//#define SE_ShroudofStealth 256 // not implemented as bonus - rogue improved invs +//#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold #define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab #define SE_CombatStability 259 // implemented[AA] - damage mitigation #define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType #define SE_SongModCap 261 // implemented[AA] - Song Mod cap increase (no longer used on live) #define SE_RaiseStatCap 262 // implemented #define SE_TradeSkillMastery 263 // implemented - lets you raise more than one tradeskill above master. -#define SE_HastenedAASkill 264 // not implemented as bonus - Use redux field in aa_actions table for this effect +//#define SE_HastenedAASkill 264 // not implemented as bonus - Use redux field in aa_actions table for this effect #define SE_MasteryofPast 265 // implemented[AA] - Spells less than effect values level can not be fizzled #define SE_ExtraAttackChance 266 // implemented - increase chance to score an extra attack with a 2-Handed Weapon. #define SE_PetDiscipline2 267 // *not implemented - /pet focus, /pet no cast -#define SE_ReduceTradeskillFail 268 // *not implemented? - reduces chance to fail with given tradeskill by a percent chance +//#define SE_ReduceTradeskillFail 268 // *not implemented? - reduces chance to fail with given tradeskill by a percent chance #define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound. #define SE_BardSongRange 270 // implemented[AA] - increase range of beneficial bard songs (Sionachie's Crescendo) #define SE_BaseMovementSpeed 271 // implemented[AA] - mods basemove speed, doesn't stack with other move mods @@ -428,11 +428,11 @@ typedef enum { #define SE_FinishingBlow 278 // implemented[AA] - chance to do massive damage under 10% HP (base1 = chance, base2 = damage) #define SE_Flurry 279 // implemented #define SE_PetFlurry 280 // implemented[AA] -#define SE_FeignedMinion 281 // *not implemented[AA] ability allows you to instruct your pet to feign death via the '/pet feign' command. value = succeed chance +//#define SE_FeignedMinion 281 // *not implemented[AA] ability allows you to instruct your pet to feign death via the '/pet feign' command. value = succeed chance #define SE_ImprovedBindWound 282 // implemented[AA] - increase bind wound amount by percent. #define SE_DoubleSpecialAttack 283 // implemented[AA] - Chance to perform second special attack as monk //#define SE_LoHSetHeal 284 // not used -#define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max +//#define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max #define SE_FcDamageAmt 286 // implemented - adds direct spell damage #define SE_SpellDurationIncByTic 287 // implemented #define SE_SpecialAttackKBProc 288 // implemented[AA] - Chance to to do a knockback from special attacks [AA Dragon Punch]. @@ -453,13 +453,13 @@ typedef enum { #define SE_FcDamageAmtCrit 303 // implemented - adds direct spell damage #define SE_OffhandRiposteFail 304 // implemented as bonus - enemy cannot riposte offhand attacks #define SE_MitigateDamageShield 305 // implemented - off hand attacks only (Shielding Resistance) -#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds -#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor. +//#define SE_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_ReduceReuseTimer 310 // implemented #define SE_LimitCombatSkills 311 // implemented - Excludes focus from procs (except if proc is a memorizable spell) -#define SE_Sanctuary 312 // *not implemented +//#define SE_Sanctuary 312 // *not implemented #define SE_ForageAdditionalItems 313 // implemented[AA] - chance to forage additional items #define SE_Invisibility2 314 // implemented - fixed duration invisible #define SE_InvisVsUndead2 315 // implemented - fixed duration ITU @@ -472,14 +472,14 @@ typedef enum { #define SE_GateToHomeCity 322 // implemented #define SE_DefensiveProc 323 // implemented #define SE_HPToMana 324 // implemented -#define SE_ChanceInvsBreakToAoE 325 // *not implemented[AA] - [AA Nerves of Steel] increasing chance to remain hidden when they are an indirect target of an AoE spell. -#define SE_SpellSlotIncrease 326 // not implemented as bonus - increases your spell slot availability +//#define SE_ChanceInvsBreakToAoE 325 // *not implemented[AA] - [AA Nerves of Steel] increasing chance to remain hidden when they are an indirect target of an AoE spell. +#define SE_SpellSlotIncrease 326 // *not implemented as bonus - increases your spell slot availability #define SE_MysticalAttune 327 // implemented - increases amount of buffs that a player can have #define SE_DelayDeath 328 // implemented - increases how far you can fall below 0 hp before you die #define SE_ManaAbsorbPercentDamage 329 // implemented #define SE_CriticalDamageMob 330 // implemented -#define SE_Salvage 331 // *not implemented - chance to recover items that would be destroyed in failed tradeskill combine -#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp) +#define SE_Salvage 331 // implemented - chance to recover items that would be destroyed in failed tradeskill combine +//#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp) #define SE_EffectOnFade 333 // implemented #define SE_BardAEDot 334 // implemented #define SE_BlockNextSpellFocus 335 // implemented - base1 chance to block next spell ie Puratus (8494) @@ -492,15 +492,15 @@ typedef enum { #define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing #define SE_InterruptCasting 343 // implemented - % chance to interrupt spells being cast every tic. Cacophony (8272) #define SE_ChannelChanceItems 344 // implemented[AA] - chance to not have ITEM effects interrupted when you take damage. -#define SE_AssassinationLevel 345 // not implemented as bonus - AA Assisination max level to kill -#define SE_HeadShotLevel 346 // not implemented as bonus - AA HeadShot max level to kill +//#define SE_AssassinationLevel 345 // not implemented as bonus - AA Assisination max level to kill +//#define SE_HeadShotLevel 346 // not implemented as bonus - AA HeadShot max level to kill #define SE_DoubleRangedAttack 347 // implemented - chance at an additional archery attack (consumes arrow) #define SE_LimitManaMin 348 // implemented #define SE_ShieldEquipHateMod 349 // implemented[AA] Increase melee hate when wearing a shield. #define SE_ManaBurn 350 // implemented - Drains mana for damage/heal at a defined ratio up to a defined maximum amount of mana. -#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this -#define SE_IncreaseTrapCount 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755) -#define SE_AdditionalAura 353 // *not implemented - allows use of more than 1 aura, aa effect +//#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this +//#define SE_IncreaseTrapCount 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755) +//#define SE_AdditionalAura 353 // *not implemented - allows use of more than 1 aura, aa effect //#define SE_DeactivateAllTraps 354 // *not implemented - looks to be some type of invulnerability? Test DAT (8757) //#define SE_LearnTrap 355 // *not implemented - looks to be some type of invulnerability? Test LT (8758) //#define SE_ChangeTriggerType 356 // not used @@ -509,17 +509,17 @@ typedef enum { #define SE_Invulnerabilty 359 // *not implemented - Invulnerability (Brell's Blessing) #define SE_SpellOnKill 360 // implemented - a buff that has a base1 % to cast spell base2 when you kill a "challenging foe" base3 min level #define SE_SpellOnDeath 361 // implemented - casts spell on death of buffed -#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank. -#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank. +//#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank. +//#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank. #define SE_TripleAttackChance 364 // implemented #define SE_SpellOnKill2 365 // implemented - chance to trigger a spell on kill when the kill is caused by a specific spell with this effect in it (10470 Venin) #define SE_ShieldEquipDmgMod 366 // implemented[AA] Damage modifier to melee if shield equiped. (base1 = dmg mod , base2 = ?) ie Shield Specialist AA #define SE_SetBodyType 367 // implemented - set body type of base1 so it can be affected by spells that are limited to that type (Plant, Animal, Undead, etc) -#define SE_FactionMod 368 // *not implemented - increases faction with base1 (faction id, live won't match up w/ ours) by base2 +//#define SE_FactionMod 368 // *not implemented - increases faction with base1 (faction id, live won't match up w/ ours) by base2 #define SE_CorruptionCounter 369 // implemented #define SE_ResistCorruption 370 // implemented #define SE_AttackSpeed4 371 // implemented - stackable slow effect 'Inhibit Melee' -#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not. +//#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not. #define SE_CastOnWearoff 373 // implemented - Triggers only if fades after natural duration. #define SE_ApplyEffect 374 // implemented #define SE_DotCritDmgIncrease 375 // implemented - Increase damage of DoT critical amount @@ -528,30 +528,30 @@ typedef enum { #define SE_SpellEffectResistChance 378 // implemented - Increase chance to resist specific spell effect (base1=value, base2=spell effect id) #define SE_ShadowStepDirectional 379 // implemented - handled by client #define SE_Knockdown 380 // implemented - small knock back(handled by client) -#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront +//#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront #define SE_NegateSpellEffect 382 // implemented - negates specific spell bonuses for duration of the debuff. #define SE_SympatheticProc 383 // implemented - focus on items that has chance to proc a spell when you cast #define SE_Leap 384 // implemented - Leap effect, ie stomping leap #define SE_LimitSpellGroup 385 // implemented - Limits to spell group(ie type 3 reuse reduction augs that are class specific and thus all share s SG) #define SE_CastOnCurer 386 // implemented - Casts a spell on the person curing #define SE_CastOnCure 387 // implemented - Casts a spell on the cured person -#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA) +//#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA) #define SE_FcTimerRefresh 389 // implemented - Refresh spell icons -#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited. +//#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited. #define SE_LimitManaMax 391 // implemented #define SE_FcHealAmt 392 // implemented - Adds or removes healing from spells #define SE_FcHealPctIncoming 393 // implemented - HealRate with focus restrictions. #define SE_FcHealAmtIncoming 394 // implemented - Adds/Removes amount of healing on target by X value with foucs restrictions. #define SE_FcHealPctCritIncoming 395 // implemented[AA] - Increases chance of having a heal crit when cast on you. [focus limited] #define SE_FcHealAmtCrit 396 // implemented - Adds a direct healing amount to spells -#define SE_PetMeleeMitigation 397 // *not implemented[AA] - additional mitigation to your pets. +//#define SE_PetMeleeMitigation 397 // *not implemented[AA] - additional mitigation to your pets. #define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets #define SE_FcTwincast 399 // implemented - cast 2 spells for every 1 #define SE_HealGroupFromMana 400 // implemented - Drains mana and heals for each point of mana drained #define SE_ManaDrainWithDmg 401 // implemented - Deals damage based on the amount of mana drained #define SE_EndDrainWithDmg 402 // implemented - Deals damage for the amount of endurance drained -#define SE_LimitSpellClass 403 // *not implemented - unclear what this refers too (not 'right click' spell bar) -#define SE_LimitSpellSubclass 404 // *not implemented - unclear what this refers too (not 'right click' spell bar) +//#define SE_LimitSpellClass 403 // *not implemented - unclear what this refers too (not 'right click' spell bar) +//#define SE_LimitSpellSubclass 404 // *not implemented - unclear what this refers too (not 'right click' spell bar) #define SE_TwoHandBluntBlock 405 // implemented - chance to block attacks when using two hand blunt weapons (similiar to shield block) #define SE_CastonNumHitFade 406 // implemented - casts a spell when a buff fades due to its numhits being depleted #define SE_CastonFocusEffect 407 // implemented - casts a spell if focus limits are met (ie triggers when a focus effects is applied) @@ -573,7 +573,7 @@ typedef enum { #define SE_LimitUseType 423 // implemented - limit a focus to require a certain numhits type #define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace //#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626) -#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window +//#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window #define SE_SkillProc 427 // implemented - chance to proc when using a skill(ie taunt) #define SE_LimitToSkill 428 // implemented - limits what skills will effect a skill proc #define SE_SkillProc2 429 // implemented - chance to proc when using a skill (most have hit limits) @@ -584,20 +584,20 @@ typedef enum { #define SE_CriticalHealDecay 434 // implemented - increase critical heal chance, effect decays based on level of spell it effects. #define SE_CriticalRegenDecay 435 // implemented - increase critical heal over time chance, effect decays based on level of spell it effects. //#define SE_BeneficialCountDownHold 436 // not used ( 23491 | ABTest Buff Hold) -#define SE_TeleporttoAnchor 437 // *not implemented - Teleport Guild Hall Anchor(33099) -#define SE_TranslocatetoAnchor 438 // *not implemented - Translocate Primary Anchor (27750) -#define SE_IncreaseAssassinationLvl 439 // *not implemented[AA] - increases the maximum level of humanoid that can be affected by assassination +//#define SE_TeleporttoAnchor 437 // *not implemented - Teleport Guild Hall Anchor(33099) +//#define SE_TranslocatetoAnchor 438 // *not implemented - Translocate Primary Anchor (27750) +//#define SE_IncreaseAssassinationLvl 439 // *not implemented[AA] - increases the maximum level of humanoid that can be affected by assassination #define SE_FinishingBlowLvl 440 // implemented[AA] - Sets the level Finishing blow can be triggered on an NPC -#define SE_CancleIfMoved 441 // *not implemented - Buff is removed from target when target moves X amount of distance away from where initially hit. +#define SE_DistanceRemoval 441 // implemented - Buff is removed from target when target moves X amount of distance away from where initially hit. #define SE_TriggerOnReqTarget 442 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) #define SE_TriggerOnReqCaster 443 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) #define SE_ImprovedTaunt 444 // implemented - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y -#define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs. +//#define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs. //#define SE_AStacker 446 // *not implementet - bufff stacking blocker ? (26219 | Qirik's Watch) //#define SE_BStacker 447 // *not implemented //#define SE_CStacker 448 // *not implemented //#define SE_DStacker 449 // *not implemented -//#define SE_DotGuard 450 // *not implemented +#define SE_MitigateDotDamage 450 // implemented DOT spell mitigation rune with max value #define SE_MeleeThresholdGuard 451 // implemented Partial Melee Rune that only is lowered if melee hits are over X amount of damage #define SE_SpellThresholdGuard 452 // implemented Partial Spell Rune that only is lowered if spell hits are over X amount of damage #define SE_TriggerMeleeThreshold 453 // implemented Trigger effect on X amount of melee damage taken diff --git a/utils/sql/git/required/2014_02_20_buff_update.txt b/utils/sql/git/required/2014_02_20_buff_update.txt new file mode 100644 index 000000000..e691a0a67 --- /dev/null +++ b/utils/sql/git/required/2014_02_20_buff_update.txt @@ -0,0 +1,16 @@ +-- UPDATE BUFF TABLES +ALTER TABLE `character_buffs` CHANGE `death_save_chance` `dot_rune` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `merc_buffs` CHANGE `DeathSaveSuccessChance` `dot_rune` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `botbuffs` CHANGE `DeathSaveSuccessChance` `dot_rune` INT(10) NOT NULL DEFAULT '0'; + +ALTER TABLE `character_buffs` CHANGE `death_save_aa_chance` `caston_x` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `merc_buffs` CHANGE `CasterAARank` `caston_x` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `botbuffs` CHANGE `CasterAARank` `caston_x` INT(10) NOT NULL DEFAULT '0'; + +ALTER TABLE `character_buffs` ADD `caston_y` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `merc_buffs` ADD `caston_y` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `botbuffs` ADD `caston_y` INT(10) NOT NULL DEFAULT '0'; + +ALTER TABLE `character_buffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `merc_buffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `botbuffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/zone/attack.cpp b/zone/attack.cpp index 73ae7053f..4467d6320 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3273,8 +3273,28 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi } // If this is a DoT, use DoT Shielding... - if(iBuffTic) - damage -= (damage * itembonuses.DoTShielding / 100); + if(iBuffTic) { + damage -= (damage * itembonuses.DoTShielding / 100); + + if (spellbonuses.MitigateDotRune[0]){ + slot = spellbonuses.MitigateDotRune[1]; + if(slot >= 0) + { + int damage_to_reduce = damage * spellbonuses.MitigateDotRune[0] / 100; + if(damage_to_reduce > buffs[slot].dot_rune) + { + damage -= damage_to_reduce; + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + } + else + { + buffs[slot].dot_rune = (buffs[slot].dot_rune - damage_to_reduce); + damage -= damage_to_reduce; + } + } + } + } // This must be a DD then so lets apply Spell Shielding and runes. else @@ -3342,7 +3362,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi BuffFadeBySlot(slot); } else{ - buffs[slot].melee_rune = (buffs[slot].magic_rune - damage); + buffs[slot].magic_rune = (buffs[slot].magic_rune - damage); } } } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index dc8776c67..f97bcd34b 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2239,6 +2239,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } break; } + + case SE_MitigateDotDamage: + { + if (newbon->MitigateDotRune[0] < effect_value){ + newbon->MitigateDotRune[0] = effect_value; + newbon->MitigateDotRune[1] = buffslot; + } + break; + } case SE_ManaAbsorbPercentDamage: { @@ -2513,6 +2522,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } break; + + case SE_DistanceRemoval: + newbon->DistanceRemoval = true; + break; + } } } @@ -3558,6 +3572,11 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) spellbonuses.MitigateSpellRune[1] = -1; break; + case SE_MitigateDotDamage: + spellbonuses.MitigateDotRune[0] = effect_value; + spellbonuses.MitigateDotRune[1] = -1; + break; + case SE_ManaAbsorbPercentDamage: spellbonuses.ManaAbsorbPercentDamage[0] = effect_value; spellbonuses.ManaAbsorbPercentDamage[1] = -1; @@ -3841,7 +3860,16 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.CriticalMend = effect_value; aabonuses.CriticalMend = effect_value; break; - + + case SE_DistanceRemoval: + spellbonuses.DistanceRemoval = effect_value; + break; + + case SE_ImprovedTaunt: + spellbonuses.ImprovedTaunt[0] = effect_value; + spellbonuses.ImprovedTaunt[1] = effect_value; + spellbonuses.ImprovedTaunt[2] = -1; + } } } diff --git a/zone/bot.cpp b/zone/bot.cpp index 3805dd025..9054df6a5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2476,7 +2476,7 @@ void Bot::SaveBuffs() { if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, " "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, " - "DeathSaveSuccessChance, CasterAARank, Persistent) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);", + "dot_rune, caston_x, Persistent, caston_y, caston_z) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);", GetBotID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula, buffs[BuffCount].ticsremaining, CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, @@ -2484,8 +2484,8 @@ void Bot::SaveBuffs() { CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune, - buffs[BuffCount].deathSaveSuccessChance, - buffs[BuffCount].deathsaveCasterAARank, IsPersistent), TempErrorMessageBuffer)) { + buffs[BuffCount].dot_rune, + buffs[BuffCount].caston_x, IsPersistent, buffs[BuffCount].caston_y,buffs[BuffCount].caston_z), TempErrorMessageBuffer)) { errorMessage = std::string(TempErrorMessageBuffer); safe_delete(Query); Query = 0; @@ -2515,7 +2515,7 @@ void Bot::LoadBuffs() { bool BuffsLoaded = false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, DeathSaveSuccessChance, CasterAARank, Persistent FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { errorMessage = std::string(TempErrorMessageBuffer); } else { @@ -2541,8 +2541,8 @@ void Bot::LoadBuffs() { buffs[BuffCount].numhits = atoi(DataRow[8]); buffs[BuffCount].melee_rune = atoi(DataRow[9]); buffs[BuffCount].magic_rune = atoi(DataRow[10]); - buffs[BuffCount].deathSaveSuccessChance = atoi(DataRow[11]); - buffs[BuffCount].deathsaveCasterAARank = atoi(DataRow[12]); + buffs[BuffCount].dot_rune = atoi(DataRow[11]); + buffs[BuffCount].caston_x = atoi(DataRow[12]); buffs[BuffCount].casterid = 0; bool IsPersistent = false; @@ -2550,6 +2550,9 @@ void Bot::LoadBuffs() { if(atoi(DataRow[13])) IsPersistent = true; + buffs[BuffCount].caston_x = atoi(DataRow[14]); + buffs[BuffCount].caston_x = atoi(DataRow[15]); + buffs[BuffCount].persistant_buff = IsPersistent; BuffCount++; @@ -6818,13 +6821,6 @@ int16 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) SpellSkill_Found = true; break; - case SE_LimitSpellSubclass:{ - int16 spell_skill = spell.skill * -1; - if(base1 == spell_skill) - LimitFound = true; - break; - } - case SE_LimitClass: //Do not use this limit more then once per spell. If multiple class, treat value like items would. if (!PassLimitClass(base1, GetClass())) @@ -7423,13 +7419,6 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel SpellSkill_Found = true; break; - case SE_LimitSpellSubclass:{ - int16 spell_skill = spell.skill * -1; - if(focus_spell.base[i] == spell_skill) - return 0; - break; - } - case SE_LimitClass: //Do not use this limit more then once per spell. If multiple class, treat value like items would. if (!PassLimitClass(focus_spell.base[i], GetClass())) diff --git a/zone/command.cpp b/zone/command.cpp index 329584140..1b8ce3365 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -11047,9 +11047,6 @@ void command_showbonusstats(Client *c, const Seperator *sep) c->Message(0, " Target Spell Bonuses:"); c->Message(0, " Accuracy: %i%% Divine Save: %i%%",c->GetTarget()->GetSpellBonuses().Accuracy, c->GetTarget()->GetSpellBonuses().DivineSaveChance); c->Message(0, " Flurry: %i%% HitChance: %i%% ",c->GetTarget()->GetSpellBonuses().FlurryChance, c->GetTarget()->GetSpellBonuses().HitChance / 15); - int deathsaveslot = c->GetTarget()->GetBuffSlotFromType(SE_DeathSave); - int dschance = deathsaveslot >= 0 ? c->GetTarget()->GetBuffs()[deathsaveslot].deathSaveSuccessChance : 0; - c->Message(0, " Death Save: %i%%",dschance); } c->Message(0, " Effective Casting Level: %i",c->GetTarget()->GetCasterLevel(0)); } diff --git a/zone/common.h b/zone/common.h index 622dafb6f..97b94887a 100644 --- a/zone/common.h +++ b/zone/common.h @@ -155,8 +155,10 @@ struct Buffs_Struct { uint32 numhits; //the number of physical hits this buff can take before it fades away, lots of druid armor spells take advantage of this mixed with powerful effects uint32 melee_rune; uint32 magic_rune; - uint8 deathSaveSuccessChance; - uint8 deathsaveCasterAARank; + uint32 dot_rune; + uint32 caston_x; + uint32 caston_y; + uint32 caston_z; bool persistant_buff; bool client; //True if the caster is a client bool UpdateClient; @@ -319,6 +321,7 @@ struct StatBonuses { uint16 MeleeThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. uint16 SpellThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. uint16 MitigateSpellRune[2]; // 0 = Mitigation value 1 = Buff Slot + uint16 MitigateDotRune[2]; // 0 = Mitigation value 1 = Buff Slot uint32 TriggerMeleeThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger uint32 TriggerSpellThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger uint16 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot @@ -328,6 +331,7 @@ struct StatBonuses { bool CriticalHealDecay; // increase critical heal chance, decays based on spell level cast bool CriticalDotDecay; // increase critical dot chance, decays based on spell level cast bool DivineAura; // invulnerability + bool DistanceRemoval; // Check if Cancle if Moved effect is present int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid //bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect //bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 75c83cb3e..f307e08e2 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1284,6 +1284,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } + case SE_MitigateDotDamage: + { + buffs[buffslot].dot_rune = spells[spell_id].max[i]; + break; + } + case SE_TriggerMeleeThreshold: { buffs[buffslot].melee_rune = spells[spell_id].base2[i]; @@ -1296,7 +1302,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - + case SE_DistanceRemoval: + { + buffs[buffslot].caston_x = int(GetX()); + buffs[buffslot].caston_y = int(GetY()); + buffs[buffslot].caston_z = int(GetZ()); + break; + } + case SE_Levitate: { #ifdef SPELL_EFFECT_SPAM @@ -2361,30 +2374,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - case SE_DeathSave: { -#ifdef SPELL_EFFECT_SPAM - snprintf(effect_desc, _EDLEN, "Death Save: %+i", effect_value); -#endif - uint8 BonusChance = 0; - if(caster) { - - BonusChance = caster->aabonuses.UnfailingDivinity + - caster->itembonuses.UnfailingDivinity + - caster->spellbonuses.UnfailingDivinity; - } - -#ifdef SPELL_EFFECT_SPAM - //snprintf(effect_desc, _EDLEN, "Death Save Chance: %+i", SuccessChance); -#endif - //buffs[buffslot].deathSaveSuccessChance = SuccessChance; - //buffs[buffslot].deathsaveCasterAARank = caster->GetAA(aaUnfailingDivinity); - buffs[buffslot].deathsaveCasterAARank = BonusChance; - //SetDeathSaveChance(true); - - - break; - } - case SE_SummonAndResAllCorpses: { if(IsClient()) @@ -2763,7 +2752,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_MitigateDamageShield: case SE_FcBaseEffects: case SE_LimitClass: - case SE_LimitSpellSubclass: case SE_BlockBehind: case SE_ShieldBlock: case SE_PetCriticalHit: @@ -2816,6 +2804,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_IncreaseChanceMemwipe: case SE_CriticalMend: case SE_LimitCastTimeMax: + case SE_DeathSave: + case SE_TriggerOnReqCaster: { break; } @@ -3378,6 +3368,26 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste } break; } + + case SE_DistanceRemoval: + { + if (spellbonuses.DistanceRemoval){ + + int distance = sqrt( + ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) + + ((int(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) + + ((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z)) + ); + + if (distance > spells[spell_id].base[i]){ + + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot , true); + } + break; + } + } + default: { // do we need to do anyting here? @@ -5249,9 +5259,14 @@ bool Mob::TryDeathSave() { int SuccessChance = 0; int buffSlot = spellbonuses.DeathSave[1]; - uint8 UD_HealMod = buffs[buffSlot].deathsaveCasterAARank; //Contains value of UD heal modifier. + int16 UD_HealMod = 0; uint32 HealAmt = 300; //Death Pact max Heal + Mob* caster = entity_list.GetMobID(buffs[buffSlot].casterid); + + if (caster) + UD_HealMod = caster->spellbonuses.UnfailingDivinity + caster->itembonuses.UnfailingDivinity + caster->aabonuses.UnfailingDivinity; + if(buffSlot >= 0){ SuccessChance = ( (GetCHA() * (RuleI(Spells, DeathSaveCharismaMod))) + 1) / 10; //(CHA Mod Default = 3) @@ -5580,19 +5595,24 @@ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){ bool Mob::ImprovedTaunt(){ - if (spellbonuses.ImprovedTaunt[2]){ + if (spellbonuses.ImprovedTaunt[0]){ if (GetLevel() > spellbonuses.ImprovedTaunt[0]) return false; - target = entity_list.GetMob(buffs[spellbonuses.ImprovedTaunt[2]].casterid); + if (spellbonuses.ImprovedTaunt[2] >= 0){ - if (target){ - SetTarget(target); - return true; + target = entity_list.GetMob(buffs[spellbonuses.ImprovedTaunt[2]].casterid); + + if (target){ + SetTarget(target); + return true; + } + else { + if(!TryFadeEffect(spellbonuses.ImprovedTaunt[2])) + BuffFadeBySlot(spellbonuses.ImprovedTaunt[2], true); //If caster killed removed effect. + } } - else - BuffFadeBySlot(spellbonuses.ImprovedTaunt[2]); //If caster killed removed effect. } return false; diff --git a/zone/spells.cpp b/zone/spells.cpp index ca3fef4fa..de16af190 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2953,8 +2953,10 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].numhits = spells[spell_id].numhits; buffs[emptyslot].client = caster ? caster->IsClient() : 0; buffs[emptyslot].persistant_buff = 0; - buffs[emptyslot].deathsaveCasterAARank = 0; - buffs[emptyslot].deathSaveSuccessChance = 0; + buffs[emptyslot].caston_x = 0; + buffs[emptyslot].caston_y = 0; + buffs[emptyslot].caston_z = 0; + buffs[emptyslot].dot_rune = 0; if (level_override > 0) { buffs[emptyslot].UpdateClient = true; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 480cd80cb..aa68888fd 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1815,7 +1815,7 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) { if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO merc_buffs (MercId, SpellId, CasterLevel, DurationFormula, " "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, " - "DeathSaveSuccessChance, CasterAARank, Persistent) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);", + "dot_rune, caston_x, Persistent, caston_y, caston_z) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);", merc->GetMercID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula, buffs[BuffCount].ticsremaining, CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, @@ -1823,8 +1823,8 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) { CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune, - buffs[BuffCount].deathSaveSuccessChance, - buffs[BuffCount].deathsaveCasterAARank, IsPersistent), TempErrorMessageBuffer)) { + buffs[BuffCount].dot_rune, + buffs[BuffCount].caston_x, IsPersistent, buffs[BuffCount].caston_y, buffs[BuffCount].caston_z), TempErrorMessageBuffer)) { errorMessage = std::string(TempErrorMessageBuffer); safe_delete(Query); Query = 0; @@ -1856,7 +1856,7 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) { bool BuffsLoaded = false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, DeathSaveSuccessChance, CasterAARank, Persistent FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) { + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, CasterAARank, Persistent FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) { errorMessage = std::string(TempErrorMessageBuffer); } else { @@ -1882,8 +1882,8 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) { buffs[BuffCount].numhits = atoi(DataRow[8]); buffs[BuffCount].melee_rune = atoi(DataRow[9]); buffs[BuffCount].magic_rune = atoi(DataRow[10]); - buffs[BuffCount].deathSaveSuccessChance = atoi(DataRow[11]); - buffs[BuffCount].deathsaveCasterAARank = atoi(DataRow[12]); + buffs[BuffCount].dot_rune = atoi(DataRow[11]); + buffs[BuffCount].caston_x = atoi(DataRow[12]); buffs[BuffCount].casterid = 0; bool IsPersistent = false; @@ -1891,6 +1891,9 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) { if(atoi(DataRow[13])) IsPersistent = true; + buffs[BuffCount].caston_y = atoi(DataRow[13]); + buffs[BuffCount].caston_z = atoi(DataRow[14]); + buffs[BuffCount].persistant_buff = IsPersistent; BuffCount++; @@ -2566,11 +2569,11 @@ void ZoneDatabase::SaveBuffs(Client *c) { for (int i = 0; i < buff_count; i++) { if(buffs[i].spellid != SPELL_UNKNOWN) { if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `character_buffs` (character_id, slot_id, spell_id, " - "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, magic_rune, persistent, death_save_chance, " - "death_save_aa_chance) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", + "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, magic_rune, persistent, dot_rune, " + "caston_x, caston_y, caston_z) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", c->CharacterID(), i, buffs[i].spellid, buffs[i].casterlevel, buffs[i].caster_name, buffs[i].ticsremaining, buffs[i].counters, buffs[i].numhits, buffs[i].melee_rune, buffs[i].magic_rune, buffs[i].persistant_buff, - buffs[i].deathSaveSuccessChance, buffs[i].deathsaveCasterAARank), + buffs[i].dot_rune, buffs[i].caston_x, buffs[i].caston_y, buffs[i].caston_z), errbuf)) { LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query, errbuf); } @@ -2592,7 +2595,7 @@ void ZoneDatabase::LoadBuffs(Client *c) { MYSQL_RES *result; MYSQL_ROW row; if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, counters, " - "numhits, melee_rune, magic_rune, persistent, death_save_chance, death_save_aa_chance FROM `character_buffs` WHERE " + "numhits, melee_rune, magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z FROM `character_buffs` WHERE " "`character_id`='%u'", c->CharacterID()), errbuf, &result)) { @@ -2617,8 +2620,10 @@ void ZoneDatabase::LoadBuffs(Client *c) { uint32 melee_rune = atoul(row[7]); uint32 magic_rune = atoul(row[8]); uint8 persistent = atoul(row[9]); - uint32 death_save_chance = atoul(row[10]); - uint32 death_save_aa_chance = atoul(row[11]); + uint32 dot_rune = atoul(row[10]); + uint32 caston_x = atoul(row[11]); + uint32 caston_y = atoul(row[12]); + uint32 caston_z = atoul(row[13]); buffs[slot_id].spellid = spell_id; buffs[slot_id].casterlevel = caster_level; @@ -2638,8 +2643,10 @@ void ZoneDatabase::LoadBuffs(Client *c) { buffs[slot_id].melee_rune = melee_rune; buffs[slot_id].magic_rune = magic_rune; buffs[slot_id].persistant_buff = persistent ? true : false; - buffs[slot_id].deathSaveSuccessChance = death_save_chance; - buffs[slot_id].deathsaveCasterAARank = death_save_aa_chance; + buffs[slot_id].dot_rune = dot_rune; + buffs[slot_id].caston_x = caston_x; + buffs[slot_id].caston_y = caston_y; + buffs[slot_id].caston_z = caston_z; buffs[slot_id].UpdateClient = false; if(IsRuneSpell(spell_id)) { c->SetHasRune(true); From 402a10c4881e88212bd7fdddb4ea6da635f3f3a1 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 20 Feb 2014 01:05:44 -0500 Subject: [PATCH 02/91] change log --- changelog.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog.txt b/changelog.txt index eb1785b41..7ad5f7ae6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/20/2014 == +Kayen: Implemented SE_MitigateDotDamage - dot spell mitigation rune with max value +Kayen: Implemented SE_DistanceRemoval - removed from target when target moves X amount of distance away from where initially hit. + +Required SQL: utils/sql/git/2014_02_20_buff_updates.sql + == 02/18/2014 == Kayen: Implemented SE_TriggerOnReqCaster - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) Kayen: Implemented SE_ImprovedTaunt - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y From 026f019f58bcd5129e2fc9f3c151b33a038d0b2b Mon Sep 17 00:00:00 2001 From: Michael Cook Date: Fri, 21 Feb 2014 22:57:15 -0500 Subject: [PATCH 03/91] Changed SE_SingingSkill to SE_Amplificatoin This effect will now stack with other singing spell/item mods like it does on live. --- common/spdat.h | 2 +- zone/bonuses.cpp | 15 ++++++--------- zone/client_mods.cpp | 2 +- zone/common.h | 1 + zone/spell_effects.cpp | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 237125b90..9a6c1b1ea 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -265,7 +265,7 @@ typedef enum { #define SE_Hunger 115 // implemented - Song of Sustenance #define SE_CurseCounter 116 // implemented #define SE_MagicWeapon 117 // implemented - makes weapon magical -#define SE_SingingSkill 118 // *implemented - needs AA conversion +#define SE_Amplification 118 // implemented - Harmonize/Amplification (stacks with other singing mods) #define SE_AttackSpeed3 119 // implemented #define SE_HealRate 120 // implemented - reduces healing by a % #define SE_ReverseDS 121 // implemented diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index dc8776c67..c92de1034 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1607,12 +1607,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->reflect_chance += effect_value; break; - case SE_SingingSkill: - { - if(effect_value > newbon->singingMod) - newbon->singingMod = effect_value; + case SE_Amplification: + newbon->Amplification += effect_value; break; - } case SE_ChangeAggro: newbon->hatemod += effect_value; @@ -3124,10 +3121,10 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.reflect_chance = effect_value; break; - case SE_SingingSkill: - spellbonuses.singingMod = effect_value; - itembonuses.singingMod = effect_value; - aabonuses.singingMod = effect_value; + case SE_Amplification: + spellbonuses.Amplification = effect_value; + itembonuses.Amplification = effect_value; + aabonuses.Amplification = effect_value; break; case SE_ChangeAggro: diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 925aa15a4..8c29f05eb 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -1857,7 +1857,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const effectmod = itembonuses.singingMod; else effectmod = spellbonuses.singingMod; - effectmod += aabonuses.singingMod; + effectmod += aabonuses.singingMod + spellbonuses.Amplification; break; default: effectmod = 10; diff --git a/zone/common.h b/zone/common.h index 622dafb6f..8ac23b5df 100644 --- a/zone/common.h +++ b/zone/common.h @@ -231,6 +231,7 @@ struct StatBonuses { int effective_casting_level; int reflect_chance; // chance to reflect incoming spell uint16 singingMod; + uint16 Amplification; // stacks with singingMod uint16 brassMod; uint16 percussionMod; uint16 windMod; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 28f806483..03a5d6d2e 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2631,7 +2631,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_MeleeMitigation: case SE_Reflect: case SE_Screech: - case SE_SingingSkill: + case SE_Amplification: case SE_MagicWeapon: case SE_Hunger: case SE_MagnifyVision: From 44c833fbe6666d3b8746fbefbebf31afbe85a1dc Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 22 Feb 2014 01:34:16 -0500 Subject: [PATCH 04/91] fixes Re implemented deathsavechanceAA ect as ExtraDIChance Confirmed on live that this effect persists even if caster not in zone. --- .../git/required/2014_02_20_buff_update.txt | 6 +- zone/bot.cpp | 15 +++-- zone/common.h | 7 ++- zone/spell_effects.cpp | 60 ++++++++++++++++--- zone/spells.cpp | 1 + zone/zonedb.cpp | 30 +++++----- 6 files changed, 88 insertions(+), 31 deletions(-) diff --git a/utils/sql/git/required/2014_02_20_buff_update.txt b/utils/sql/git/required/2014_02_20_buff_update.txt index e691a0a67..a53930481 100644 --- a/utils/sql/git/required/2014_02_20_buff_update.txt +++ b/utils/sql/git/required/2014_02_20_buff_update.txt @@ -13,4 +13,8 @@ ALTER TABLE `botbuffs` ADD `caston_y` INT(10) NOT NULL DEFAULT '0'; ALTER TABLE `character_buffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0'; ALTER TABLE `merc_buffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0'; -ALTER TABLE `botbuffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0'; \ No newline at end of file +ALTER TABLE `botbuffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0'; + +ALTER TABLE `character_buffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `merc_buffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0'; +ALTER TABLE `botbuffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/zone/bot.cpp b/zone/bot.cpp index 9054df6a5..a7ff02f1b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2476,7 +2476,7 @@ void Bot::SaveBuffs() { if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, " "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, " - "dot_rune, caston_x, Persistent, caston_y, caston_z) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);", + "dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", GetBotID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula, buffs[BuffCount].ticsremaining, CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, @@ -2485,7 +2485,11 @@ void Bot::SaveBuffs() { CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune, buffs[BuffCount].dot_rune, - buffs[BuffCount].caston_x, IsPersistent, buffs[BuffCount].caston_y,buffs[BuffCount].caston_z), TempErrorMessageBuffer)) { + buffs[BuffCount].caston_x, + IsPersistent, + buffs[BuffCount].caston_y, + buffs[BuffCount].caston_z, + buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) { errorMessage = std::string(TempErrorMessageBuffer); safe_delete(Query); Query = 0; @@ -2515,7 +2519,7 @@ void Bot::LoadBuffs() { bool BuffsLoaded = false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { errorMessage = std::string(TempErrorMessageBuffer); } else { @@ -2550,8 +2554,9 @@ void Bot::LoadBuffs() { if(atoi(DataRow[13])) IsPersistent = true; - buffs[BuffCount].caston_x = atoi(DataRow[14]); - buffs[BuffCount].caston_x = atoi(DataRow[15]); + buffs[BuffCount].caston_y = atoi(DataRow[14]); + buffs[BuffCount].caston_z = atoi(DataRow[15]); + buffs[BuffCount].ExtraDIChance = atoi(DataRow[16]); buffs[BuffCount].persistant_buff = IsPersistent; diff --git a/zone/common.h b/zone/common.h index 97b94887a..ec364931b 100644 --- a/zone/common.h +++ b/zone/common.h @@ -156,9 +156,10 @@ struct Buffs_Struct { uint32 melee_rune; uint32 magic_rune; uint32 dot_rune; - uint32 caston_x; - uint32 caston_y; - uint32 caston_z; + int32 caston_x; + int32 caston_y; + int32 caston_z; + int32 ExtraDIChance; bool persistant_buff; bool client; //True if the caster is a client bool UpdateClient; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f307e08e2..99c45ea0f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1321,6 +1321,20 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } + case SE_DeathSave: { + + int16 mod = 0; + + if(caster) { + mod = caster->aabonuses.UnfailingDivinity + + caster->itembonuses.UnfailingDivinity + + caster->spellbonuses.UnfailingDivinity; + } + + buffs[buffslot].ExtraDIChance = mod; + break; + } + case SE_Illusion: { #ifdef SPELL_EFFECT_SPAM @@ -1432,10 +1446,17 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) snprintf(effect_desc, _EDLEN, "Memory Blur: %d", effect_value); #endif int wipechance = spells[spell_id].base[i]; - int bonus = spellbonuses.IncreaseChanceMemwipe + itembonuses.IncreaseChanceMemwipe + aabonuses.IncreaseChanceMemwipe; + int bonus = 0; + + if (caster){ + bonus = caster->spellbonuses.IncreaseChanceMemwipe + + caster->itembonuses.IncreaseChanceMemwipe + + caster->aabonuses.IncreaseChanceMemwipe; + } + wipechance += wipechance*bonus/100; - if(MakeRandomInt(0, 100) < wipechance) + if(MakeRandomInt(0, 99) < wipechance) { if(IsAIControlled()) { @@ -2804,7 +2825,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_IncreaseChanceMemwipe: case SE_CriticalMend: case SE_LimitCastTimeMax: - case SE_DeathSave: case SE_TriggerOnReqCaster: { break; @@ -3267,6 +3287,31 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste break; } + case SE_WipeHateList: + { + + int wipechance = spells[spell_id].base[i]; + int bonus = 0; + + if (caster){ + bonus = caster->spellbonuses.IncreaseChanceMemwipe + + caster->itembonuses.IncreaseChanceMemwipe + + caster->aabonuses.IncreaseChanceMemwipe; + } + + wipechance += wipechance*bonus/100; + + if(MakeRandomInt(0, 99) < wipechance) + { + if(IsAIControlled()) + { + WipeHateList(); + } + Message(13, "Your mind fogs. Who are my friends? Who are my enemies?... it was all so clear a moment ago..."); + } + break; + } + case SE_Charm: { if (!caster || !PassCharismaCheck(caster, this, spell_id)) { BuffFadeByEffect(SE_Charm); @@ -5262,13 +5307,10 @@ bool Mob::TryDeathSave() { int16 UD_HealMod = 0; uint32 HealAmt = 300; //Death Pact max Heal - Mob* caster = entity_list.GetMobID(buffs[buffSlot].casterid); - - if (caster) - UD_HealMod = caster->spellbonuses.UnfailingDivinity + caster->itembonuses.UnfailingDivinity + caster->aabonuses.UnfailingDivinity; - if(buffSlot >= 0){ + UD_HealMod = buffs[buffSlot].ExtraDIChance; + SuccessChance = ( (GetCHA() * (RuleI(Spells, DeathSaveCharismaMod))) + 1) / 10; //(CHA Mod Default = 3) if (SuccessChance > 95) @@ -5333,6 +5375,8 @@ bool Mob::TryDeathSave() { } } } + + BuffFadeBySlot(buffSlot); } return false; } diff --git a/zone/spells.cpp b/zone/spells.cpp index de16af190..eceade9b1 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2957,6 +2957,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].caston_y = 0; buffs[emptyslot].caston_z = 0; buffs[emptyslot].dot_rune = 0; + buffs[emptyslot].ExtraDIChance = 0; if (level_override > 0) { buffs[emptyslot].UpdateClient = true; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index aa68888fd..278f87eaf 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1815,7 +1815,7 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) { if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO merc_buffs (MercId, SpellId, CasterLevel, DurationFormula, " "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, " - "dot_rune, caston_x, Persistent, caston_y, caston_z) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);", + "dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", merc->GetMercID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula, buffs[BuffCount].ticsremaining, CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, @@ -1824,7 +1824,11 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) { CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune, buffs[BuffCount].dot_rune, - buffs[BuffCount].caston_x, IsPersistent, buffs[BuffCount].caston_y, buffs[BuffCount].caston_z), TempErrorMessageBuffer)) { + buffs[BuffCount].caston_x, + IsPersistent, + buffs[BuffCount].caston_y, + buffs[BuffCount].caston_z, + buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) { errorMessage = std::string(TempErrorMessageBuffer); safe_delete(Query); Query = 0; @@ -1856,7 +1860,7 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) { bool BuffsLoaded = false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, CasterAARank, Persistent FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) { + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) { errorMessage = std::string(TempErrorMessageBuffer); } else { @@ -1893,6 +1897,7 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) { buffs[BuffCount].caston_y = atoi(DataRow[13]); buffs[BuffCount].caston_z = atoi(DataRow[14]); + buffs[BuffCount].ExtraDIChance = atoi(DataRow[15]); buffs[BuffCount].persistant_buff = IsPersistent; @@ -2570,10 +2575,10 @@ void ZoneDatabase::SaveBuffs(Client *c) { if(buffs[i].spellid != SPELL_UNKNOWN) { if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `character_buffs` (character_id, slot_id, spell_id, " "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, magic_rune, persistent, dot_rune, " - "caston_x, caston_y, caston_z) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", + "caston_x, caston_y, caston_z, ExtraDIChance) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i')", c->CharacterID(), i, buffs[i].spellid, buffs[i].casterlevel, buffs[i].caster_name, buffs[i].ticsremaining, buffs[i].counters, buffs[i].numhits, buffs[i].melee_rune, buffs[i].magic_rune, buffs[i].persistant_buff, - buffs[i].dot_rune, buffs[i].caston_x, buffs[i].caston_y, buffs[i].caston_z), + buffs[i].dot_rune, buffs[i].caston_x, buffs[i].caston_y, buffs[i].caston_z, buffs[i].ExtraDIChance), errbuf)) { LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query, errbuf); } @@ -2595,7 +2600,7 @@ void ZoneDatabase::LoadBuffs(Client *c) { MYSQL_RES *result; MYSQL_ROW row; if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, counters, " - "numhits, melee_rune, magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z FROM `character_buffs` WHERE " + "numhits, melee_rune, magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance FROM `character_buffs` WHERE " "`character_id`='%u'", c->CharacterID()), errbuf, &result)) { @@ -2621,9 +2626,10 @@ void ZoneDatabase::LoadBuffs(Client *c) { uint32 magic_rune = atoul(row[8]); uint8 persistent = atoul(row[9]); uint32 dot_rune = atoul(row[10]); - uint32 caston_x = atoul(row[11]); - uint32 caston_y = atoul(row[12]); - uint32 caston_z = atoul(row[13]); + int32 caston_x = atoul(row[11]); + int32 caston_y = atoul(row[12]); + int32 caston_z = atoul(row[13]); + int32 ExtraDIChance = atoul(row[14]); buffs[slot_id].spellid = spell_id; buffs[slot_id].casterlevel = caster_level; @@ -2647,6 +2653,7 @@ void ZoneDatabase::LoadBuffs(Client *c) { buffs[slot_id].caston_x = caston_x; buffs[slot_id].caston_y = caston_y; buffs[slot_id].caston_z = caston_z; + buffs[slot_id].ExtraDIChance = ExtraDIChance; buffs[slot_id].UpdateClient = false; if(IsRuneSpell(spell_id)) { c->SetHasRune(true); @@ -2655,11 +2662,6 @@ void ZoneDatabase::LoadBuffs(Client *c) { c->SetHasSpellRune(true); } - /* - if(IsDeathSaveSpell(spell_id)) { - c->SetDeathSaveChance(true); - } - */ } mysql_free_result(result); } From 2a28e88bcfc6e56633dd1df91eafb249bbbe40f1 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 22 Feb 2014 02:23:37 -0500 Subject: [PATCH 05/91] not_reflectable -> reflectable --- common/shareddb.cpp | 2 +- common/spdat.h | 2 +- ...{2014_02_20_buff_update.txt => 2014_02_20_buff_update.sql} | 4 +++- zone/mob.cpp | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) rename utils/sql/git/required/{2014_02_20_buff_update.txt => 2014_02_20_buff_update.sql} (92%) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index aa136c41b..a49cec4e8 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1710,7 +1710,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].descnum = atoi(row[155]); sp[tempid].effectdescnum = atoi(row[157]); - sp[tempid].not_reflectable = atoi(row[161]) != 0; + sp[tempid].reflectable = atoi(row[161]) != 0; sp[tempid].bonushate=atoi(row[162]); sp[tempid].EndurCost=atoi(row[166]); diff --git a/common/spdat.h b/common/spdat.h index 70f4c4b24..d308206e3 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -688,7 +688,7 @@ struct SPDat_Spell_Struct /* 157 */ int effectdescnum; // eqstr of effect description /* 158 */ //Category Desc ID 3 /* 159 */ //bool npc_no_los; -/* 161 */ bool not_reflectable; +/* 161 */ bool reflectable; /* 162 */ int bonushate; /* 163 */ /* 164 */ // for most spells this appears to mimic ResistDiff diff --git a/utils/sql/git/required/2014_02_20_buff_update.txt b/utils/sql/git/required/2014_02_20_buff_update.sql similarity index 92% rename from utils/sql/git/required/2014_02_20_buff_update.txt rename to utils/sql/git/required/2014_02_20_buff_update.sql index a53930481..636fa0221 100644 --- a/utils/sql/git/required/2014_02_20_buff_update.txt +++ b/utils/sql/git/required/2014_02_20_buff_update.sql @@ -17,4 +17,6 @@ ALTER TABLE `botbuffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0'; ALTER TABLE `character_buffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0'; ALTER TABLE `merc_buffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0'; -ALTER TABLE `botbuffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0'; \ No newline at end of file +ALTER TABLE `botbuffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0'; + +ALTER TABLE `spells_new` CHANGE `not_reflectable` `reflectable` INT(11) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/zone/mob.cpp b/zone/mob.cpp index cee9003cc..a77589d63 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4348,7 +4348,7 @@ void Mob::MeleeLifeTap(int32 damage) { bool Mob::TryReflectSpell(uint32 spell_id) { - if (spells[spell_id].not_reflectable) + if (!spells[spell_id].reflectable) return false; int chance = itembonuses.reflect_chance + spellbonuses.reflect_chance + aabonuses.reflect_chance; From 476dc6e7837d8fbc3d5a64a44ab7d86493a172f1 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 22 Feb 2014 15:04:01 -0800 Subject: [PATCH 06/91] SetDestructibleObject exported to lua --- zone/lua_mob.cpp | 8 +++++++- zone/lua_mob.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 851c1293a..768e091db 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1801,6 +1801,11 @@ void Lua_Mob::SetAppearance(int app, bool ignore_self) { self->SetAppearance(static_cast(app), ignore_self); } +void Lua_Mob::SetDestructibleObject(bool set) { + Lua_Safe_Call_Void(); + self->SetDestructibleObject(set); +} + luabind::scope lua_register_mob() { return luabind::class_("Mob") .def(luabind::constructor<>()) @@ -2107,7 +2112,8 @@ luabind::scope lua_register_mob() { .def("ClearSpecialAbilities", (void(Lua_Mob::*)(void))&Lua_Mob::ClearSpecialAbilities) .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("SetAppearance", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetAppearance) + .def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject); } luabind::scope lua_register_special_abilities() { diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 160fae5ad..cc8ade4f7 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -342,6 +342,7 @@ public: void ProcessSpecialAbilities(std::string str); void SetAppearance(int app); void SetAppearance(int app, bool ignore_self); + void SetDestructibleObject(bool set); }; #endif From 5a6c25887ad86c7c4c59f867e5181092d2ac06fb Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 22 Feb 2014 15:26:18 -0800 Subject: [PATCH 07/91] Export of eq.clock(), os.clock() isn't a good option on unix --- zone/lua_general.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 1c22865c9..7b275ea67 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -16,6 +16,7 @@ #include "QuestParserCollection.h" #include "questmgr.h" #include "QGlobals.h" +#include "../common/timer.h" struct Events { }; struct Factions { }; @@ -1064,6 +1065,13 @@ void lua_clear_npctype_cache(int npctype_id) { quest_manager.ClearNPCTypeCache(npctype_id); } +double lua_clock() { + timeval read_time; + gettimeofday(&read_time, nullptr); + uint32 t = read_time.tv_sec * 1000 + read_time.tv_usec / 1000; + return static_cast(t) / 1000.0; +} + luabind::scope lua_register_general() { return luabind::namespace_("eq") [ @@ -1231,7 +1239,8 @@ luabind::scope lua_register_general() { luabind::def("clear_opcode", &lua_clear_opcode), luabind::def("enable_recipe", &lua_enable_recipe), luabind::def("disable_recipe", &lua_disable_recipe), - luabind::def("clear_npctype_cache", &lua_clear_npctype_cache) + luabind::def("clear_npctype_cache", &lua_clear_npctype_cache), + luabind::def("clock", &lua_clock) ]; } From c6377b93d517a02b369b8881c8028823486adae8 Mon Sep 17 00:00:00 2001 From: cavedude00 Date: Sun, 23 Feb 2014 15:55:26 -0800 Subject: [PATCH 08/91] Merchants will now keep track of charges sold to them properly so it is no longer possible to recharge items using merchants. --- common/Item.cpp | 8 ++++++ common/Item.h | 1 + zone/client_packet.cpp | 60 +++++++++++++++++++++++------------------- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/common/Item.cpp b/common/Item.cpp index 2e43a6d75..82e3b5112 100644 --- a/common/Item.cpp +++ b/common/Item.cpp @@ -238,6 +238,14 @@ bool ItemInst::IsStackable() const return m_item->Stackable; } +bool ItemInst::IsCharged() const +{ + if(m_item->MaxCharges > 1) + return true; + else + return false; +} + // Can item be equipped? bool ItemInst::IsEquipable(uint16 race, uint16 class_) const diff --git a/common/Item.h b/common/Item.h index 33f0ff12f..d62d92bda 100644 --- a/common/Item.h +++ b/common/Item.h @@ -291,6 +291,7 @@ public: // Can item be stacked? bool IsStackable() const; + bool IsCharged() const; // Can item be equipped by/at? bool IsEquipable(uint16 race, uint16 class_) const; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index af17626fb..d304715e4 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5561,8 +5561,13 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) Message(15,"You can only have one of a lore item."); return; } - if(tmpmer_used && (mp->quantity > prevcharges)) - mp->quantity = prevcharges; + if(tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1)) + { + if(prevcharges > item->MaxCharges && item->MaxCharges > 1) + mp->quantity = item->MaxCharges; + else + mp->quantity = prevcharges; + } EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); Merchant_Sell_Struct* mpo=(Merchant_Sell_Struct*)outapp->pBuffer; @@ -5573,13 +5578,11 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) int16 freeslotid=0; int16 charges = 0; - if (item->Stackable) { + if (item->Stackable || item->MaxCharges > 1) charges = mp->quantity; - } else { - // this needs expanded to handle varying charges from the merchant, - // but will require merchantlist_temp changes amonst other things. + else charges = item->MaxCharges; - } + ItemInst* inst = database.CreateItem(item, charges); int SinglePrice = 0; @@ -5588,7 +5591,10 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) else SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); - mpo->price = SinglePrice * mp->quantity; + if(item->MaxCharges > 1) + mpo->price = SinglePrice; + else + mpo->price = SinglePrice * mp->quantity; if(mpo->price < 0 ) { safe_delete(outapp); @@ -5632,9 +5638,6 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) } std::string packet; - if(mp->quantity==1 && item->MaxCharges>0 && item->MaxCharges<255) - mp->quantity=item->MaxCharges; - if (!stacked && inst) { PutItemInInventory(freeslotid, *inst); SendItemPacket(freeslotid, inst, ItemPacketTrade); @@ -5756,37 +5759,36 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) //Message(13,"%s tells you, 'LOL NOPE'", vendor->GetName()); return; } - if (RuleB(Merchant, UsePriceMod)){ - price=(int)((item->Price*mp->quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor,true)+0.5); // need to round up, because client does it automatically when displaying price - } + + int cost_quantity = mp->quantity; + if(inst->IsCharged()) + int cost_quantity = 1; + + if (RuleB(Merchant, UsePriceMod)) + price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor,true)+0.5); // need to round up, because client does it automatically when displaying price else - price=(int)((item->Price*mp->quantity)*(RuleR(Merchant, BuyCostMod))+0.5); + price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))+0.5); AddMoneyToPP(price,false); - if (inst->IsStackable()) + if (inst->IsStackable() || inst->IsCharged()) { unsigned int i_quan = inst->GetCharges(); - if (mp->quantity > i_quan) + if (mp->quantity > i_quan || inst->IsCharged()) mp->quantity = i_quan; } else - { mp->quantity = 1; - } if (RuleB(EventLog, RecordSellToMerchant)) LogMerchant(this, vendor, mp->quantity, price, item, false); - int freeslot = 0; - int charges = 0; - if(inst->IsStackable()) - charges = mp->quantity; - else - //charges = inst->GetCharges(); - //FIXME: Temp merchant table uses 'charges' as the quantity, so doesn't properly handle charged items. + int charges = mp->quantity; + //Hack workaround so usable items with 0 charges aren't simply deleted + if(charges == 0 && item->ItemType != 11 && item->ItemType != 17 && item->ItemType != 19 && item->ItemType != 21) charges = 1; - if((freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){ + int freeslot = 0; + if(charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){ ItemInst* inst2 = inst->Clone(); if (RuleB(Merchant, UsePriceMod)){ inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor,false)); @@ -5846,6 +5848,10 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) else this->DeleteItemInInventory(mp->itemslot,mp->quantity,false); + //This forces the price to show up correctly for charged items. + if(inst->IsCharged()) + mp->quantity = 1; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct)); Merchant_Purchase_Struct* mco=(Merchant_Purchase_Struct*)outapp->pBuffer; mco->npcid = vendor->GetID(); From 14649ce61102c6a36c09622a33dce0e312e45417 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Sun, 23 Feb 2014 19:48:46 -0500 Subject: [PATCH 09/91] Exported the client object SendTargetCommand to Perl. --- changelog.txt | 3 +++ zone/perl_client.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/changelog.txt b/changelog.txt index 7ad5f7ae6..d63eb2d20 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/23/2014 == +Secrets: Exported the client object SendTargetCommand to Perl. + == 02/20/2014 == Kayen: Implemented SE_MitigateDotDamage - dot spell mitigation rune with max value Kayen: Implemented SE_DistanceRemoval - removed from target when target moves X amount of distance away from where initially hit. diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 92ecd52ca..da81c03d7 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5792,6 +5792,30 @@ XS(XS_Client_SetThirst) XSRETURN_EMPTY; } +XS(XS_Client_SendTargetCommand); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendTargetCommand) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SendTargetCommand(THIS, in_entid)"); + { + Client * THIS; + int32 in_entid = (uint32)SvUV(ST(1)); + + 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 == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + THIS->SendTargetCommand(in_entid); + } + XSRETURN_EMPTY; +} + XS(XS_Client_SetConsumption); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SetConsumption) { @@ -6109,6 +6133,7 @@ XS(boot_Client) newXSproto(strcpy(buf, "SetConsumption"), XS_Client_SetConsumption, file, "$$$"); newXSproto(strcpy(buf, "SilentMessage"), XS_Client_SilentMessage, file, "$$"); newXSproto(strcpy(buf, "PlayMP3"), XS_Client_PlayMP3, file, "$;$"); + newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$"); XSRETURN_YES; } From 1f471aa9f929f063588278265ceb4b79f4968830 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 24 Feb 2014 00:22:02 -0500 Subject: [PATCH 10/91] Remove dist in Mob casting AI for roots --- zone/MobAI.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index 38addf695..26f8fb2e1 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -127,7 +127,6 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { case SpellType_Root: { if ( !tar->IsRooted() - && dist2 >= 900 && MakeRandomInt(0, 99) < 50 && tar->DontRootMeBefore() < Timer::GetCurrentTime() && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 @@ -167,7 +166,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { } case SpellType_InCombatBuff: { - if(MakeRandomInt(0,100) < 50) + if(MakeRandomInt(0, 99) < 50) { AIDoSpellCast(i, tar, mana_cost); return true; @@ -201,7 +200,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { break; } case SpellType_Dispel: { - if(MakeRandomInt(0, 100) < 15) + if(MakeRandomInt(0, 99) < 15) { if(!checked_los) { if(!CheckLosFN(tar)) From 6b94e08404b46670fd67874628b5b5551dce5cf8 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 24 Feb 2014 00:27:26 -0500 Subject: [PATCH 11/91] Make the random addition to time_cancast actually useful It was in ms so adding 0-4 ms doesn't really do much --- zone/MobAI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index 26f8fb2e1..c6584db5e 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -94,7 +94,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { dist2 <= spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range ) && (mana_cost <= GetMana() || GetMana() == GetMaxMana()) - && (AIspells[i].time_cancast+(MakeRandomInt(0, 4))) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time. + && (AIspells[i].time_cancast + (MakeRandomInt(0, 4) * 1000)) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time. ) { #if MobAI_DEBUG_Spells >= 21 From 837c02ffd6ec5d0b5776910a4535ac1ad1c49c2f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 24 Feb 2014 00:37:38 -0500 Subject: [PATCH 12/91] Mobs cast root on random person on hatelist --- zone/MobAI.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index c6584db5e..b14276313 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -125,20 +125,19 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { break; } case SpellType_Root: { - if ( - !tar->IsRooted() - && MakeRandomInt(0, 99) < 50 - && tar->DontRootMeBefore() < Timer::GetCurrentTime() - && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 + Mob *rootee = GetHateRandom(); + if (rootee && !rootee->IsRooted() && MakeRandomInt(0, 99) < 50 + && rootee->DontRootMeBefore() < Timer::GetCurrentTime() + && rootee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 ) { if(!checked_los) { - if(!CheckLosFN(tar)) + if(!CheckLosFN(rootee)) return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call checked_los = true; } uint32 tempTime = 0; - AIDoSpellCast(i, tar, mana_cost, &tempTime); - tar->SetDontRootMeBefore(tempTime); + AIDoSpellCast(i, rootee, mana_cost, &tempTime); + rootee->SetDontRootMeBefore(tempTime); return true; } break; From 60c280a5214b7920c9f52a289f3afece81ed7184 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 24 Feb 2014 00:43:24 -0500 Subject: [PATCH 13/91] Debuff/Slow on random mob on hatelist --- zone/MobAI.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index b14276313..e3fd0b171 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -182,7 +182,20 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { break; } case SpellType_Slow: - case SpellType_Debuff: + case SpellType_Debuff: { + Mob * debuffee = GetHateRandom(); + if (debuffee && manaR >= 10 && MakeRandomInt(0, 99 < 70) && + debuffee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0) { + if (!checked_los) { + if (!CheckLosFN(debuffee)) + return false; + checked_los = true; + } + AIDoSpellCast(i, debuffee, mana_cost); + return true; + } + break; + } case SpellType_Nuke: { if ( manaR >= 10 && MakeRandomInt(0, 99) < 70 From d887c77a1b6eb384782f3a3f9b2dd92300eee882 Mon Sep 17 00:00:00 2001 From: sorvani Date: Mon, 24 Feb 2014 00:20:18 -0600 Subject: [PATCH 14/91] Updated GetUnusedInstanceID to not recycle instance ID's unless it has reached max (65535) --- changelog.txt | 3 +++ common/database.cpp | 49 +++++++++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/changelog.txt b/changelog.txt index d63eb2d20..9bde9fe75 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/24/2014 == +Sorvani: Updated GetUnusedInstanceID to not recycle instance ID's unless it has reached max (65535) + == 02/23/2014 == Secrets: Exported the client object SendTargetCommand to Perl. diff --git a/common/database.cpp b/common/database.cpp index d0e2f3c59..04e4d3b9a 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2516,30 +2516,53 @@ bool Database::GetUnusedInstanceID(uint16 &instance_id) MYSQL_RES *result; MYSQL_ROW row; - uint32 count = RuleI(Zone, ReservedInstances) + 1; + uint32 count = RuleI(Zone, ReservedInstances); uint32 max = 65535; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id >= %i ORDER BY id", count), errbuf, &result)) { + if (RunQuery(query, MakeAnyLenString(&query, "SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u", count,count), errbuf, &result)) { safe_delete_array(query); if (mysql_num_rows(result) != 0) { - while((row = mysql_fetch_row(result))) { - if(count < atoi(row[0])) { - instance_id = count; - mysql_free_result(result); - return true; - } else if(count > max) { - instance_id = 0; - mysql_free_result(result); - return false; + row = mysql_fetch_row(result); + mysql_free_result(result); + if(atoi(row[0]) <= max) { + count = atoi(row[0]); + } else { + 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) { + count++; + while((row = mysql_fetch_row(result))) { + if(count < atoi(row[0])) { + instance_id = count; + mysql_free_result(result); + return true; + } else if(count > max) { + instance_id = 0; + mysql_free_result(result); + return false; + } else { + count++; + } + } + } else { + instance_id = 0; + mysql_free_result(result); + return false; + } } else { - count++; + safe_delete_array(query); + instance_id = 0; + return false; } } } else { + instance_id = 0; mysql_free_result(result); + return false; } } else { safe_delete_array(query); + instance_id = 0; + return false; } instance_id = count; return true; From 33a8a398fe1aa24af60c6dcd30ced3e4c3b0df39 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 24 Feb 2014 01:24:27 -0500 Subject: [PATCH 15/91] Add SpellType_Root to EngagedCast check --- zone/MobAI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index e3fd0b171..a9c5085fd 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -1881,7 +1881,7 @@ bool NPC::AI_EngagedCastCheck() { // try casting a heal on nearby if (!entity_list.AICheckCloseBeneficialSpells(this, 25, MobAISpellRange, SpellType_Heal)) { //nobody to heal, try some detrimental spells. - if(!AICastSpell(GetTarget(), 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm)) { + if(!AICastSpell(GetTarget(), 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) { //no spell to cast, try again soon. AIautocastspell_timer->Start(RandomTimer(500, 1000), false); } From 20869a0aec735e14252a1bebea33d12c12f2a421 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 24 Feb 2014 02:31:40 -0500 Subject: [PATCH 16/91] Mobs will now face their target they are casting on Note: they do not switch their target, this is how live does it --- zone/spells.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index de9ff6336..6fd687c18 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -461,8 +461,9 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, if (IsAIControlled()) { SetRunAnimSpeed(0); - if(this != pMob) - this->FaceTarget(pMob); + pMob = entity_list.GetMob(target_id); + if (pMob && this != pMob) + FaceTarget(pMob); } // if we got here we didn't fizzle, and are starting our cast From 746569b471311b537ae7c7fba76a5229f836bd47 Mon Sep 17 00:00:00 2001 From: Michael Cook Date: Mon, 24 Feb 2014 17:25:49 -0500 Subject: [PATCH 17/91] Fix HOTs timer when they're in the song window --- zone/effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index bd14af6f7..785dd882c 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -426,7 +426,7 @@ int32 Client::GetActSpellDuration(uint16 spell_id, int32 duration) // Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1 // However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync - if (!IsShortDurationBuff(spell_id) || + if (!(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) || IsFearSpell(spell_id) || IsCharmSpell(spell_id) || IsMezSpell(spell_id) || From 970b30b467f7d89f3bf0799f3d98f7904a01bd1b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 25 Feb 2014 23:15:14 -0500 Subject: [PATCH 18/91] Added entity_list::IsMobSpawnedByNpcTypeID Exported to both lua and perl --- zone/entity.cpp | 16 ++++++++++++++++ zone/entity.h | 1 + zone/lua_entity_list.cpp | 6 ++++++ zone/lua_entity_list.h | 1 + zone/perl_entity.cpp | 28 ++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+) diff --git a/zone/entity.cpp b/zone/entity.cpp index 9103ed157..64bbe53c1 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1012,6 +1012,22 @@ Mob *EntityList::GetMobByNpcTypeID(uint32 get_id) return nullptr; } +bool EntityList::IsMobSpawnedByNpcTypeID(uint32 get_id) +{ + if (get_id == 0 || npc_list.empty()) + return false; + + auto it = npc_list.begin(); + while (it != npc_list.end()) { + // Mobs will have a 0 as their GetID() if they're dead + if (it->second->GetNPCTypeID() == get_id && it->second->GetID() != 0) + return true; + ++it; + } + + return false; +} + Object *EntityList::GetObjectByDBID(uint32 id) { if (id == 0 || object_list.empty()) diff --git a/zone/entity.h b/zone/entity.h index dbdeb5dcd..a42e63f9b 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -135,6 +135,7 @@ public: inline Mob *GetMobID(uint16 id) { return(GetMob(id)); } //for perl Mob *GetMob(const char* name); Mob *GetMobByNpcTypeID(uint32 get_id); + bool IsMobSpawnedByNpcTypeID(uint32 get_id); Mob *GetTargetForVirus(Mob* spreader); inline NPC *GetNPCByID(uint16 id) { return npc_list.count(id) ? npc_list.at(id) : nullptr; } diff --git a/zone/lua_entity_list.cpp b/zone/lua_entity_list.cpp index 5319b016a..32bd9a89d 100644 --- a/zone/lua_entity_list.cpp +++ b/zone/lua_entity_list.cpp @@ -65,6 +65,11 @@ Lua_Mob Lua_EntityList::GetMobByNpcTypeID(int npc_type) { return Lua_Mob(self->GetMobByNpcTypeID(npc_type)); } +bool Lua_EntityList::IsMobSpawnedByNpcTypeID(int npc_type) { + Lua_Safe_Call_Bool(); + return self->IsMobSpawnedByNpcTypeID(npc_type); +} + Lua_NPC Lua_EntityList::GetNPCByID(int id) { Lua_Safe_Call_Class(Lua_NPC); return Lua_NPC(self->GetNPCByID(id)); @@ -420,6 +425,7 @@ luabind::scope lua_register_entity_list() { .def("GetMob", (Lua_Mob(Lua_EntityList::*)(const char*))&Lua_EntityList::GetMob) .def("GetMob", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMob) .def("GetMobByNpcTypeID", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMobByNpcTypeID) + .def("IsMobSpawnedByNpcTypeID", (bool(Lua_EntityList::*)(int))&Lua_EntityList::IsMobSpawnedByNpcTypeID) .def("GetNPCByID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByID) .def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID) .def("GetClientByName", (Lua_Client(Lua_EntityList::*)(const char*))&Lua_EntityList::GetClientByName) diff --git a/zone/lua_entity_list.h b/zone/lua_entity_list.h index 4fbc5f0ed..dc9c1a5ed 100644 --- a/zone/lua_entity_list.h +++ b/zone/lua_entity_list.h @@ -51,6 +51,7 @@ public: Lua_Mob GetMob(const char *name); Lua_Mob GetMob(int id); Lua_Mob GetMobByNpcTypeID(int npc_type); + bool IsMobSpawnedByNpcTypeID(int npc_type); Lua_NPC GetNPCByID(int id); Lua_NPC GetNPCByNPCTypeID(int npc_type); Lua_Client GetClientByName(const char *name); diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp index 255e9278f..3d685e011 100644 --- a/zone/perl_entity.cpp +++ b/zone/perl_entity.cpp @@ -150,6 +150,33 @@ XS(XS_EntityList_GetMobByNpcTypeID) XSRETURN(1); } +XS(XS_EntityList_IsMobSpawnedByNpcTypeID); /* prototype pass -Wmissing-prototypes */ +XS(XS_EntityList_IsMobSpawnedByNpcTypeID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::ValidMobByNpcTypeID(THIS, get_id)"); + { + EntityList * THIS; + bool RETVAL; + uint32 get_id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->IsMobSpawnedByNpcTypeID(get_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + XS(XS_EntityList_GetNPCByID); /* prototype to pass -Wmissing-prototypes */ XS(XS_EntityList_GetNPCByID) { @@ -2127,6 +2154,7 @@ XS(boot_EntityList) newXSproto(strcpy(buf, "GetMob"), XS_EntityList_GetMob, file, "$$"); newXSproto(strcpy(buf, "GetMobByID"), XS_EntityList_GetMobByID, file, "$$"); newXSproto(strcpy(buf, "GetMobByNpcTypeID"), XS_EntityList_GetMobByNpcTypeID, file, "$$"); + newXSproto(strcpy(buf, "IsMobSpawnedByNpcTypeID"), XS_EntityList_IsMobSpawnedByNpcTypeID, file, "$$"); newXSproto(strcpy(buf, "GetNPCByID"), XS_EntityList_GetNPCByID, file, "$$"); newXSproto(strcpy(buf, "GetNPCByNPCTypeID"), XS_EntityList_GetNPCByNPCTypeID, file, "$$"); newXSproto(strcpy(buf, "GetClientByName"), XS_EntityList_GetClientByName, file, "$$"); From 8caac162b2e19c99cf4ac7257aa595e053001205 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 26 Feb 2014 00:09:59 -0500 Subject: [PATCH 19/91] Spell effect addition / fixes --- changelog.txt | 5 +++++ common/spdat.h | 4 ++-- zone/aggro.cpp | 2 +- zone/attack.cpp | 3 +++ zone/bonuses.cpp | 20 +++++++++++++++++++- zone/common.h | 5 +++-- zone/spell_effects.cpp | 3 +-- 7 files changed, 34 insertions(+), 8 deletions(-) diff --git a/changelog.txt b/changelog.txt index 9bde9fe75..7cd8ace08 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- + +== 02/26/2014 == +Kayen: Implemented SE_FrenziedDevestation - increase critical spell chacnce and 2x mana cost for DD spells +Kayen: Fixed SE_SpellProcChance - Now works on spell dervived procs + == 02/24/2014 == Sorvani: Updated GetUnusedInstanceID to not recycle instance ID's unless it has reached max (65535) diff --git a/common/spdat.h b/common/spdat.h index 56de446d2..347da070b 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -359,7 +359,7 @@ typedef enum { #define SE_DispelBeneficial 209 // implemented //#define SE_PetShield 210 // *not implemented #define SE_AEMelee 211 // implemented -//#define SE_CastingSkills 212 // *not implemented -Include/Exclude Casting Skill type. (*no longer used on live) +#define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana. #define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet #define SE_MaxHPChange 214 // implemented #define SE_PetAvoidance 215 // implemented[AA] - increases pet ability to avoid melee damage @@ -397,7 +397,7 @@ typedef enum { #define SE_RaiseSkillCap 247 // *not implemented[AA] - adds skill over the skill cap. //#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100) #define SE_SecondaryDmgInc 249 // implemented[AA] Allows off hand weapon to recieve a damage bonus (Sinister Strikes) -#define SE_SpellProcChance 250 // implemented - Increase chance to sympathetic proc by % +#define SE_SpellProcChance 250 // implemented - Increase chance to proc from melee proc spells (ie Spirit of Panther) #define SE_ConsumeProjectile 251 // implemented[AA] - chance to not consume an arrow (ConsumeProjectile = 100) #define SE_FrontalBackstabChance 252 // implemented[AA] - chance to perform a full damage backstab from front. #define SE_FrontalBackstabMinDmg 253 // implemented[AA] - allow a frontal backstab for mininum damage. diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 326e320c0..d9c50a430 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1414,7 +1414,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { if (caster->IsClient()) { //3: At maxed ability, Total Domination has a 50% chance of preventing the charm break that otherwise would have occurred. - uint16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance; + int16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance; if (MakeRandomInt(0, 99) < TotalDominationBonus) return true; diff --git a/zone/attack.cpp b/zone/attack.cpp index 4467d6320..5e7b21a93 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4149,6 +4149,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on } } + int16 SpellProcChance = spellbonuses.SpellProcChance + itembonuses.SpellProcChance + aabonuses.SpellProcChance; uint32 i; for(i = 0; i < MAX_PROCS; i++) { if (PermaProcs[i].spellID != SPELL_UNKNOWN) { @@ -4169,6 +4170,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on else { int chance = ProcChance * (SpellProcs[i].chance); + chance += chance*SpellProcChance/100; if(MakeRandomInt(0, 100) < chance) { mlog(COMBAT__PROCS, "Spell proc %d procing spell %d (%d percent chance)", i, SpellProcs[i].spellID, chance); ExecWeaponProc(nullptr, SpellProcs[i].spellID, on); @@ -4180,6 +4182,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on } if (bRangedAttack) { int chance = ProcChance * RangedProcs[i].chance; + chance += chance*SpellProcChance/100; if(MakeRandomInt(0, 100) < chance) { mlog(COMBAT__PROCS, "Ranged proc %d procing spell %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index eb5cad582..743c5d250 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1228,6 +1228,14 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) break; } + case SE_FrenziedDevastation: + newbon->FrenziedDevastation += base2; + break; + + case SE_SpellProcChance: + newbon->SpellProcChance += base1; + break; + } } } @@ -2524,6 +2532,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->DistanceRemoval = true; break; + case SE_FrenziedDevastation: + newbon->FrenziedDevastation += spells[spell_id].base2[i]; + break; + } } } @@ -3866,7 +3878,13 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) spellbonuses.ImprovedTaunt[0] = effect_value; spellbonuses.ImprovedTaunt[1] = effect_value; spellbonuses.ImprovedTaunt[2] = -1; - + break; + + case SE_FrenziedDevastation: + spellbonuses.FrenziedDevastation += effect_value; + aabonuses.FrenziedDevastation += effect_value; + itembonuses.FrenziedDevastation += effect_value; + break; } } } diff --git a/zone/common.h b/zone/common.h index a832657e3..39f766794 100644 --- a/zone/common.h +++ b/zone/common.h @@ -311,8 +311,8 @@ struct StatBonuses { //uint16 BlockSpellEffect[EFFECT_COUNT]; // Prevents spells with certain effects from landing on you *no longer used bool ImmuneToFlee; // Bypass the fleeing flag uint16 VoiceGraft; // Stores the ID of the mob with which to talk through - uint16 SpellProcChance; // chance to proc from sympathetic spell effects - uint16 CharmBreakChance; // chance to break charm + int16 SpellProcChance; // chance to proc from sympathetic spell effects + int16 CharmBreakChance; // chance to break charm int16 SongRange; // increases range of beneficial bard songs uint16 HPToManaConvert; // Uses HP to cast spells at specific conversion uint16 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have. @@ -335,6 +335,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 + 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/spell_effects.cpp b/zone/spell_effects.cpp index 5deb29e14..bdc3bbc68 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2826,6 +2826,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_CriticalMend: case SE_LimitCastTimeMax: case SE_TriggerOnReqCaster: + case SE_FrenziedDevastation: { break; } @@ -5396,14 +5397,12 @@ bool Mob::AffectedBySpellExcludingSlot(int slot, int effect) float Mob::GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod) { - ProcBonus = spellbonuses.SpellProcChance + itembonuses.SpellProcChance; ProcChance = 0; if(cast_time > 0) { ProcChance = ((float)cast_time * RuleR(Casting, AvgSpellProcsPerMinute) / 60000.0f); ProcChance = ProcChance * (float)(ProcRateMod/100); - ProcChance = ProcChance+(ProcChance*ProcBonus/100); } return ProcChance; } From 205dd8a1e5c7053cf7bd87e2682eb55a46ccbd12 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 26 Feb 2014 05:16:37 -0500 Subject: [PATCH 20/91] Clean up and revision of proc chance bonuses Combat Effects and WeaponProc bonuses will no longer affect melee spell procs --- .../required/2014_02_26_virulentvenomAA.sql | 8 ++++ zone/attack.cpp | 44 +++++++------------ zone/bonuses.cpp | 21 ++++----- zone/bot.cpp | 18 ++++---- zone/bot.h | 2 +- zone/common.h | 1 + zone/effects.cpp | 8 ++++ zone/mob.h | 2 +- 8 files changed, 54 insertions(+), 50 deletions(-) create mode 100644 utils/sql/git/required/2014_02_26_virulentvenomAA.sql diff --git a/utils/sql/git/required/2014_02_26_virulentvenomAA.sql b/utils/sql/git/required/2014_02_26_virulentvenomAA.sql new file mode 100644 index 000000000..6fa6cec9e --- /dev/null +++ b/utils/sql/git/required/2014_02_26_virulentvenomAA.sql @@ -0,0 +1,8 @@ +-- Virulent Venom +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('888', '1', '250', '10', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('889', '1', '250', '20', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('890', '1', '250', '30', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('891', '1', '250', '40', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('892', '1', '250', '50', '0'); + + diff --git a/zone/attack.cpp b/zone/attack.cpp index 5e7b21a93..78ab250cd 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3888,14 +3888,14 @@ void Mob::HealDamage(uint32 amount, Mob* caster) { //proc chance includes proc bonus -float Mob::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { +float Mob::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { int mydex = GetDEX(); - float AABonus = 0; ProcBonus = 0; ProcChance = 0; + BaseProcChance = 0; - if (aabonuses.ProcChance) - AABonus = float(aabonuses.ProcChance) / 100.0f; + ProcBonus = float(aabonuses.ProcChanceSPA + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); //Spell Effects + ProcBonus += float(itembonuses.ProcChance)/10.0f; //Combat Effects switch(hand){ case 13: @@ -3915,18 +3915,18 @@ float Mob::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_spe if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance weapon_speed = RuleI(Combat, MinHastedDelay); - ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus); - if(RuleB(Combat, AdjustProcPerMinute) == true) { ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f; - ProcChance = ProcChance + (ProcChance * ProcBonus); + BaseProcChance = ProcChance; + ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib); + ProcChance += ProcChance*ProcBonus/100.0f; } else { ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy); - ProcChance = ProcChance + (ProcChance * ProcBonus); + BaseProcChance = ProcChance; + ProcChance += ProcChance*ProcBonus/100.0f; } mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); @@ -3950,16 +3950,6 @@ float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 w break; } - /* - float PermaHaste; - if(GetHaste() > 0) - PermaHaste = 1 / (1 + (float)GetHaste()/100); - else if(GetHaste() < 0) - PermaHaste = 1 * (1 - (float)GetHaste()/100); - else - PermaHaste = 1.0f; - */ - //calculate the weapon speed in ms, so we can use the rule to compare against. //weapon_speed = ((int)(weapon_speed*(100.0f+attack_speed)*PermaHaste)); if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance @@ -4054,8 +4044,8 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { //we have to calculate these again, oh well int ourlevel = GetLevel(); - float ProcChance, ProcBonus; - GetProcChances(ProcBonus, ProcChance, weapon_g->GetItem()->Delay, hand); + float ProcChance, ProcBonus, BaseProcChance; + GetProcChances(BaseProcChance, ProcBonus, ProcChance, weapon_g->GetItem()->Delay, hand); if(hand != 13) { ProcChance /= 2; @@ -4092,11 +4082,11 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on, uint16 hand) { uint16 skillinuse = 28; int ourlevel = GetLevel(); - float ProcChance, ProcBonus; + float ProcChance, ProcBonus, BaseProcChance; if(weapon!=nullptr) - GetProcChances(ProcBonus, ProcChance, weapon->Delay, hand); + GetProcChances(BaseProcChance, ProcBonus, ProcChance, weapon->Delay, hand); else - GetProcChances(ProcBonus, ProcChance); + GetProcChances(BaseProcChance, ProcBonus, ProcChance); if(hand != 13) //Is Archery intended to proc at 50% rate? ProcChance /= 2; @@ -4153,7 +4143,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on uint32 i; for(i = 0; i < MAX_PROCS; i++) { if (PermaProcs[i].spellID != SPELL_UNKNOWN) { - if(MakeRandomInt(0, 100) < PermaProcs[i].chance) { + if(MakeRandomInt(0, 100) < PermaProcs[i].chance) { //TODO: Unclear if these are treated like Spells or WeaponProcs mlog(COMBAT__PROCS, "Permanent proc %d procing spell %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); ExecWeaponProc(nullptr, PermaProcs[i].spellID, on); } else { @@ -4169,7 +4159,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on } else { - int chance = ProcChance * (SpellProcs[i].chance); + int chance = BaseProcChance * (SpellProcs[i].chance); chance += chance*SpellProcChance/100; if(MakeRandomInt(0, 100) < chance) { mlog(COMBAT__PROCS, "Spell proc %d procing spell %d (%d percent chance)", i, SpellProcs[i].spellID, chance); @@ -4181,7 +4171,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on } } if (bRangedAttack) { - int chance = ProcChance * RangedProcs[i].chance; + int chance = BaseProcChance * RangedProcs[i].chance; chance += chance*SpellProcChance/100; if(MakeRandomInt(0, 100) < chance) { mlog(COMBAT__PROCS, "Ranged proc %d procing spell %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 743c5d250..7cde92ae4 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -963,7 +963,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) newbon->GiveDoubleAttack += base1; break; case SE_ProcChance: - newbon->ProcChance += base1; + newbon->ProcChanceSPA += base1; break; case SE_RiposteChance: newbon->RiposteChance += base1; @@ -1890,17 +1890,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_ProcChance: { - //multiplier is to be compatible with item effects,watching for overflow too - effect_value = effect_value<3000? effect_value : 3000; - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->ProcChance += effect_value; + newbon->ProcChanceSPA += effect_value; - else if((effect_value < 0) && (newbon->DoubleAttackChance > effect_value)) - newbon->ProcChance = effect_value; + else if((effect_value < 0) && (newbon->ProcChanceSPA > effect_value)) + newbon->ProcChanceSPA = effect_value; - if(newbon->ProcChance < effect_value) - newbon->ProcChance = effect_value; + if(newbon->ProcChanceSPA < effect_value) + newbon->ProcChanceSPA = effect_value; break; } @@ -3323,9 +3320,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_ProcChance: - spellbonuses.ProcChance = effect_value; - aabonuses.ProcChance = effect_value; - itembonuses.ProcChance = effect_value; + spellbonuses.ProcChanceSPA = effect_value; + aabonuses.ProcChanceSPA = effect_value; + itembonuses.ProcChanceSPA = effect_value; break; case SE_ExtraAttackChance: diff --git a/zone/bot.cpp b/zone/bot.cpp index a7ff02f1b..3cac34024 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7705,14 +7705,14 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } //proc chance includes proc bonus -float Bot::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { +float Bot::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { int mydex = GetDEX(); - float AABonus = 0; ProcBonus = 0; ProcChance = 0; + BaseProcChance = 0; - if (aabonuses.ProcChance) - AABonus = float(aabonuses.ProcChance) / 100.0f; + ProcBonus = float(aabonuses.ProcChanceSPA + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); //Spell Effects + ProcBonus += float(itembonuses.ProcChance)/10.0f; //Combat Effects switch(hand){ case SLOT_PRIMARY: @@ -7732,18 +7732,18 @@ float Bot::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_spe if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance weapon_speed = RuleI(Combat, MinHastedDelay); - ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus); - if(RuleB(Combat, AdjustProcPerMinute) == true) { ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f; - ProcChance = ProcChance + (ProcChance * ProcBonus); + ProcChance = BaseProcChance; + ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib); + ProcChance += ProcChance*ProcBonus / 100.0f; } else { ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy); - ProcChance = ProcChance + (ProcChance * ProcBonus); + ProcChance = BaseProcChance; + ProcChance += ProcChance*ProcBonus / 100.0f; } mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); diff --git a/zone/bot.h b/zone/bot.h index 309931acd..8cf439c57 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -167,7 +167,7 @@ public: uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; } uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; } uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; } - virtual float GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand); + virtual float GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand); virtual bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte); virtual int GetMonkHandToHandDamage(void); virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse); diff --git a/zone/common.h b/zone/common.h index 39f766794..89ef7e573 100644 --- a/zone/common.h +++ b/zone/common.h @@ -274,6 +274,7 @@ struct StatBonuses { int16 DamageModifier[HIGHEST_SKILL+2]; //i int16 MinDamageModifier[HIGHEST_SKILL+2]; //i int16 ProcChance; // ProcChance/10 == % increase i = CombatEffects + int16 ProcChanceSPA; // ProcChance from spell effects int16 ExtraAttackChance; int16 DoTShielding; int16 DivineSaveChance[2]; // Second Chance (base1 = chance, base2 = spell on trigger) diff --git a/zone/effects.cpp b/zone/effects.cpp index 785dd882c..3c11b41d5 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -81,6 +81,8 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { int chance = RuleI(Spells, BaseCritChance); chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; + + chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation; if (chance > 0){ @@ -321,6 +323,12 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int32 Client::GetActSpellCost(uint16 spell_id, int32 cost) { + //FrenziedDevastation doubles mana cost of all DD spells + int16 FrenziedDevastation = itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation; + + if (FrenziedDevastation && IsPureNukeSpell(spell_id)) + cost *= 2; + // Formula = Unknown exact, based off a random percent chance up to mana cost(after focuses) of the cast spell if(this->itembonuses.Clairvoyance && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) { diff --git a/zone/mob.h b/zone/mob.h index f636b5ea7..47e87412f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -971,7 +971,7 @@ protected: void TryWeaponProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13); void TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = 13); void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on); - virtual float GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); + virtual float GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item); int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr); From 1d6bd3cc5ea51449f48e352bab5a7f798978621d Mon Sep 17 00:00:00 2001 From: cavedude00 Date: Wed, 26 Feb 2014 18:06:16 -0800 Subject: [PATCH 21/91] Better flee runspeed calculation. Added two new NPC special_abilities, ALWAYS_FLEE and FLEE_PERCENT. Fixed an issue where a NPC could get stuck on a single coord in a rectangular roambox. Added mindelay to spawngroup to allow for greater control of the roambox delay. SQL is required. --- changelog.txt | 7 ++++ common/ruletypes.h | 4 +- .../required/2014_02_26_roambox_update.sql | 2 + zone/MobAI.cpp | 10 +++-- zone/command.cpp | 16 +++++--- zone/common.h | 5 ++- zone/fearpath.cpp | 40 ++++++++++--------- zone/lua_mob.cpp | 4 +- zone/lua_npc.cpp | 6 +-- zone/lua_npc.h | 2 +- zone/net.cpp | 8 ++++ zone/npc.cpp | 16 +++++++- zone/npc.h | 5 ++- zone/perl_npc.cpp | 20 +++++++--- zone/questmgr.cpp | 4 +- zone/spawn2.cpp | 4 +- zone/spawngroup.cpp | 11 ++--- zone/spawngroup.h | 3 +- zone/waypoints.cpp | 7 ++-- 19 files changed, 118 insertions(+), 56 deletions(-) create mode 100644 utils/sql/git/required/2014_02_26_roambox_update.sql diff --git a/changelog.txt b/changelog.txt index 7cd8ace08..8d4485765 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,12 +4,19 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 02/26/2014 == Kayen: Implemented SE_FrenziedDevestation - increase critical spell chacnce and 2x mana cost for DD spells Kayen: Fixed SE_SpellProcChance - Now works on spell dervived procs +cavedude: Added two new NPC special_abilities. ALWAYS_FLEE, which forces the NPC to always flee ignoring FleeIfNotAlone and FLEE_PERCENT which allows you to change the HP an individual NPC will flee at. If no value is set, the rule is used as normal. +cavedude: Fixed an issue where rectangular roamboxes could cause an NPC to get stuck on a single coord. +cavedude: Added a new roambox column, mindelay allowing you to have more control over the roambox delay. + +Required SQL: utils/sql/git/required/2014_02_26_roambox_update.sql == 02/24/2014 == +cavedude: Better flee runspeed calculation. Added rule Combat:FleeMultiplier to alter this behavior. Sorvani: Updated GetUnusedInstanceID to not recycle instance ID's unless it has reached max (65535) == 02/23/2014 == Secrets: Exported the client object SendTargetCommand to Perl. +cavedude: Merchants will now keep better track of charges. == 02/20/2014 == Kayen: Implemented SE_MitigateDotDamage - dot spell mitigation rune with max value diff --git a/common/ruletypes.h b/common/ruletypes.h index b610c308d..3f9e74546 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -310,8 +310,8 @@ RULE_INT ( Combat, ClientBaseCritChance, 0 ) //The base crit chance for all clie RULE_BOOL ( Combat, UseIntervalAC, true) RULE_INT ( Combat, PetAttackMagicLevel, 30) RULE_BOOL ( Combat, EnableFearPathing, true) -RULE_INT ( Combat, FleeHPRatio, 25) -RULE_INT ( Combat, FleeSnareHPRatio, 11) // HP at which snare will halt movement of a fleeing NPC. +RULE_REAL ( Combat, FleeMultiplier, 2.0) // Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker. +RULE_INT ( Combat, FleeHPRatio, 25) //HP % when a NPC begins to flee. RULE_BOOL ( Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it. RULE_BOOL ( Combat, AdjustProcPerMinute, true) RULE_REAL ( Combat, AvgProcsPerMinute, 2.0) diff --git a/utils/sql/git/required/2014_02_26_roambox_update.sql b/utils/sql/git/required/2014_02_26_roambox_update.sql new file mode 100644 index 000000000..5b099983f --- /dev/null +++ b/utils/sql/git/required/2014_02_26_roambox_update.sql @@ -0,0 +1,2 @@ +alter table `spawngroup` add column `mindelay` int(11) not null default 15000 AFTER delay; +alter table `spawngroup` change `delay` `delay` int(11) not null default 45000; \ No newline at end of file diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index a9c5085fd..bcb4b131b 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -455,6 +455,7 @@ void NPC::AI_Init() { roambox_distance = 0; roambox_movingto_x = 0; roambox_movingto_y = 0; + roambox_min_delay = 2500; roambox_delay = 2500; } @@ -1590,14 +1591,17 @@ void NPC::AI_DoMovement() { movey *= MakeRandomInt(0, 1) ? 1 : -1; roambox_movingto_x = GetX() + movex; roambox_movingto_y = GetY() + movey; + //Try to calculate new coord using distance. if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x) roambox_movingto_x -= movex * 2; if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y) roambox_movingto_y -= movey * 2; + //New coord is still invalid, ignore distance and just pick a new random coord. + //If we're here we may have a roambox where one side is shorter than the specified distance. Commons, Wkarana, etc. if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x) - roambox_movingto_x = roambox_max_x; + roambox_movingto_x = MakeRandomFloat(roambox_min_x+1,roambox_max_x-1); if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y) - roambox_movingto_y = roambox_max_y; + roambox_movingto_y = MakeRandomFloat(roambox_min_y+1,roambox_max_y-1); } mlog(AI__WAYPOINTS, "Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)", @@ -1605,7 +1609,7 @@ void NPC::AI_DoMovement() { if (!CalculateNewPosition2(roambox_movingto_x, roambox_movingto_y, GetZ(), walksp, true)) { roambox_movingto_x = roambox_max_x + 1; // force update - pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_delay, roambox_delay + 5000); + pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay); SetMoving(false); SendPosition(); // makes mobs stop clientside } diff --git a/zone/command.cpp b/zone/command.cpp index 1b8ce3365..40e37045e 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2085,21 +2085,27 @@ void command_ai(Client *c, const Seperator *sep) } else if (strcasecmp(sep->arg[1], "roambox") == 0) { if (target && target->IsAIControlled() && target->IsNPC()) { - if ((sep->argnum == 6 || sep->argnum == 7) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) { + if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) { uint32 tmp = 2500; + uint32 tmp2 = 2500; if (sep->IsNumber(7)) tmp = atoi(sep->arg[7]); - target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp); + if (sep->IsNumber(8)) + tmp2 = atoi(sep->arg[8]); + target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp, tmp2); } else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) { uint32 tmp = 2500; + uint32 tmp2 = 2500; if (sep->IsNumber(4)) tmp = atoi(sep->arg[4]); - target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp); + if (sep->IsNumber(5)) + tmp2 = atoi(sep->arg[5]); + target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2); } else { - c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [delay]"); - c->Message(0, "Usage: #ai roambox dist roamdist [delay]"); + c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]"); + c->Message(0, "Usage: #ai roambox dist roamdist [delay] [mindelay]"); } } else diff --git a/zone/common.h b/zone/common.h index 89ef7e573..eac6b0940 100644 --- a/zone/common.h +++ b/zone/common.h @@ -122,7 +122,10 @@ enum { TETHER = 33, DESTRUCTIBLE_OBJECT = 34, NO_HARM_FROM_CLIENT = 35, - MAX_SPECIAL_ATTACK = 36 + ALWAYS_FLEE = 36, + FLEE_PERCENT = 37, + MAX_SPECIAL_ATTACK = 38 + }; typedef enum { //fear states diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 57a3de337..698d59bae 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -53,7 +53,10 @@ void Mob::CheckFlee() { //see if were possibly hurt enough float ratio = GetHPRatio(); - if(ratio >= RuleI(Combat, FleeHPRatio)) + float fleeratio = GetSpecialAbility(FLEE_PERCENT); + fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio); + + if(ratio >= fleeratio) return; //we might be hurt enough, check con now.. @@ -77,25 +80,24 @@ void Mob::CheckFlee() { switch(con) { //these values are not 100% researched case CON_GREEN: - run_ratio = RuleI(Combat, FleeHPRatio); + run_ratio = fleeratio; break; case CON_LIGHTBLUE: - run_ratio = RuleI(Combat, FleeHPRatio) * 8 / 10; + run_ratio = fleeratio * 9 / 10; break; case CON_BLUE: - run_ratio = RuleI(Combat, FleeHPRatio) * 6 / 10; + run_ratio = fleeratio * 8 / 10; break; default: - run_ratio = RuleI(Combat, FleeHPRatio) * 4 / 10; + run_ratio = fleeratio * 7 / 10; break; } if(ratio < run_ratio) { if (RuleB(Combat, FleeIfNotAlone) || - (!RuleB(Combat, FleeIfNotAlone) && - (entity_list.GetHatedCount(hate_top, this) == 0))) + GetSpecialAbility(ALWAYS_FLEE) || + (!RuleB(Combat, FleeIfNotAlone) && (entity_list.GetHatedCount(hate_top, this) == 0))) StartFleeing(); - } } @@ -110,7 +112,9 @@ void Mob::ProcessFlee() { } //see if we are still dying, if so, do nothing - if(GetHPRatio() < (float)RuleI(Combat, FleeHPRatio)) + float fleeratio = GetSpecialAbility(FLEE_PERCENT); + fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio); + if(GetHPRatio() < fleeratio) return; //we are not dying anymore... see what we do next @@ -128,19 +132,19 @@ void Mob::ProcessFlee() { float Mob::GetFearSpeed() { if(flee_mode) { //we know ratio < FLEE_HP_RATIO - float speed = GetRunspeed(); + float speed = GetBaseRunspeed(); float ratio = GetHPRatio(); + float multiplier = RuleR(Combat, FleeMultiplier); - // mob's movement will halt with a decent snare at HP specified by rule. - if (ratio <= RuleI(Combat, FleeSnareHPRatio) && GetSnaredAmount() > 40) { - return 0.0001f; - } + if(GetSnaredAmount() > 40) + multiplier = multiplier / 6.0f; - if (ratio < FLEE_HP_MINSPEED) - ratio = FLEE_HP_MINSPEED; - - speed = speed * 0.5 * ratio / 100; + speed = speed * ratio * multiplier / 100; + //NPC will eventually stop. Snares speeds this up. + if(speed < 0.09) + speed = 0.0001f; + return(speed); } return(GetRunspeed()); diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index fb028ba11..1fc1f38e3 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -2166,7 +2166,9 @@ luabind::scope lua_register_special_abilities() { luabind::value("leash", static_cast(LEASH)), luabind::value("tether", static_cast(TETHER)), luabind::value("destructible_object", static_cast(DESTRUCTIBLE_OBJECT)), - luabind::value("no_harm_from_client", static_cast(NO_HARM_FROM_CLIENT)) + luabind::value("no_harm_from_client", static_cast(NO_HARM_FROM_CLIENT)), + luabind::value("always_flee", static_cast(ALWAYS_FLEE)), + luabind::value("flee_percent", static_cast(FLEE_PERCENT)) ]; } diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index 184bf2a4a..e9d1ae0ec 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -297,9 +297,9 @@ void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, f self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y); } -void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay) { +void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay, uint32 mindelay) { Lua_Safe_Call_Void(); - self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y, delay); + self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y, delay, mindelay); } int Lua_NPC::GetNPCSpellsID() { @@ -494,7 +494,7 @@ luabind::scope lua_register_npc() { .def("SaveGuardSpot", (void(Lua_NPC::*)(bool))&Lua_NPC::SaveGuardSpot) .def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding) .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float))&Lua_NPC::AI_SetRoambox) - .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32))&Lua_NPC::AI_SetRoambox) + .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32,uint32))&Lua_NPC::AI_SetRoambox) .def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID) .def("GetSpawnPointID", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointID) .def("GetSpawnPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointX) diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 3ea322adc..1dbd33253 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -85,7 +85,7 @@ public: void SaveGuardSpot(bool clear); bool IsGuarding(); void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y); - void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay); + void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay, uint32 mindelay); int GetNPCSpellsID(); int GetSpawnPointID(); float GetSpawnPointX(); diff --git a/zone/net.cpp b/zone/net.cpp index 1d49d17ed..5c1f95e16 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -381,6 +381,14 @@ int main(int argc, char** argv) { entity_list.AddClient(client); } + uint8 IDLEZONETIME = 200; + if ( numclients < 1 && temp_timer.GetDuration() != IDLEZONETIME ) + temp_timer.SetTimer(IDLEZONETIME); + else if ( numclients > 0 && temp_timer.GetDuration() == IDLEZONETIME ) + { + temp_timer.SetTimer(10); + temp_timer.Trigger(); + } //check for timeouts in other threads timeout_manager.CheckTimeouts(); diff --git a/zone/npc.cpp b/zone/npc.cpp index 16c626d89..2c21c94a7 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -216,6 +216,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float roambox_min_y = -2; roambox_movingto_x = -2; roambox_movingto_y = -2; + roambox_min_delay = 1000; roambox_delay = 1000; org_heading = heading; p_depop = false; @@ -1526,6 +1527,12 @@ void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool rem case 'i': SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1); break; + case 'e': + SetSpecialAbility(ALWAYS_FLEE, remove ? 0 : 1); + break; + case 'h': + SetSpecialAbility(FLEE_PERCENT, remove ? 0 : 1); + break; default: break; @@ -1686,7 +1693,14 @@ bool Mob::HasNPCSpecialAtk(const char* parse) { HasAllAttacks = false; } break; - + case 'e': + if(!GetSpecialAbility(ALWAYS_FLEE)) + HasAllAttacks = false; + break; + case 'h': + if(!GetSpecialAbility(FLEE_PERCENT)) + HasAllAttacks = false; + break; default: HasAllAttacks = false; break; diff --git a/zone/npc.h b/zone/npc.h index bcb69e0d7..f374c8fe6 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -265,8 +265,8 @@ public: inline bool IsGuarding() const { return(guard_heading != 0); } void SaveGuardSpotCharm(); void RestoreGuardSpotCharm(); - void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500); - void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500); + void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500, uint32 iMinDelay = 2500); + void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500, uint32 iMinDelay = 2500); //mercenary stuff void LoadMercTypes(); @@ -430,6 +430,7 @@ protected: float roambox_movingto_x; float roambox_movingto_y; uint32 roambox_delay; + uint32 roambox_min_delay; uint16 skills[HIGHEST_SKILL+1]; uint32 equipment[MAX_WORN_INVENTORY]; //this is an array of item IDs diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 0ac61e3cc..2ab0ca036 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1433,8 +1433,8 @@ XS(XS_NPC_AI_SetRoambox); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_AI_SetRoambox) { dXSARGS; - if (items < 6 || items > 7) - Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500)"); + if (items < 6 || items > 8) + Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500, iMinDelay= 2500)"); { NPC * THIS; float iDist = (float)SvNV(ST(1)); @@ -1443,6 +1443,7 @@ XS(XS_NPC_AI_SetRoambox) float iMaxY = (float)SvNV(ST(4)); float iMinY = (float)SvNV(ST(5)); uint32 iDelay; + uint32 iMinDelay; if (sv_derived_from(ST(0), "NPC")) { IV tmp = SvIV((SV*)SvRV(ST(0))); @@ -1453,13 +1454,20 @@ XS(XS_NPC_AI_SetRoambox) if(THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - if (items < 7) + if (items < 7){ + iMinDelay = 2500; iDelay = 2500; - else { + } + else if (items < 8){ + iMinDelay = 2500; iDelay = (uint32)SvUV(ST(6)); } + else { + iDelay = (uint32)SvUV(ST(6)); + iMinDelay = (uint32)SvUV(ST(7)); + } - THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay); + THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay, iMinDelay); } XSRETURN_EMPTY; } @@ -2208,7 +2216,7 @@ XS(boot_NPC) newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$"); newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$"); newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$"); - newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$"); + newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$$"); newXSproto(strcpy(buf, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$"); newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$"); newXSproto(strcpy(buf, "GetSpawnPointX"), XS_NPC_GetSpawnPointX, file, "$"); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index e0183852f..484a63d40 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -347,8 +347,8 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id) entity_list.AddNPC(npc); entity_list.LimitAddNPC(npc); - if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay) - npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay); + if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay && sg->min_delay) + npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay,sg->min_delay); if(zone->InstantGrids()) { found_spawn->LoadGrid(); diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index 30f89998f..9b4963fd4 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -229,8 +229,8 @@ bool Spawn2::Process() { entity_list.AddNPC(npc); //this limit add must be done after the AddNPC since we need the entity ID. entity_list.LimitAddNPC(npc); - if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay) - npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay); + if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay && sg->min_delay) + npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay,sg->min_delay); if(zone->InstantGrids()) { _log(SPAWNS__MAIN, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).", spawn2_id, spawngroup_id_, npc->GetName(), npcid, x, y, z); LoadGrid(); diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index 85fba5c20..0044b06eb 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -35,7 +35,7 @@ SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_lim npc_spawn_limit = in_npc_spawn_limit; } -SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in ) { +SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in, int min_delay_in ) { id = in_id; strn0cpy( name_, name, 120); group_spawn_limit = in_group_spawn_limit; @@ -44,6 +44,7 @@ SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, floa roambox[2]=maxy; roambox[3]=miny; roamdist=dist; + min_delay=min_delay_in; delay=delay_in; despawn=despawn_in; despawn_timer=despawn_timer_in; @@ -150,11 +151,11 @@ bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnG // CODER new spawn code query = 0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result)) + if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result)) { safe_delete_array(query); while((row = mysql_fetch_row(result))) { - SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10])); + SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11])); spawn_group_list->AddSpawnGroup(newSpawnGroup); } mysql_free_result(result); @@ -205,11 +206,11 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_g // CODER new spawn code query = 0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result)) + if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result)) { safe_delete_array(query); while((row = mysql_fetch_row(result))) { - SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10])); + SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11])); spawn_group_list->AddSpawnGroup(newSpawnGroup); } mysql_free_result(result); diff --git a/zone/spawngroup.h b/zone/spawngroup.h index ce1b150db..bec6389be 100644 --- a/zone/spawngroup.h +++ b/zone/spawngroup.h @@ -39,13 +39,14 @@ public: class SpawnGroup { public: - SpawnGroup(uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in ); + SpawnGroup(uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in, int min_delay_in ); ~SpawnGroup(); uint32 GetNPCType(); void AddSpawnEntry( SpawnEntry* newEntry ); uint32 id; float roamdist; float roambox[4]; + int min_delay; int delay; int despawn; uint32 despawn_timer; diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index e9257d5a0..449f34c2a 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -47,11 +47,11 @@ static inline float ABS(float x) { return(x); } -void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay) { - AI_SetRoambox(iDist, GetX()+iRoamDist, GetX()-iRoamDist, GetY()+iRoamDist, GetY()-iRoamDist, iDelay); +void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay, uint32 iMinDelay) { + AI_SetRoambox(iDist, GetX()+iRoamDist, GetX()-iRoamDist, GetY()+iRoamDist, GetY()-iRoamDist, iDelay, iMinDelay); } -void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay) { +void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay, uint32 iMinDelay) { roambox_distance = iDist; roambox_max_x = iMaxX; roambox_min_x = iMinX; @@ -59,6 +59,7 @@ void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, floa roambox_min_y = iMinY; roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc roambox_delay = iDelay; + roambox_min_delay = iMinDelay; } void NPC::DisplayWaypointInfo(Client *c) { From f89d783308ad8dced4fc947cde450ea75864ad86 Mon Sep 17 00:00:00 2001 From: cavedude00 Date: Wed, 26 Feb 2014 18:20:16 -0800 Subject: [PATCH 22/91] Corrected 2014_02_26_virulentvenomAA.sql --- utils/sql/git/required/2014_02_26_virulentvenomAA.sql | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/sql/git/required/2014_02_26_virulentvenomAA.sql b/utils/sql/git/required/2014_02_26_virulentvenomAA.sql index 6fa6cec9e..0a0520c7b 100644 --- a/utils/sql/git/required/2014_02_26_virulentvenomAA.sql +++ b/utils/sql/git/required/2014_02_26_virulentvenomAA.sql @@ -1,8 +1,8 @@ -- Virulent Venom -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('888', '1', '250', '10', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('889', '1', '250', '20', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('890', '1', '250', '30', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('891', '1', '250', '40', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1, `base2`) VALUES ('892', '1', '250', '50', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('888', '1', '250', '10', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('889', '1', '250', '20', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('890', '1', '250', '30', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('891', '1', '250', '40', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('892', '1', '250', '50', '0'); From 489a6ffd16e6f14b522f0c19badec0d3b7eee474 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 26 Feb 2014 21:27:30 -0500 Subject: [PATCH 23/91] Fix for 'sqrt' failures on vs2010 clients --- changelog.txt | 1 + zone/spell_effects.cpp | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/changelog.txt b/changelog.txt index 8d4485765..f59ae6a35 100644 --- a/changelog.txt +++ b/changelog.txt @@ -7,6 +7,7 @@ Kayen: Fixed SE_SpellProcChance - Now works on spell dervived procs cavedude: Added two new NPC special_abilities. ALWAYS_FLEE, which forces the NPC to always flee ignoring FleeIfNotAlone and FLEE_PERCENT which allows you to change the HP an individual NPC will flee at. If no value is set, the rule is used as normal. cavedude: Fixed an issue where rectangular roamboxes could cause an NPC to get stuck on a single coord. cavedude: Added a new roambox column, mindelay allowing you to have more control over the roambox delay. +Uleat: Fix for 'sqrt' failure on vs2010 clients Required SQL: utils/sql/git/required/2014_02_26_roambox_update.sql diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index bdc3bbc68..bd584ea6e 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3419,13 +3419,11 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste { if (spellbonuses.DistanceRemoval){ - int distance = sqrt( - ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) + - ((int(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) + - ((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z)) - ); + int distance = ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) + + ((int(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) + + ((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z)); - if (distance > spells[spell_id].base[i]){ + if (distance > (spells[spell_id].base[i] * spells[spell_id].base[i])){ if(!TryFadeEffect(slot)) BuffFadeBySlot(slot , true); From 01bd8bd9faf02863ea022cb3a7a86a9f9209727c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 27 Feb 2014 03:19:25 -0500 Subject: [PATCH 24/91] Reworked Weapon Procing Broke up the logic to be a bit easier to follow Procs should happen in the order that they do on live Proc rates for spell buff procs should be more consistent with live --- zone/attack.cpp | 284 ++++++++++++++++++++++++------------------------ zone/bot.cpp | 35 +++--- zone/bot.h | 2 +- zone/mob.h | 3 +- 4 files changed, 161 insertions(+), 163 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 78ab250cd..b658ba655 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3885,19 +3885,13 @@ void Mob::HealDamage(uint32 amount, Mob* caster) { safe_delete_array(TempString); } - - //proc chance includes proc bonus -float Mob::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { +float Mob::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand) +{ int mydex = GetDEX(); - ProcBonus = 0; - ProcChance = 0; - BaseProcChance = 0; + float ProcChance = 0.0f; - ProcBonus = float(aabonuses.ProcChanceSPA + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); //Spell Effects - ProcBonus += float(itembonuses.ProcChance)/10.0f; //Combat Effects - - switch(hand){ + switch (hand) { case 13: weapon_speed = attack_timer.GetDuration(); break; @@ -3909,24 +3903,20 @@ float Mob::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcCh break; } - //calculate the weapon speed in ms, so we can use the rule to compare against. - - if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance + // fast as a client can swing, so should be the floor of the proc chance + if (weapon_speed < RuleI(Combat, MinHastedDelay)) weapon_speed = RuleI(Combat, MinHastedDelay); - if(RuleB(Combat, AdjustProcPerMinute) == true) - { - ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - BaseProcChance = ProcChance; - ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib); - ProcChance += ProcChance*ProcBonus/100.0f; - } - else - { - ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy); - BaseProcChance = ProcChance; - ProcChance += ProcChance*ProcBonus/100.0f; + if (RuleB(Combat, AdjustProcPerMinute)) { + ProcChance = (static_cast(weapon_speed) * + RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms + ProcBonus += static_cast(mydex) * RuleR(Combat, ProcPerMinDexContrib); + ProcChance += ProcChance * ProcBonus / 100.0f; + } else { + ProcChance = RuleR(Combat, BaseProcChance) + + static_cast(mydex) / RuleR(Combat, ProcDexDivideBy); + ProcChance += ProcChance * ProcBonus / 100.0f; } mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); @@ -4029,162 +4019,178 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { } if(!weapon_g) { - TryWeaponProc(nullptr, (const Item_Struct*)nullptr, on, hand); + TrySpellProc(nullptr, (const Item_Struct*)nullptr, on); return; } if(!weapon_g->IsType(ItemClassCommon)) { - TryWeaponProc(nullptr, (const Item_Struct*) nullptr, on, hand); + TrySpellProc(nullptr, (const Item_Struct*)nullptr, on); return; } - //do main procs + // Innate + aug procs from weapons + // TODO: powersource procs TryWeaponProc(weapon_g, weapon_g->GetItem(), on, hand); + // Procs from Buffs and AA both melee and range + TrySpellProc(weapon_g, weapon_g->GetItem(), on, hand); + return; +} - //we have to calculate these again, oh well +void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand) +{ + if (!weapon) + return; + uint16 skillinuse = 28; int ourlevel = GetLevel(); - float ProcChance, ProcBonus, BaseProcChance; - GetProcChances(BaseProcChance, ProcBonus, ProcChance, weapon_g->GetItem()->Delay, hand); - if(hand != 13) - { + float ProcBonus = static_cast(aabonuses.ProcChanceSPA + + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); + ProcBonus += static_cast(itembonuses.ProcChance) / 10.0f; // Combat Effects + float ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand); + + if (hand != 13) //Is Archery intened to proc at 50% rate? ProcChance /= 2; + + // Try innate proc on weapon + // We can proc once here, either weapon or one aug + bool proced = false; // silly bool to prevent augs from going if weapon does + skillinuse = GetSkillByItemType(weapon->ItemType); + if (weapon->Proc.Type == ET_CombatProc) { + float WPC = ProcChance * (100.0f + // Proc chance for this weapon + static_cast(weapon->ProcRate)) / 100.0f; + if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. + if (weapon->Proc.Level > ourlevel) { + mlog(COMBAT__PROCS, + "Tried to proc (%s), but our level (%d) is lower than required (%d)", + weapon->Name, ourlevel, weapon->Proc.Level); + if (IsPet()) { + Mob *own = GetOwner(); + if (own) + own->Message_StringID(13, PROC_PETTOOLOW); + } else { + Message_StringID(13, PROC_TOOLOW); + } + } else { + mlog(COMBAT__PROCS, + "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", + weapon->Name, weapon->Proc.Effect, WPC * 100); + ExecWeaponProc(inst, weapon->Proc.Effect, on); + proced = true; + } + } } - //do augment procs - int r; - for(r = 0; r < MAX_AUGMENT_SLOTS; r++) { - const ItemInst* aug_i = weapon_g->GetAugment(r); - if(!aug_i) - continue; - const Item_Struct* aug = aug_i->GetItem(); - if(!aug) - continue; + if (!proced) { + for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) { + const ItemInst *aug_i = inst->GetAugment(r); + if (!aug_i) // no aug, try next slot! + continue; + const Item_Struct *aug = aug_i->GetItem(); + if (!aug) + continue; - if (aug->Proc.Type == ET_CombatProc) { - ProcChance = ProcChance*(100+aug->ProcRate)/100; - if (MakeRandomFloat(0, 1) < ProcChance) { - if(aug->Proc.Level > ourlevel) { - Mob * own = GetOwner(); - if(own != nullptr) { - own->Message_StringID(13,PROC_PETTOOLOW); + if (aug->Proc.Type == ET_CombatProc) { + float APC = ProcChance * (100.0f + // Proc chance for this aug + static_cast(aug->ProcRate)) / 100.0f; + if (MakeRandomFloat(0, 1) <= APC) { + if (IsPet()) { + Mob *own = GetOwner(); + if (own) + own->Message_StringID(13, PROC_PETTOOLOW); } else { - Message_StringID(13,PROC_TOOLOW); + Message_StringID(13, PROC_TOOLOW); } } else { ExecWeaponProc(aug_i, aug->Proc.Effect, on); + break; } } } } + // TODO: Powersource procs + if (HasSkillProcs()) + TrySkillProc(on, skillinuse, ProcChance); + + return; } -void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on, uint16 hand) { - uint16 skillinuse = 28; - int ourlevel = GetLevel(); - float ProcChance, ProcBonus, BaseProcChance; - if(weapon!=nullptr) - GetProcChances(BaseProcChance, ProcBonus, ProcChance, weapon->Delay, hand); +void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand) +{ + float ProcBonus = static_cast(spellbonuses.SpellProcChance + + itembonuses.SpellProcChance + aabonuses.SpellProcChance); + float ProcChance = 0.0f; + if (weapon) + ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand); else - GetProcChances(BaseProcChance, ProcBonus, ProcChance); + ProcChance = GetProcChances(ProcBonus); - if(hand != 13) //Is Archery intended to proc at 50% rate? + if (hand != 13) //Is Archery intened to proc at 50% rate? ProcChance /= 2; - //give weapon a chance to proc first. - if(weapon != nullptr) { - skillinuse = GetSkillByItemType(weapon->ItemType); - if (weapon->Proc.Type == ET_CombatProc) { - float WPC = ProcChance*(100.0f+(float)weapon->ProcRate)/100.0f; - if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. - if(weapon->Proc.Level > ourlevel) { - mlog(COMBAT__PROCS, "Tried to proc (%s), but our level (%d) is lower than required (%d)", weapon->Name, ourlevel, weapon->Proc.Level); - Mob * own = GetOwner(); - if(own != nullptr) { - own->Message_StringID(13,PROC_PETTOOLOW); - } else { - Message_StringID(13,PROC_TOOLOW); - } + bool rangedattk = false; + if (weapon && hand == 11) { + if (weapon->ItemType == ItemTypeArrow || + weapon->ItemType == ItemTypeLargeThrowing || + weapon->ItemType == ItemTypeSmallThrowing || + weapon->ItemType == ItemTypeBow) + rangedattk = true; + } + + for (uint32 i = 0; i < MAX_PROCS; i++) { + if (IsPet() && hand == 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) + continue; // If pets ever can proc from off hand, this will need to change + + // Not ranged + if (!rangedattk) { + // Perma procs (AAs) + if (PermaProcs[i].spellID != SPELL_UNKNOWN) { + if (MakeRandomInt(0, 99) < PermaProcs[i].chance) { // TODO: Do these get spell bonus? + mlog(COMBAT__PROCS, + "Permanent proc %d procing spell %d (%d percent chance)", + i, PermaProcs[i].spellID, PermaProcs[i].chance); + ExecWeaponProc(nullptr, PermaProcs[i].spellID, on); } else { - mlog(COMBAT__PROCS, "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", weapon->Name, weapon->Proc.Effect, ProcChance*100); - ExecWeaponProc(inst, weapon->Proc.Effect, on); + mlog(COMBAT__PROCS, + "Permanent proc %d failed to proc %d (%d percent chance)", + i, PermaProcs[i].spellID, PermaProcs[i].chance); } - } else { - mlog(COMBAT__PROCS, "Attacking weapon (%s) did no proc (%.2f percent chance).", weapon->Name, ProcChance*100); } - } - } - if(ProcBonus == -1) { - LogFile->write(EQEMuLog::Error, "ProcBonus was -1 value!"); - return; - } - - bool bRangedAttack = false; - if (weapon != nullptr) { - if (weapon->ItemType == ItemTypeBow || weapon->ItemType == ItemTypeLargeThrowing || weapon->ItemType == ItemTypeSmallThrowing) { - bRangedAttack = true; - } - } - - bool isRanged = false; - if(weapon) - { - if(weapon->ItemType == ItemTypeArrow || - weapon->ItemType == ItemTypeLargeThrowing || - weapon->ItemType == ItemTypeSmallThrowing || - weapon->ItemType == ItemTypeBow) - { - isRanged = true; - } - } - - int16 SpellProcChance = spellbonuses.SpellProcChance + itembonuses.SpellProcChance + aabonuses.SpellProcChance; - uint32 i; - for(i = 0; i < MAX_PROCS; i++) { - if (PermaProcs[i].spellID != SPELL_UNKNOWN) { - if(MakeRandomInt(0, 100) < PermaProcs[i].chance) { //TODO: Unclear if these are treated like Spells or WeaponProcs - mlog(COMBAT__PROCS, "Permanent proc %d procing spell %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); - ExecWeaponProc(nullptr, PermaProcs[i].spellID, on); - } else { - mlog(COMBAT__PROCS, "Permanent proc %d failed to proc %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); - } - } - - if(!isRanged) - { - if(IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) - { - //Maybe implement this later if pets are ever given dual procs? - } - else - { - int chance = BaseProcChance * (SpellProcs[i].chance); - chance += chance*SpellProcChance/100; - if(MakeRandomInt(0, 100) < chance) { - mlog(COMBAT__PROCS, "Spell proc %d procing spell %d (%d percent chance)", i, SpellProcs[i].spellID, chance); + // Spell procs (buffs) + if (SpellProcs[i].spellID != SPELL_UNKNOWN) { + float chance = ProcChance * (SpellProcs[i].chance / 100.0f); + if (MakeRandomFloat(0, 1) <= chance) { + mlog(COMBAT__PROCS, + "Spell proc %d procing spell %d (%.2f percent chance)", + i, SpellProcs[i].spellID, chance); ExecWeaponProc(nullptr, SpellProcs[i].spellID, on); CheckNumHitsRemaining(11, 0, SpellProcs[i].base_spellID); } else { - mlog(COMBAT__PROCS, "Spell proc %d failed to proc %d (%d percent chance)", i, SpellProcs[i].spellID, chance); + mlog(COMBAT__PROCS, + "Spell proc %d failed to proc %d (%.2f percent chance)", + i, SpellProcs[i].spellID, chance); } } - } - if (bRangedAttack) { - int chance = BaseProcChance * RangedProcs[i].chance; - chance += chance*SpellProcChance/100; - if(MakeRandomInt(0, 100) < chance) { - mlog(COMBAT__PROCS, "Ranged proc %d procing spell %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); - ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); - CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID); - } else { - mlog(COMBAT__PROCS, "Ranged proc %d failed to proc %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); + } else if (rangedattk) { // ranged only + // ranged spell procs (buffs) + if (RangedProcs[i].spellID != SPELL_UNKNOWN) { + float chance = ProcChance * (RangedProcs[i].chance / 100.0f); + if (MakeRandomFloat(0, 1) <= chance) { + mlog(COMBAT__PROCS, + "Ranged proc %d procing spell %d (%.2f percent chance)", + i, RangedProcs[i].spellID, chance); + ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); + CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID); + } else { + mlog(COMBAT__PROCS, + "Ranged proc %d failed to proc %d (%.2f percent chance)", + i, RangedProcs[i].spellID, chance); + } } } } - if (HasSkillProcs()) - TrySkillProc(on, skillinuse, ProcChance); + return; } void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3cac34024..2b9580452 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7705,16 +7705,11 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } //proc chance includes proc bonus -float Bot::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { +float Bot::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand) { int mydex = GetDEX(); - ProcBonus = 0; - ProcChance = 0; - BaseProcChance = 0; + float ProcChance = 0.0f; - ProcBonus = float(aabonuses.ProcChanceSPA + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); //Spell Effects - ProcBonus += float(itembonuses.ProcChance)/10.0f; //Combat Effects - - switch(hand){ + switch (hand) { case SLOT_PRIMARY: weapon_speed = attack_timer.GetDuration(); break; @@ -7726,23 +7721,19 @@ float Bot::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcCh break; } - //calculate the weapon speed in ms, so we can use the rule to compare against. - - if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance + // fast as a client can swing, so should be the floor of the proc chance + if (weapon_speed < RuleI(Combat, MinHastedDelay)) weapon_speed = RuleI(Combat, MinHastedDelay); - if(RuleB(Combat, AdjustProcPerMinute) == true) - { - ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - ProcChance = BaseProcChance; - ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib); - ProcChance += ProcChance*ProcBonus / 100.0f; - } - else - { - ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy); - ProcChance = BaseProcChance; + if (RuleB(Combat, AdjustProcPerMinute)) { + ProcChance = (static_cast(weapon_speed) * + RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms + ProcBonus += static_cast(mydex) * RuleR(Combat, ProcPerMinDexContrib); + ProcChance += ProcChance * ProcBonus / 100.0f; + } else { + ProcChance = RuleR(Combat, BaseProcChance) + + static_cast(mydex) / RuleR(Combat, ProcDexDivideBy); ProcChance += ProcChance*ProcBonus / 100.0f; } diff --git a/zone/bot.h b/zone/bot.h index 8cf439c57..b0ff52845 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -167,7 +167,7 @@ public: uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; } uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; } uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; } - virtual float GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand); + virtual float GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand); virtual bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte); virtual int GetMonkHandToHandDamage(void); virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse); diff --git a/zone/mob.h b/zone/mob.h index 47e87412f..9b5b353eb 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -969,9 +969,10 @@ protected: bool PassLimitClass(uint32 Classes_, uint16 Class_); void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage=0); void TryWeaponProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13); + void TrySpellProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13); void TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = 13); void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on); - virtual float GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); + virtual float GetProcChances(float ProcBonus, uint16 weapon_speed = 30, uint16 hand = 13); virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item); int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr); From 0f60fb06e37fbf7d6d0183214b237fb8014799e1 Mon Sep 17 00:00:00 2001 From: Siroro Date: Thu, 27 Feb 2014 09:41:57 +0000 Subject: [PATCH 25/91] Added rule Merchant: EnableAltCurrencySell - defaults to true, allows servers to disable the ability to resell items to alternate currency merchants. --- common/ruletypes.h | 2 ++ zone/client_packet.cpp | 56 +++++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 3f9e74546..dcfeafbaf 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -466,6 +466,8 @@ RULE_INT ( Merchant, PriceBonusPct, 4) // Determines maximum price bonus from ha RULE_INT ( Merchant, PricePenaltyPct, 4) // Determines maximum price penalty from having bad faction/CHA. Value is a percent. RULE_REAL( Merchant, ChaBonusMod, 3.45) // Determines CHA cap, from 104 CHA. 3.45 is 132 CHA at apprehensive. 0.34 is 400 CHA at apprehensive. RULE_REAL ( Merchant, ChaPenaltyMod, 1.52) // Determines CHA bottom, up to 102 CHA. 1.52 is 37 CHA at apprehensive. 0.98 is 0 CHA at apprehensive. +RULE_BOOL ( Merchant, EnableAltCurrencySell, true) // Enables the ability to resell items to alternate currency merchants + RULE_CATEGORY_END() RULE_CATEGORY ( Bazaar ) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d304715e4..1b3485ec8 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12627,32 +12627,38 @@ void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) uint32 cost = 0; uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); uint32 merchant_id = tar->MerchantType; - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; + + if (RuleB(Merchant, EnableAltCurrencySell)) { + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == inst->GetItem()->ID) { + cost = ml.alt_currency_cost; + found = true; + break; + } } - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - - if(item->ID == inst->GetItem()->ID) { - cost = ml.alt_currency_cost; - found = true; - break; + if (!found) { + cost = 0; } } - - if(!found) { + else { cost = 0; } @@ -12802,6 +12808,10 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) { return; } + if (!RuleB(Merchant, EnableAltCurrencySell)) { + return; + } + const Item_Struct* item = nullptr; uint32 cost = 0; uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); @@ -12824,7 +12834,7 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) { if(!item) continue; - if(item->ID == inst->GetItem()->ID) { + if(item->ID == inst->GetItem()->ID) { cost = ml.alt_currency_cost; found = true; break; From a490b2ff222b7587b5c4b42fb1b1230b5698ad52 Mon Sep 17 00:00:00 2001 From: Siroro Date: Thu, 27 Feb 2014 12:00:06 +0000 Subject: [PATCH 26/91] Fixed augs having 100% proc rate --- zone/attack.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index b658ba655..db8e5bf6c 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4091,18 +4091,22 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on if (aug->Proc.Type == ET_CombatProc) { float APC = ProcChance * (100.0f + // Proc chance for this aug - static_cast(aug->ProcRate)) / 100.0f; + static_cast(aug->ProcRate)) / 100.0f; if (MakeRandomFloat(0, 1) <= APC) { - if (IsPet()) { - Mob *own = GetOwner(); - if (own) - own->Message_StringID(13, PROC_PETTOOLOW); - } else { - Message_StringID(13, PROC_TOOLOW); + if (aug->Proc.Level > ourlevel) { + if (IsPet()) { + Mob *own = GetOwner(); + if (own) + own->Message_StringID(13, PROC_PETTOOLOW); + } + else { + Message_StringID(13, PROC_TOOLOW); + } + } + else { + ExecWeaponProc(aug_i, aug->Proc.Effect, on); + break; } - } else { - ExecWeaponProc(aug_i, aug->Proc.Effect, on); - break; } } } From 16afa277def8ffc478a271854c7735acffa09c76 Mon Sep 17 00:00:00 2001 From: cavedude00 Date: Thu, 27 Feb 2014 23:21:57 -0800 Subject: [PATCH 27/91] Exported TrainDisc to Lua. Updated idle timer names. --- changelog.txt | 4 ++++ zone/lua_client.cpp | 6 ++++++ zone/lua_client.h | 1 + zone/net.cpp | 19 ++++++++++--------- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/changelog.txt b/changelog.txt index f59ae6a35..56afea293 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/27/2014 == +cavedude: Exported TrainDisc to Lua. + == 02/26/2014 == Kayen: Implemented SE_FrenziedDevestation - increase critical spell chacnce and 2x mana cost for DD spells Kayen: Fixed SE_SpellProcChance - Now works on spell dervived procs @@ -8,6 +11,7 @@ cavedude: Added two new NPC special_abilities. ALWAYS_FLEE, which forces the NPC cavedude: Fixed an issue where rectangular roamboxes could cause an NPC to get stuck on a single coord. cavedude: Added a new roambox column, mindelay allowing you to have more control over the roambox delay. Uleat: Fix for 'sqrt' failure on vs2010 clients +image: Added idle zone timer to save CPU cycles. Required SQL: utils/sql/git/required/2014_02_26_roambox_update.sql diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index f49fb0da0..a1af1b499 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -560,6 +560,11 @@ void Lua_Client::UnscribeSpellAll(bool update_client) { self->UnscribeSpellAll(update_client); } +void Lua_Client::TrainDisc(int itemid) { + Lua_Safe_Call_Void(); + self->TrainDiscipline(itemid); +} + void Lua_Client::UntrainDisc(int slot) { Lua_Safe_Call_Void(); self->UntrainDisc(slot); @@ -1352,6 +1357,7 @@ luabind::scope lua_register_client() { .def("UnscribeSpell", (void(Lua_Client::*)(int,bool))&Lua_Client::UnscribeSpell) .def("UnscribeSpellAll", (void(Lua_Client::*)(void))&Lua_Client::UnscribeSpellAll) .def("UnscribeSpellAll", (void(Lua_Client::*)(bool))&Lua_Client::UnscribeSpellAll) + .def("TrainDisc", (void(Lua_Client::*)(int))&Lua_Client::TrainDisc) .def("UntrainDisc", (void(Lua_Client::*)(int))&Lua_Client::UntrainDisc) .def("UntrainDisc", (void(Lua_Client::*)(int,bool))&Lua_Client::UntrainDisc) .def("UntrainDiscAll", (void(Lua_Client::*)(void))&Lua_Client::UntrainDiscAll) diff --git a/zone/lua_client.h b/zone/lua_client.h index 4aedd0df5..8809779e9 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -138,6 +138,7 @@ public: void UnscribeSpell(int slot, bool update_client); void UnscribeSpellAll(); void UnscribeSpellAll(bool update_client); + void TrainDisc(int itemid); void UntrainDisc(int slot); void UntrainDisc(int slot, bool update_client); void UntrainDiscAll(); diff --git a/zone/net.cpp b/zone/net.cpp index 5c1f95e16..d870ee550 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -336,8 +336,10 @@ int main(int argc, char** argv) { bool worldwasconnected = worldserver.Connected(); EQStream* eqss; EQStreamInterface *eqsi; - Timer temp_timer(10); - temp_timer.Start(); + uint8 IDLEZONEUPDATE = 200; + uint8 ZONEUPDATE = 10; + Timer zoneupdate_timer(ZONEUPDATE); + zoneupdate_timer.Start(); while(RunLoops) { { //profiler block to omit the sleep from times @@ -381,13 +383,12 @@ int main(int argc, char** argv) { entity_list.AddClient(client); } - uint8 IDLEZONETIME = 200; - if ( numclients < 1 && temp_timer.GetDuration() != IDLEZONETIME ) - temp_timer.SetTimer(IDLEZONETIME); - else if ( numclients > 0 && temp_timer.GetDuration() == IDLEZONETIME ) + if ( numclients < 1 && zoneupdate_timer.GetDuration() != IDLEZONEUPDATE ) + zoneupdate_timer.SetTimer(IDLEZONEUPDATE); + else if ( numclients > 0 && zoneupdate_timer.GetDuration() == IDLEZONEUPDATE ) { - temp_timer.SetTimer(10); - temp_timer.Trigger(); + zoneupdate_timer.SetTimer(ZONEUPDATE); + zoneupdate_timer.Trigger(); } //check for timeouts in other threads @@ -402,7 +403,7 @@ int main(int argc, char** argv) { worldwasconnected = false; } - if (ZoneLoaded && temp_timer.Check()) { + if (ZoneLoaded && zoneupdate_timer.Check()) { { if(net.group_timer.Enabled() && net.group_timer.Check()) entity_list.GroupProcess(); From cb633e4b6ad6eb92bf4043eda8bf606b7b3fe82e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 28 Feb 2014 02:25:17 -0500 Subject: [PATCH 28/91] Crit Spells to StringIDs Some minor clean up Fix an mlog message --- zone/StringIDs.h | 2 ++ zone/effects.cpp | 56 +++++++++++++++++++++++++----------------------- zone/merc.cpp | 3 ++- zone/spells.cpp | 3 ++- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/zone/StringIDs.h b/zone/StringIDs.h index fe91ed76e..a09563274 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -125,6 +125,7 @@ #define YOU_ARE_PROTECTED 424 //%1 tries to cast a spell on you, but you are protected. #define TARGET_RESISTED 425 //Your target resisted the %1 spell. #define YOU_RESIST 426 //You resist the %1 spell! +#define YOU_CRIT_BLAST 428 //You deliver a critical blast! (%1) #define SUMMONING_CORPSE 429 //Summoning your corpse. #define SUMMONING_CORPSE_OTHER 430 //Summoning %1's corpse. #define MISSING_SPELL_COMP_ITEM 433 //You are missing %1. @@ -168,6 +169,7 @@ #define OTHER_REGAIN_CAST 1033 //%1 regains concentration and continues casting. #define GENERIC_SHOUT 1034 //%1 shouts '%2' #define GENERIC_EMOTE 1036 //%1 %2 +#define OTHER_CRIT_BLAST 1040 //%1 delivers a critical blast! (%2) #define NPC_ENRAGE_START 1042 //%1 has become ENRAGED. #define NPC_ENRAGE_END 1043 //%1 is no longer enraged. #define NPC_RAMPAGE 1044 //%1 goes on a RAMPAGE! diff --git a/zone/effects.cpp b/zone/effects.cpp index 3c11b41d5..39ee1ab18 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -62,7 +62,7 @@ int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { - + if (spells[spell_id].targettype == ST_Self) return value; @@ -74,24 +74,24 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) value -= (GetLevel() - 40) * 20; - + //This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch. if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch - + int chance = RuleI(Spells, BaseCritChance); chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation; - + if (chance > 0){ - + int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. //Improved Harm Touch is a guaranteed crit if you have at least one level of SCF. if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0)) chance = 100; - + if (MakeRandomInt(1,100) <= chance){ Critical = true; ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; @@ -104,53 +104,55 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } ratio += RuleI(Spells, WizCritRatio); //Default is zero - + if (Critical){ - value = value_BaseEffect*ratio/100; + value = value_BaseEffect*ratio/100; - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; + value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; if (target) { - value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); + value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; + value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100; + value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100; - value -= GetFocusEffect(focusFcDamageAmt, spell_id); + value -= GetFocusEffect(focusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; - entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), -value); + entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, + OTHER_CRIT_BLAST, GetName(), itoa(-value)); + Message_StringID(MT_SpellCrits, YOU_CRIT_BLAST, itoa(-value)); return value; } } - value = value_BaseEffect; - - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; - - value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; + value = value_BaseEffect; - if (target) { + value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; + + value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; + + if (target) { value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); - } + value -= target->GetFcDamageAmtIncoming(this, spell_id); + } - value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id); + value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id); + + value -= GetFocusEffect(focusFcDamageAmt, spell_id); - value -= GetFocusEffect(focusFcDamageAmt, spell_id); - if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); return value; - } +} int32 Client::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { diff --git a/zone/merc.cpp b/zone/merc.cpp index ef5818b6d..c8c8da6fa 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -2824,7 +2824,8 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value = (value * GetSpellScale() / 100); - entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), -value); + entity_list.MessageClose_StringID(this, false, 100, MT_SpellCrits, + OTHER_CRIT_BLAST, GetName(), itoa(-value)); return value; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 6fd687c18..855b292d5 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -445,7 +445,8 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, casting_spell_resist_adjust = resist_adjust; - mlog(SPELLS__CASTING, "Spell %d: Casting time %d (orig %d), mana cost %d", orgcasttime, cast_time, mana_cost); + mlog(SPELLS__CASTING, "Spell %d: Casting time %d (orig %d), mana cost %d", + spell_id, cast_time, orgcasttime, mana_cost); // cast time is 0, just finish it right now and be done with it if(cast_time == 0) { From c030cfe0ad00f18e62885e04724540203306d656 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 28 Feb 2014 02:56:36 -0500 Subject: [PATCH 29/91] Fix zone crash with TryWeaponProc --- zone/attack.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index db8e5bf6c..0b59632b1 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4080,7 +4080,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on } } - if (!proced) { + if (!proced && inst) { for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) { const ItemInst *aug_i = inst->GetAugment(r); if (!aug_i) // no aug, try next slot! @@ -4098,12 +4098,10 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on Mob *own = GetOwner(); if (own) own->Message_StringID(13, PROC_PETTOOLOW); - } - else { + } else { Message_StringID(13, PROC_TOOLOW); } - } - else { + } else { ExecWeaponProc(aug_i, aug->Proc.Effect, on); break; } From d525d23217f922e654da3387bf72cf605301782e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 28 Feb 2014 02:57:43 -0500 Subject: [PATCH 30/91] Shielding now uses StringIDs --- zone/StringIDs.h | 3 +++ zone/client_packet.cpp | 8 +++++--- zone/client_process.cpp | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/zone/StringIDs.h b/zone/StringIDs.h index a09563274..5609ec2cd 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -224,6 +224,9 @@ #define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.' #define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets. #define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first. +#define ALREADY_SHIELDED 3279 //Either you or your target is already being shielded. +#define START_SHIELDING 3281 //%1 begins to use %2 as a living shield! +#define END_SHIELDING 3282 //%1 ceases protecting %2. #define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1. #define TRADESKILL_MISSING_COMPONENTS 3456 //Sorry, but you don't have everything you need for this recipe in your general inventory. #define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1! diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 1b3485ec8..f8d90f745 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1615,7 +1615,8 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app) if (shield_target) { - entity_list.MessageClose(this,false,100,0,"%s ceases shielding %s.",GetName(),shield_target->GetName()); + entity_list.MessageClose_StringID(this, false, 100, 0, + END_SHIELDING, GetName(), shield_target->GetName()); for (int y = 0; y < 2; y++) { if (shield_target->shielder[y].shielder_id == GetID()) @@ -1640,7 +1641,8 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app) { if (shield_target->shielder[x].shielder_id == 0) { - entity_list.MessageClose(this,false,100,0,"%s uses their shield to guard %s.",GetName(),shield_target->GetName()); + entity_list.MessageClose_StringID(this ,false, 100, 0, + START_SHIELDING, GetName(), shield_target->GetName()); shield_target->shielder[x].shielder_id = GetID(); int shieldbonus = shield->AC*2; switch (GetAA(197)) @@ -1677,7 +1679,7 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app) } if (!ack) { - Message(0, "No more than two warriors may shield the same being."); + Message_StringID(0, ALREADY_SHIELDED); shield_target = 0; return; } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index dad18843d..f1e25226a 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -581,7 +581,8 @@ bool Client::Process() { { if (!CombatRange(shield_target)) { - entity_list.MessageClose(this,false,100,0,"%s ceases shielding %s.",GetCleanName(),shield_target->GetCleanName()); + entity_list.MessageClose_StringID(this, false, 100, 0, + END_SHIELDING, GetCleanName(), shield_target->GetCleanName()); for (int y = 0; y < 2; y++) { if (shield_target->shielder[y].shielder_id == GetID()) From 5c7484cea26376441c2959a440f0a4aaae3b4ef4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 28 Feb 2014 17:43:37 -0500 Subject: [PATCH 31/91] Switched some heal messages to StringIDs --- zone/StringIDs.h | 2 ++ zone/attack.cpp | 20 ++++++------------- zone/effects.cpp | 51 +++++++++++++++++++++++++----------------------- 3 files changed, 35 insertions(+), 38 deletions(-) diff --git a/zone/StringIDs.h b/zone/StringIDs.h index 5609ec2cd..2cc076b59 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -125,6 +125,7 @@ #define YOU_ARE_PROTECTED 424 //%1 tries to cast a spell on you, but you are protected. #define TARGET_RESISTED 425 //Your target resisted the %1 spell. #define YOU_RESIST 426 //You resist the %1 spell! +#define YOU_CRIT_HEAL 427 //You perform an exceptional heal! (%1) #define YOU_CRIT_BLAST 428 //You deliver a critical blast! (%1) #define SUMMONING_CORPSE 429 //Summoning your corpse. #define SUMMONING_CORPSE_OTHER 430 //Summoning %1's corpse. @@ -169,6 +170,7 @@ #define OTHER_REGAIN_CAST 1033 //%1 regains concentration and continues casting. #define GENERIC_SHOUT 1034 //%1 shouts '%2' #define GENERIC_EMOTE 1036 //%1 %2 +#define OTHER_CRIT_HEAL 1039 //%1 performs an exceptional heal! (%2) #define OTHER_CRIT_BLAST 1040 //%1 delivers a critical blast! (%2) #define NPC_ENRAGE_START 1042 //%1 has become ENRAGED. #define NPC_ENRAGE_END 1043 //%1 is no longer enraged. diff --git a/zone/attack.cpp b/zone/attack.cpp index 0b59632b1..52dd97196 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3851,24 +3851,17 @@ void Mob::HealDamage(uint32 amount, Mob* caster) { } } - if(amount > (maxhp - curhp)) acthealed = (maxhp - curhp); else acthealed = amount; - char *TempString = nullptr; - - MakeAnyLenString(&TempString, "%d", acthealed); - - if(acthealed > 100){ - if(caster){ - Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), TempString); - if(caster != this){ - caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), TempString); - } - } - else{ + 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)); + } else { Message(MT_NonMelee, "You have been healed for %d points of damage.", acthealed); } } @@ -3882,7 +3875,6 @@ void Mob::HealDamage(uint32 amount, Mob* caster) { SendHPUpdate(); } - safe_delete_array(TempString); } //proc chance includes proc bonus diff --git a/zone/effects.cpp b/zone/effects.cpp index 39ee1ab18..c5e9dc20a 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -259,7 +259,7 @@ int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { } int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - + if (target == nullptr) target = this; @@ -267,54 +267,57 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int16 chance = 0; int8 modifier = 1; bool Critical = false; - - value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); - + + value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); + value = value_BaseEffect; - value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100); - + value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100); + // Instant Heals if(spells[spell_id].buffduration < 1) { - chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - if (spellbonuses.CriticalHealDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); - + chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); + if(chance && (MakeRandomInt(0,99) < chance)) { Critical = true; modifier = 2; //At present time no critical heal amount modifier SPA exists. } - + value *= modifier; - value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier; - value += GetFocusEffect(focusFcHealAmt, spell_id); - value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); - + value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier; + value += GetFocusEffect(focusFcHealAmt, spell_id); + value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); + if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; - value += value*target->GetHealRate(spell_id, this)/100; + value += value*target->GetHealRate(spell_id, this)/100; - if (Critical) - entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value); + if (Critical) { + entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, + OTHER_CRIT_HEAL, GetName(), itoa(value)); + Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value)); + } return value; } //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] else { - - chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - + chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; + + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); + if (spellbonuses.CriticalRegenDecay) chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - + if(chance && (MakeRandomInt(0,99) < chance)) return (value * 2); } From d46912fcdfe1050e7dcf835ac472e55fa1d982bb Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 28 Feb 2014 22:06:10 -0500 Subject: [PATCH 32/91] Fix issue with mob procing --- zone/attack.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 52dd97196..3701e249d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2004,6 +2004,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool if(!bRiposte && other->GetHP() > 0 ) { TryWeaponProc(nullptr, weapon, other, Hand); //no weapon + TrySpellProc(nullptr, weapon, other, Hand); } TriggerDefensiveProcs(nullptr, other, Hand, damage); @@ -4131,7 +4132,7 @@ void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, } for (uint32 i = 0; i < MAX_PROCS; i++) { - if (IsPet() && hand == 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) + if (IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) continue; // If pets ever can proc from off hand, this will need to change // Not ranged From e0edd7a29078050220e9b1d607c601bb5a4a5d56 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 1 Mar 2014 00:16:37 -0500 Subject: [PATCH 33/91] Added FilteredMessage functions that better use ServerFilters These functions are currently unused, but should work nicely. I didn't add overloads to separate them from the others for now --- zone/client.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ zone/client.h | 8 +++++ zone/entity.cpp | 35 ++++++++++++++++++ zone/entity.h | 2 ++ 4 files changed, 140 insertions(+) diff --git a/zone/client.cpp b/zone/client.cpp index 6cf490960..cc79233ce 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2963,6 +2963,101 @@ void Client::Message_StringID(uint32 type, uint32 string_id, const char* message safe_delete(outapp); } +// helper function, returns true if we should see the message +bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter) +{ + eqFilterMode mode = GetFilter(filter); + // easy ones first + if (mode == FilterShow) + return true; + else if (mode == FilterHide) + return false; + + if (!sender && mode == FilterHide) { + return false; + } else if (sender) { + if (this == sender) { + if (mode == FilterHide) // don't need to check others + return false; + } else if (mode == FilterShowSelfOnly) { // we know sender isn't us + return false; + } else if (mode == FilterShowGroupOnly) { + Group *g = GetGroup(); + if (!g || !g->IsGroupMember(sender)) + return false; + } + } + + // we passed our checks + return true; +} + +void Client::FilteredMessage_StringID(Mob *sender, uint32 type, + eqFilterType filter, uint32 string_id) +{ + if (!FilteredMessageCheck(sender, filter)) + return; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage, 12); + SimpleMessage_Struct *sms = (SimpleMessage_Struct *)outapp->pBuffer; + sms->color = type; + sms->string_id = string_id; + + sms->unknown8 = 0; + + QueuePacket(outapp); + safe_delete(outapp); + + return; +} + +void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id, + const char *message1, const char *message2, const char *message3, + const char *message4, const char *message5, const char *message6, + const char *message7, const char *message8, const char *message9) +{ + if (!FilteredMessageCheck(sender, filter)) + return; + + int i, argcount, length; + char *bufptr; + const char *message_arg[9] = {0}; + + if (type == MT_Emote) + type = 4; + + if (!message1) { + Message_StringID(type, string_id); // use the simple message instead + return; + } + + i = 0; + message_arg[i++] = message1; + message_arg[i++] = message2; + message_arg[i++] = message3; + message_arg[i++] = message4; + message_arg[i++] = message5; + message_arg[i++] = message6; + message_arg[i++] = message7; + message_arg[i++] = message8; + message_arg[i++] = message9; + + for (argcount = length = 0; message_arg[argcount]; argcount++) + length += strlen(message_arg[argcount]) + 1; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_FormattedMessage, length+13); + FormattedMessage_Struct *fm = (FormattedMessage_Struct *)outapp->pBuffer; + fm->string_id = string_id; + fm->type = type; + bufptr = fm->message; + for (i = 0; i < argcount; i++) { + strcpy(bufptr, message_arg[i]); + bufptr += strlen(message_arg[i]) + 1; + } + + QueuePacket(outapp); + safe_delete(outapp); +} void Client::SetTint(int16 in_slot, uint32 color) { Color_Struct new_color; diff --git a/zone/client.h b/zone/client.h index be1c29c1c..10465dbc6 100644 --- a/zone/client.h +++ b/zone/client.h @@ -250,6 +250,14 @@ public: uint8 SlotConvert(uint8 slot,bool bracer=false); void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0); void Message_StringID(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0); + bool FilteredMessageCheck(Mob *sender, eqFilterType filter); + void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id); + void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, + uint32 string_id, const char *message1, const char *message2 = nullptr, + const char *message3 = nullptr, const char *message4 = nullptr, + const char *message5 = nullptr, const char *message6 = nullptr, + const char *message7 = nullptr, const char *message8 = nullptr, + const char *message9 = nullptr); void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice); void SendTraderItem(uint32 item_id,uint16 quantity); uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity); diff --git a/zone/entity.cpp b/zone/entity.cpp index 64bbe53c1..c42ebc685 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1907,6 +1907,24 @@ void EntityList::MessageClose_StringID(Mob *sender, bool skipsender, float dist, } } +void EntityList::FilteredMessageClose_StringID(Mob *sender, bool skipsender, + float dist, uint32 type, eqFilterType filter, uint32 string_id, + const char *message1, const char *message2, const char *message3, + const char *message4, const char *message5, const char *message6, + const char *message7, const char *message8, const char *message9) +{ + Client *c; + float dist2 = dist * dist; + + for (auto it = client_list.begin(); it != client_list.end(); ++it) { + c = it->second; + if (c && c->DistNoRoot(*sender) <= dist2 && (!skipsender || c != sender)) + c->FilteredMessage_StringID(sender, type, filter, string_id, + message1, message2, message3, message4, message5, + message6, message7, message8, message9); + } +} + void EntityList::Message_StringID(Mob *sender, bool skipsender, uint32 type, uint32 string_id, const char* message1,const char* message2,const char* message3,const char* message4,const char* message5,const char* message6,const char* message7,const char* message8,const char* message9) { Client *c; @@ -1918,6 +1936,23 @@ void EntityList::Message_StringID(Mob *sender, bool skipsender, uint32 type, uin } } +void EntityList::FilteredMessage_StringID(Mob *sender, bool skipsender, + uint32 type, eqFilterType filter, uint32 string_id, + const char *message1, const char *message2, const char *message3, + const char *message4, const char *message5, const char *message6, + const char *message7, const char *message8, const char *message9) +{ + Client *c; + + for (auto it = client_list.begin(); it != client_list.end(); ++it) { + c = it->second; + if (c && (!skipsender || c != sender)) + c->FilteredMessage_StringID(sender, type, filter, string_id, + message1, message2, message3, message4, message5, message6, + message7, message8, message9); + } +} + void EntityList::MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...) { va_list argptr; diff --git a/zone/entity.h b/zone/entity.h index a42e63f9b..ede4b3bea 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -267,7 +267,9 @@ public: void MessageStatus(uint32 to_guilddbid, int to_minstatus, uint32 type, const char* message, ...); void MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...); void Message_StringID(Mob *sender, bool skipsender, uint32 type, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0); + void FilteredMessage_StringID(Mob *sender, bool skipsender, uint32 type, eqFilterType filter, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0); void MessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0); + void FilteredMessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, eqFilterType filter, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0); void ChannelMessageFromWorld(const char* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, const char* message); void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, const char* message, ...); void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...); From be57c66256583531d4e2e18342971cdc5dc9058e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 1 Mar 2014 14:02:57 -0500 Subject: [PATCH 34/91] Add FilteredMessage do nothing mob virtuals --- zone/mob.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zone/mob.h b/zone/mob.h index 9b5b353eb..ca80500d5 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -524,6 +524,13 @@ public: virtual void Message_StringID(uint32 type, uint32 string_id, const char* message, const char* message2 = 0, const char* message3 = 0, const char* message4 = 0, const char* message5 = 0, const char* message6 = 0, const char* message7 = 0, const char* message8 = 0, const char* message9 = 0, uint32 distance = 0) { } + virtual void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id) { } + virtual void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, + uint32 string_id, const char *message1, const char *message2 = nullptr, + const char *message3 = nullptr, const char *message4 = nullptr, + const char *message5 = nullptr, const char *message6 = nullptr, + const char *message7 = nullptr, const char *message8 = nullptr, + const char *message9 = nullptr) { } void Say(const char *format, ...); void Say_StringID(uint32 string_id, const char *message3 = 0, const char *message4 = 0, const char *message5 = 0, const char *message6 = 0, const char *message7 = 0, const char *message8 = 0, const char *message9 = 0); From 7133357b1a92dfc9d185e0b8df81035be49d6d01 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 2 Mar 2014 03:46:51 -0500 Subject: [PATCH 35/91] 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 36/91] 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 37/91] 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 38/91] 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 39/91] 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 40/91] 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 41/91] 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 42/91] 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 43/91] 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 44/91] 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 45/91] 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 46/91] 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 47/91] 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 48/91] 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 49/91] 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 50/91] 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 51/91] 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 52/91] 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 53/91] 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 54/91] 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 55/91] 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 56/91] 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 57/91] 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 58/91] 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 59/91] 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 60/91] 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 61/91] 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 62/91] 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 63/91] 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 64/91] ** 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 65/91] 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 66/91] 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 67/91] 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 68/91] 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 69/91] 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 70/91] 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 71/91] 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 72/91] 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 73/91] 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 74/91] 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 75/91] 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 76/91] 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 77/91] -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 4b14ec53f1f2f242e28907713103b04cbf3300f9 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 4 Apr 2014 01:59:55 -0400 Subject: [PATCH 78/91] 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 79/91] 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 80/91] 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 81/91] 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 82/91] 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 83/91] (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 35fad4d5a71435525a31172d9b29f0f8f89ed519 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Sat, 5 Apr 2014 17:18:19 -0400 Subject: [PATCH 84/91] 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 85/91] 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 86/91] 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 87/91] 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 88/91] 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 89/91] 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 90/91] 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 91/91] 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; }