diff --git a/changelog.txt b/changelog.txt index 0fd73aeba..e5f887e2d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/10/2015 == +Kayen: Updated mechanics to be consistent with live regarding how invisible breaks when the client is the target of a spell. +Invisible will drop whenever a client is hit with a detrimental spell, regardless of if resisted, if it does damage or AOE. +Hide skill now also follows the same rules as above. +Implemented support for Rogue AA - Nerves of Steel which gives a chance for hide NOT to break +when client is hit with an AOE spell. + == 09/25/2015 == Uleat: Implemented 'Inventory Snapshot' feature to track online player inventories at timed intervals. rules: diff --git a/common/spdat.h b/common/spdat.h index 41fd35f1f..6ea3033b0 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -478,7 +478,7 @@ typedef enum { #define SE_GateToHomeCity 322 // implemented #define SE_DefensiveProc 323 // implemented #define SE_HPToMana 324 // implemented -//#define SE_NoBreakAESneak 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_NoBreakAESneak 325 // 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 diff --git a/zone/attack.cpp b/zone/attack.cpp index 2f94716bf..2752752aa 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1382,7 +1382,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b if (damage > 0 && HasSkillProcSuccess() && other && other->GetHP() > 0) TrySkillProc(other, skillinuse, 0, true, Hand); - CommonBreakInvisible(); + CommonBreakInvisibleFromCombat(); if(GetTarget()) TriggerDefensiveProcs(weapon, other, Hand, damage); @@ -1940,7 +1940,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool MeleeLifeTap(damage); - CommonBreakInvisible(); + CommonBreakInvisibleFromCombat(); //I doubt this works... if (!GetTarget()) @@ -4487,7 +4487,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, int32 &damage, SkillUseTypes s CheckNumHitsRemaining(NumHit::OutgoingHitSuccess); } -void Mob::CommonBreakInvisible() +void Mob::CommonBreakInvisibleFromCombat() { //break invis when you attack if(invisible) { diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index ab7d8bfee..8038e4a24 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1452,6 +1452,11 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->TradeSkillMastery = base1; break; + case SE_NoBreakAESneak: + if (newbon->NoBreakAESneak < base1) + newbon->NoBreakAESneak = base1; + break; + // to do case SE_PetDiscipline: break; @@ -3188,6 +3193,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->RaiseSkillCap[base2] = effect_value; break; } + + case SE_NoBreakAESneak: + if (new_bonus->NoBreakAESneak < effect_value) + new_bonus->NoBreakAESneak = effect_value; + break; //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { diff --git a/zone/client.h b/zone/client.h index 885eb1407..f7527319c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -878,6 +878,7 @@ public: void SetFilter(eqFilterType filter_id, eqFilterMode value) { ClientFilters[filter_id]=value; } void BreakInvis(); + void BreakSneakWhenCastOn(Mob* caster, bool IsResisted); void LeaveGroup(); bool Hungry() const {if (GetGM()) return false; return m_pp.hunger_level <= 3000;} diff --git a/zone/common.h b/zone/common.h index 88aa58264..68accf746 100644 --- a/zone/common.h +++ b/zone/common.h @@ -471,6 +471,7 @@ struct StatBonuses { uint16 ReduceFallDamage; // reduce fall damage by percent int32 ReduceTradeskillFail[HIGHEST_SKILL+1]; // Reduces chance for trade skills to fail by percent. uint8 TradeSkillMastery; // Allow number of tradeskills to exceed 200 skill. + int16 NoBreakAESneak; // Percent value }; typedef struct diff --git a/zone/mob.h b/zone/mob.h index e48eda680..cb5948241 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -164,7 +164,8 @@ public: virtual inline bool IsBerserk() { return false; } // only clients void RogueEvade(Mob *other); void CommonOutgoingHitSuccess(Mob* defender, int32 &damage, SkillUseTypes skillInUse); - void CommonBreakInvisible(); + void BreakInvisibleSpells(); + void CommonBreakInvisibleFromCombat(); bool HasDied(); virtual bool CheckDualWield(); void DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr, int special = 0); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 1a134d167..c43c8f432 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -805,7 +805,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { } CheckIncreaseSkill(SkillArchery, GetTarget(), -15); - CommonBreakInvisible(); + CommonBreakInvisibleFromCombat(); } void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, @@ -1237,7 +1237,7 @@ void NPC::RangedAttack(Mob* other) DoRangedAttackDmg(other); - CommonBreakInvisible(); + CommonBreakInvisibleFromCombat(); } } @@ -1433,7 +1433,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 //consume ammo DeleteItemInInventory(ammo_slot, 1, true); CheckIncreaseSkill(SkillThrowing, GetTarget()); - CommonBreakInvisible(); + CommonBreakInvisibleFromCombat(); } void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* AmmoItem, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime, uint32 range_id, int AmmoSlot, float speed) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 8d3b252ad..a57be6351 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6707,3 +6707,67 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) SetSpellPowerDistanceMod(static_cast(mod)); } } + +void Mob::BreakInvisibleSpells() +{ + if(invisible) { + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if(invisible_undead) { + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if(invisible_animals){ + BuffFadeByEffect(SE_InvisVsAnimals); + invisible_animals = false; + } +} + +void Client::BreakSneakWhenCastOn(Mob* caster, bool IsResisted) +{ + bool IsCastersTarget = false; //Chance to avoid only applies to AOE spells when not targeted. + if(hidden || improved_hidden){ + + if (caster){ + Mob* target = nullptr; + target = caster->GetTarget(); + if (target && target == this){ + IsCastersTarget = true; + } + } + + if (!IsCastersTarget){ + + int chance = spellbonuses.NoBreakAESneak + itembonuses.NoBreakAESneak + aabonuses.NoBreakAESneak; + + if (IsResisted) + chance *= 2; + + if(chance && (zone->random.Roll(chance))) + return; // Do not drop Sneak/Hide + } + + //TODO: The skill buttons should reset when this occurs. Not sure how to force that yet. - Kayen + hidden = false; + improved_hidden = false; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 0; + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + + Message_StringID(MT_Skills,NO_LONGER_HIDDEN); + + //Sneaking alone will not be disabled from spells, only hide+sneak. + if (sneaking){ + sneaking = false; + SendAppearancePacket(AT_Sneak, 0); + Message_StringID(MT_Skills,STOP_SNEAKING); + } + } +} \ No newline at end of file diff --git a/zone/spells.cpp b/zone/spells.cpp index 999ab684d..f5a0ffd0d 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3679,6 +3679,8 @@ 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)) { + spelltar->BreakInvisibleSpells(); //Any detrimental spell cast on you will drop invisible (can be AOE, non damage ect). + 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, false, false, level_override); else @@ -3712,6 +3714,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r } } + if (spelltar->IsClient()) + spelltar->CastToClient()->BreakSneakWhenCastOn(this, true); + spelltar->CheckNumHitsRemaining(NumHit::IncomingSpells); CheckNumHitsRemaining(NumHit::OutgoingSpells); @@ -3719,6 +3724,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r return false; } } + if (spelltar->IsClient()){ + spelltar->CastToClient()->BreakSneakWhenCastOn(this, false); + } } else { diff --git a/zone/string_ids.h b/zone/string_ids.h index 19d7436d0..44d5924be 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -384,6 +384,8 @@ #define TARGET_PLAYER_FOR_GUILD_STATUS 12260 #define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite to invite someone to your group. #define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself. +#define NO_LONGER_HIDDEN 12337 //You are no longer hidden. +#define STOP_SNEAKING 12338 //You stop sneaking #define NOT_IN_CONTROL 12368 //You do not have control of yourself right now. #define ALREADY_CASTING 12442 //You are already casting a spell! #define SHIMMERS_BRIEFLY 12444 //Your %1 shimmers briefly.