[Spells] Invisibility updates and rework (#1991)

* updates pre merge

* update

* Update spell_effects.cpp

* Update mob.h

* test

* test

* updates

* updates

* save

* update

* working solid

* animal and undead start

* progress

* updates

* rename

* set invis appearance on bonus

* remove fade buff state check

* update IsViisble check

* optimizing

* don't break bots

* debug remover

* Update ruletypes.h

* perl adds

* Update client_packet.cpp

* update

* done

* remove debugs

* Update client_packet.cpp

* update

* [Spells] Invisibility updates and rework

lua support

* [Spells] Invisibility updates and rework

lua
This commit is contained in:
KayenEQ 2022-02-15 00:18:02 -05:00 committed by GitHub
parent 51c8771bd2
commit b938e6223c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 369 additions and 188 deletions

View File

@ -404,7 +404,7 @@ RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist chan
RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.")
RULE_BOOL(Spells, CazicTouchTargetsPetOwner, true, "If True, causes Cazic Touch to swap targets from pet to pet owner if a pet is tanking.")
RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts and dot removal on charm break to prevent faction wars.")
RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible")
RULE_BOOL(Spells, AllowDoubleInvis, true, "Allows you to cast invisibility spells on a player that is already invisible, live like behavior.")
RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls")
RULE_BOOL(Spells, InvisRequiresGroup, false, "Invis requires the the target to be in group.")
RULE_INT(Spells, ClericInnateHealFocus, 5, "Clerics on live get a 5 pct innate heal focus")

View File

@ -191,6 +191,8 @@
#define MAX_APPEARANCE_EFFECTS 20 //Up to 20 Appearance Effects can be saved to a mobs appearance effect array, these will be sent to other clients when they enter a zone (This is arbitrary)
#define MAX_CAST_ON_SKILL_USE 36 //Actual amount is MAX/3
#define MAX_INVISIBILTY_LEVEL 254
//instrument item id's used as song components
#define INSTRUMENT_HAND_DRUM 13000
#define INSTRUMENT_WOODEN_FLUTE 13001
@ -540,6 +542,13 @@ enum ReflectSpellType
RELFECT_ALL_SINGLE_TARGET_SPELLS = 3,
REFLECT_ALL_SPELLS = 4,
};
enum InvisType {
T_INVISIBLE = 0,
T_INVISIBLE_VERSE_UNDEAD = 1,
T_INVISIBLE_VERSE_ANIMAL = 2,
};
//For better organizing in proc effects, not used in spells.
enum ProcType
{
@ -1039,7 +1048,7 @@ typedef enum {
#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
//#define SE_ImprovedInvisAnimals 316 // not used
#define SE_ImprovedInvisAnimals 316 // implemented
#define SE_ItemHPRegenCapIncrease 317 // implemented[AA] - increases amount of health regen gained via items
#define SE_ItemManaRegenCapIncrease 318 // implemented - increases amount of mana regen you can gain via items
#define SE_CriticalHealOverTime 319 // implemented

View File

@ -5626,28 +5626,12 @@ void Mob::DoShieldDamageOnShielder(Mob *shield_target, int hit_damage_done, EQ::
void Mob::CommonBreakInvisibleFromCombat()
{
//break invis when you attack
if (invisible) {
LogCombat("Removing invisibility due to melee attack");
BuffFadeByEffect(SE_Invisibility);
BuffFadeByEffect(SE_Invisibility2);
invisible = false;
}
if (invisible_undead) {
LogCombat("Removing invisibility vs. undead due to melee attack");
BuffFadeByEffect(SE_InvisVsUndead);
BuffFadeByEffect(SE_InvisVsUndead2);
invisible_undead = false;
}
if (invisible_animals) {
LogCombat("Removing invisibility vs. animals due to melee attack");
BuffFadeByEffect(SE_InvisVsAnimals);
invisible_animals = false;
}
BreakInvisibleSpells();
CancelSneakHide();
if (spellbonuses.NegateIfCombat)
if (spellbonuses.NegateIfCombat) {
BuffFadeByEffect(SE_NegateIfCombat);
}
hidden = false;
improved_hidden = false;

View File

@ -47,6 +47,8 @@ void Mob::CalcBonuses()
CalcMaxMana();
SetAttackTimer();
CalcAC();
CalcSeeInvisibleLevel();
CalcInvisibleLevel();
/* Fast walking NPC's are prone to disappear into walls/hills
We set this here because NPC's can cast spells to change walkspeed/runspeed
@ -81,6 +83,9 @@ void Client::CalcBonuses()
CalcSpellBonuses(&spellbonuses);
CalcAABonuses(&aabonuses);
CalcSeeInvisibleLevel();
CalcInvisibleLevel();
ProcessItemCaps(); // caps that depend on spell/aa bonuses
RecalcWeight();
@ -867,7 +872,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
newbon->MaxBindWound += base_value;
break;
case SE_SeeInvis:
newbon->SeeInvis = base_value;
base_value = std::min({ base_value, MAX_INVISIBILTY_LEVEL });
if (newbon->SeeInvis < base_value) {
newbon->SeeInvis = base_value;
}
break;
case SE_BaseMovementSpeed:
newbon->BaseMovementSpeed += base_value;
@ -3825,6 +3833,32 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
break;
}
case SE_Invisibility:
case SE_Invisibility2:
effect_value = std::min({ effect_value, MAX_INVISIBILTY_LEVEL });
if (new_bonus->invisibility < effect_value)
new_bonus->invisibility = effect_value;
break;
case SE_InvisVsUndead:
case SE_InvisVsUndead2:
if (new_bonus->invisibility_verse_undead < effect_value)
new_bonus->invisibility_verse_undead = effect_value;
break;
case SE_InvisVsAnimals:
effect_value = std::min({ effect_value, MAX_INVISIBILTY_LEVEL });
if (new_bonus->invisibility_verse_animal < effect_value)
new_bonus->invisibility_verse_animal = effect_value;
break;
case SE_SeeInvis:
effect_value = std::min({ effect_value, MAX_INVISIBILTY_LEVEL });
if (new_bonus->SeeInvis < effect_value) {
new_bonus->SeeInvis = effect_value;
}
break;
case SE_ZoneSuspendMinion:
new_bonus->ZoneSuspendMinion = effect_value;
break;

View File

@ -7438,6 +7438,8 @@ void Bot::CalcBonuses() {
CalcSpellBonuses(&spellbonuses);
CalcAABonuses(&aabonuses);
SetAttackTimer();
CalcSeeInvisibleLevel();
CalcInvisibleLevel();
CalcATK();
CalcSTR();
CalcSTA();

View File

@ -109,7 +109,7 @@ public:
} AType;
static const int AilmentTypeCount = 5;
typedef enum InvisibilityType {
typedef enum InvisType {
IT_None = 0,
IT_Animal,
IT_Undead,

View File

@ -3454,7 +3454,6 @@ void Client::Escape()
{
entity_list.RemoveFromTargets(this, true);
SetInvisible(Invisibility::Invisible);
MessageString(Chat::Skills, ESCAPE);
}

View File

@ -682,8 +682,7 @@ void Client::CompleteConnect()
case SE_Invisibility2:
case SE_Invisibility:
{
invisible = true;
SendAppearancePacket(AT_Invis, 1);
SendAppearancePacket(AT_Invis, Invisibility::Invisible);
break;
}
case SE_Levitate:
@ -707,17 +706,6 @@ void Client::CompleteConnect()
}
break;
}
case SE_InvisVsUndead2:
case SE_InvisVsUndead:
{
invisible_undead = true;
break;
}
case SE_InvisVsAnimals:
{
invisible_animals = true;
break;
}
case SE_AddMeleeProc:
case SE_WeaponProc:
{
@ -3819,6 +3807,9 @@ void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app)
void Client::Handle_OP_Buff(const EQApplicationPacket *app)
{
/*
Note: if invisibility is on client, this will force it to drop.
*/
if (app->size != sizeof(SpellBuffPacket_Struct))
{
LogError("Size mismatch in OP_Buff. expected [{}] got [{}]", sizeof(SpellBuffPacket_Struct), app->size);
@ -3833,10 +3824,12 @@ void Client::Handle_OP_Buff(const EQApplicationPacket *app)
//something about IsDetrimentalSpell() crashes this portion of code..
//tbh we shouldn't use it anyway since this is a simple red vs blue buff check and
//isdetrimentalspell() is much more complex
if (spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].good_effect == 0)))
if (spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].good_effect == 0))) {
QueuePacket(app);
else
}
else {
BuffFadeBySpellID(spid);
}
return;
}
@ -3990,12 +3983,20 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
return;
}
if (invisible) {
ZeroInvisibleVars(InvisType::T_INVISIBLE);
BuffFadeByEffect(SE_Invisibility);
BuffFadeByEffect(SE_Invisibility2);
}
// Hack for broken RoF2 which allows casting after a zoned IVU/IVA
if (invisible_undead || invisible_animals) {
BuffFadeByEffect(SE_InvisVsAnimals);
if (invisible_undead) {
BuffFadeByEffect(SE_InvisVsUndead);
BuffFadeByEffect(SE_InvisVsUndead2);
BuffFadeByEffect(SE_Invisibility); // Included per JJ for completeness - client handles this one atm
}
if (invisible_animals) {
BuffFadeByEffect(SE_InvisVsAnimals);
BuffFadeByEffect(SE_ImprovedInvisAnimals);
}
CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer;
@ -4826,7 +4827,7 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app)
// this could be done better, but this is only called when you con so w/e
// Shroud of Stealth has a special message
if (improved_hidden && (!tmob->see_improved_hide && (tmob->see_invis || tmob->see_hide)))
if (improved_hidden && (!tmob->see_improved_hide && (tmob->SeeInvisible() || tmob->see_hide)))
MessageString(Chat::NPCQuestSay, SOS_KEEPS_HIDDEN);
// we are trying to hide but they can see us
else if ((invisible || invisible_undead || hidden || invisible_animals) && !IsInvisible(tmob))
@ -13565,7 +13566,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
}
return;
}
invisible = false;
ZeroInvisibleVars(InvisType::T_INVISIBLE);
hidden = false;
improved_hidden = false;
entity_list.QueueClients(this, app, true);

View File

@ -1163,9 +1163,9 @@ void Client::BreakInvis()
sa_out->parameter = 0;
entity_list.QueueClients(this, outapp, true);
safe_delete(outapp);
invisible = false;
invisible_undead = false;
invisible_animals = false;
ZeroInvisibleVars(InvisType::T_INVISIBLE);
ZeroInvisibleVars(InvisType::T_INVISIBLE_VERSE_UNDEAD);
ZeroInvisibleVars(InvisType::T_INVISIBLE_VERSE_ANIMAL);
hidden = false;
improved_hidden = false;
}

View File

@ -561,6 +561,9 @@ struct StatBonuses {
bool ZoneSuspendMinion; // base 1 allows suspended minions to zone
bool CompleteHealBuffBlocker; // Use in SPA 101 to prevent recast of complete heal from this effect till blocker buff is removed.
int32 Illusion; // illusion spell id
uint8 invisibility; // invisibility level
uint8 invisibility_verse_undead; // IVU level
uint8 invisibility_verse_animal; // IVA level
// AAs
int32 TrapCircumvention; // reduce chance to trigger a trap.

View File

@ -2118,7 +2118,7 @@ uint8 Lua_Mob::SeeInvisible() {
return self->SeeInvisible();
}
bool Lua_Mob::SeeInvisibleUndead() {
uint8 Lua_Mob::SeeInvisibleUndead() {
Lua_Safe_Call_Bool();
return self->SeeInvisibleUndead();
}
@ -2423,6 +2423,31 @@ Lua_NPC Lua_Mob::GetHateRandomNPC() {
return Lua_NPC(self->GetHateRandomNPC());
}
uint8 Lua_Mob::GetInvisibleLevel()
{
Lua_Safe_Call_Int();
return self->GetInvisibleLevel();
}
uint8 Lua_Mob::GetInvisibleUndeadLevel()
{
Lua_Safe_Call_Int();
return self->GetInvisibleUndeadLevel();
}
void Lua_Mob::SetSeeInvisibleLevel(uint8 invisible_level)
{
Lua_Safe_Call_Void();
self->SetInnateSeeInvisible(invisible_level);
self->CalcSeeInvisibleLevel();
}
void Lua_Mob::SetSeeInvisibleUndeadLevel(uint8 invisible_level)
{
Lua_Safe_Call_Void();
self->SetSeeInvisibleUndead(invisible_level);
}
luabind::scope lua_register_mob() {
return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
.def(luabind::constructor<>())
@ -2618,6 +2643,8 @@ luabind::scope lua_register_mob() {
.def("GetHelmTexture", &Lua_Mob::GetHelmTexture)
.def("GetHerosForgeModel", (int32(Lua_Mob::*)(uint8))&Lua_Mob::GetHerosForgeModel)
.def("GetINT", &Lua_Mob::GetINT)
.def("GetInvisibleLevel", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetInvisibleLevel)
.def("GetInvisibleUndeadLevel", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetInvisibleUndeadLevel)
.def("GetInvul", (bool(Lua_Mob::*)(void))&Lua_Mob::GetInvul)
.def("GetItemBonuses", &Lua_Mob::GetItemBonuses)
.def("GetItemHPBonuses", &Lua_Mob::GetItemHPBonuses)
@ -2763,7 +2790,9 @@ luabind::scope lua_register_mob() {
.def("SeeHide", (bool(Lua_Mob::*)(void))&Lua_Mob::SeeHide)
.def("SeeImprovedHide", (bool(Lua_Mob::*)(bool))&Lua_Mob::SeeImprovedHide)
.def("SeeInvisible", (uint8(Lua_Mob::*)(void))&Lua_Mob::SeeInvisible)
.def("SeeInvisibleUndead", (bool(Lua_Mob::*)(void))&Lua_Mob::SeeInvisibleUndead)
.def("SeeInvisibleUndead", (uint8(Lua_Mob::*)(void))&Lua_Mob::SeeInvisibleUndead)
.def("SetSeeInvisibleLevel", (void(Lua_Mob::*)(uint8))&Lua_Mob::SetSeeInvisibleLevel)
.def("SetSeeInvisibleUndeadLevel", (void(Lua_Mob::*)(uint8))&Lua_Mob::SetSeeInvisibleUndeadLevel)
.def("SendAppearanceEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,uint32,uint32))&Lua_Mob::SendAppearanceEffect)
.def("SendAppearanceEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,uint32,uint32,Lua_Client))&Lua_Mob::SendAppearanceEffect)
.def("SendBeginCast", &Lua_Mob::SendBeginCast)

View File

@ -86,6 +86,10 @@ public:
bool IsInvisible();
bool IsInvisible(Lua_Mob other);
void SetInvisible(int state);
uint8 GetInvisibleLevel();
uint8 GetInvisibleUndeadLevel();
void SetSeeInvisibleLevel(uint8 invisible_level);
void SetSeeInvisibleUndeadLevel(uint8 invisible_level);
bool FindBuff(int spell_id);
uint16 FindBuffBySlot(int slot);
uint32 BuffCount();
@ -406,7 +410,7 @@ public:
int CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite);
void SetPseudoRoot(bool in);
uint8 SeeInvisible();
bool SeeInvisibleUndead();
uint8 SeeInvisibleUndead();
bool SeeHide();
bool SeeImprovedHide();
uint8 GetNimbusEffect1();

View File

@ -81,8 +81,8 @@ Mob::Mob(
uint32 in_drakkin_details,
EQ::TintProfile in_armor_tint,
uint8 in_aa_title,
uint8 in_see_invis, // see through invis/ivu
uint8 in_see_invis_undead,
uint16 in_see_invis, // see through invis/ivu
uint16 in_see_invis_undead,
uint8 in_see_hide,
uint8 in_see_improved_hide,
int32 in_hp_regen,
@ -269,8 +269,8 @@ Mob::Mob(
maxlevel = in_maxlevel;
scalerate = in_scalerate;
invisible = 0;
invisible_undead = false;
invisible_animals = false;
invisible_undead = 0;
invisible_animals = 0;
sneaking = false;
hidden = false;
improved_hidden = false;
@ -440,10 +440,13 @@ Mob::Mob(
pStandingPetOrder = SPO_Follow;
pseudo_rooted = false;
see_invis = GetSeeInvisible(in_see_invis);
see_invis_undead = GetSeeInvisible(in_see_invis_undead);
see_hide = GetSeeInvisible(in_see_hide);
see_improved_hide = GetSeeInvisible(in_see_improved_hide);
nobuff_invisible = 0;
see_invis = 0;
innate_see_invis = GetSeeInvisibleLevelFromNPCStat(in_see_invis);
see_invis_undead = GetSeeInvisibleLevelFromNPCStat(in_see_invis_undead);
see_hide = GetSeeInvisibleLevelFromNPCStat(in_see_hide);
see_improved_hide = GetSeeInvisibleLevelFromNPCStat(in_see_improved_hide);
qglobal = in_qglobal != 0;
@ -584,10 +587,52 @@ uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) {
return(ANIM_STAND);
}
void Mob::SetInvisible(uint8 state)
void Mob::CalcSeeInvisibleLevel()
{
if (state != Invisibility::Special) {
invisible = state;
see_invis = std::max({ spellbonuses.SeeInvis, itembonuses.SeeInvis, aabonuses.SeeInvis, innate_see_invis });
}
void Mob::CalcInvisibleLevel()
{
bool is_invisible = invisible;
invisible = std::max({ spellbonuses.invisibility, nobuff_invisible });
invisible_undead = spellbonuses.invisibility_verse_undead;
invisible_animals = spellbonuses.invisibility_verse_animal;
if (!is_invisible && invisible) {
SetInvisible(Invisibility::Invisible, true);
return;
}
if (is_invisible && !invisible) {
SetInvisible(invisible, true);
return;
}
}
void Mob::SetInvisible(uint8 state, bool set_on_bonus_calc)
{
/*
If you set an NPC to invisible you will only be able to see it on
your client if your see invisible level is greater than equal to the invisible level.
Note, the clients spell file must match the servers see invisible level on the spell.
*/
if (state == Invisibility::Visible) {
SendAppearancePacket(AT_Invis, Invisibility::Visible);
ZeroInvisibleVars(InvisType::T_INVISIBLE);
}
else {
/*
if your setting invisible from a script, or escape/fading memories effect then
we use the internal invis variable which allows invisible without a buff on mob.
*/
if (!set_on_bonus_calc) {
nobuff_invisible = state;
CalcInvisibleLevel();
}
SendAppearancePacket(AT_Invis, invisible);
}
@ -597,35 +642,53 @@ void Mob::SetInvisible(uint8 state)
if (RuleB(Pets, LivelikeBreakCharmOnInvis) || IsInvisible(pet)) {
pet->BuffFadeByEffect(SE_Charm);
}
LogRules("Pets:LivelikeBreakCharmOnInvis for [{}] | Invis [{}] - Hidden [{}] - Shroud of Stealth [{}] - IVA [{}] - IVU [{}]", GetCleanName(), invisible, hidden, improved_hidden, invisible_animals, invisible_undead);
}
}
void Mob::ZeroInvisibleVars(uint8 invisible_type)
{
switch (invisible_type) {
case T_INVISIBLE:
invisible = 0;
nobuff_invisible = 0;
break;
case T_INVISIBLE_VERSE_UNDEAD:
invisible_undead = 0;
break;
case T_INVISIBLE_VERSE_ANIMAL:
invisible_animals = 0;
break;
}
}
//check to see if `this` is invisible to `other`
bool Mob::IsInvisible(Mob* other) const
{
if(!other)
if (!other) {
return(false);
uint8 SeeInvisBonus = 0;
if (IsClient())
SeeInvisBonus = aabonuses.SeeInvis;
}
//check regular invisibility
if (invisible && invisible > (other->SeeInvisible()))
if (invisible && (invisible > other->SeeInvisible())) {
return true;
}
//check invis vs. undead
if (other->GetBodyType() == BT_Undead || other->GetBodyType() == BT_SummonedUndead) {
if(invisible_undead && !other->SeeInvisibleUndead())
if (invisible_undead && (invisible_undead > other->SeeInvisibleUndead())) {
return true;
}
}
//check invis vs. animals...
//check invis vs. animals. //TODO: should we have a specific see invisible animal stat or this how live does it?
if (other->GetBodyType() == BT_Animal){
if(invisible_animals && !other->SeeInvisible())
if (invisible_animals && (invisible_animals > other->SeeInvisible())) {
return true;
}
}
if(hidden){
@ -642,8 +705,9 @@ bool Mob::IsInvisible(Mob* other) const
//handle sneaking
if(sneaking) {
if(BehindMob(other, GetX(), GetY()) )
if (BehindMob(other, GetX(), GetY())) {
return true;
}
}
return(false);
@ -6110,19 +6174,47 @@ float Mob::HeadingAngleToMob(float other_x, float other_y)
return CalculateHeadingAngleBetweenPositions(this_x, this_y, other_x, other_y);
}
bool Mob::GetSeeInvisible(uint8 see_invis)
uint8 Mob::GetSeeInvisibleLevelFromNPCStat(uint16 in_see_invis)
{
if(see_invis > 0)
{
if(see_invis == 1)
return true;
else
{
if (zone->random.Int(0, 99) < see_invis)
return true;
/*
Returns the NPC's see invisible level based on 'see_invs' value in npc_types.
1 = See Invs Level 1, 2-99 will gives a random roll to apply see invs level 1
100 = See Invs Level 2, where 101-199 gives a random roll to apply see invs 2, if fails get see invs 1
ect... for higher levels, 200,300 ect.
MAX 25499, which can give you level 254.
*/
//npc does not have see invis
if (!in_see_invis) {
return 0;
}
//npc has basic see invis
if (in_see_invis == 1) {
return 1;
}
//random chance to apply standard level 1 see invs
if (in_see_invis > 1 && in_see_invis < 100) {
if (zone->random.Int(0, 99) < in_see_invis) {
return 1;
}
}
return false;
//covers npcs with see invis levels beyond level 1, max calculated level allowed is 254
int see_invis_level = 1;
see_invis_level += (in_see_invis / 100);
int see_invis_chance = in_see_invis % 100;
//has enhanced see invis level
if (see_invis_chance == 0) {
return std::min(see_invis_level, MAX_INVISIBILTY_LEVEL);
}
//has chance for enhanced see invis level
if (zone->random.Int(0, 99) < see_invis_chance) {
return std::min(see_invis_level, MAX_INVISIBILTY_LEVEL);
}
//failed chance at attempted enhanced see invs level, use previous level.
return std::min((see_invis_level - 1), MAX_INVISIBILTY_LEVEL);
}
int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot)

View File

@ -147,8 +147,8 @@ public:
uint32 in_drakkin_details,
EQ::TintProfile in_armor_tint,
uint8 in_aa_title,
uint8 in_see_invis, // see through invis
uint8 in_see_invis_undead, // see through invis vs. undead
uint16 in_see_invis, // see through invis
uint16 in_see_invis_undead, // see through invis vs. undead
uint8 in_see_hide,
uint8 in_see_improved_hide,
int32 in_hp_regen,
@ -226,10 +226,6 @@ public:
virtual inline bool IsBerserk() { return false; } // only clients
void RogueEvade(Mob *other);
void CommonOutgoingHitSuccess(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts = nullptr);
void BreakInvisibleSpells();
virtual void CancelSneakHide();
void CommonBreakInvisible();
void CommonBreakInvisibleFromCombat();
bool HasDied();
virtual bool CheckDualWield();
void DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr);
@ -246,6 +242,39 @@ public:
return;
}
//Invisible
bool IsInvisible(Mob* other = 0) const;
void SetInvisible(uint8 state, bool set_on_bonus_calc = false);
void CalcSeeInvisibleLevel();
void CalcInvisibleLevel();
void ZeroInvisibleVars(uint8 invisible_type);
inline uint8 GetSeeInvisibleLevelFromNPCStat(uint16 in_see_invis);
void BreakInvisibleSpells();
virtual void CancelSneakHide();
void CommonBreakInvisible();
void CommonBreakInvisibleFromCombat();
inline uint8 GetInvisibleLevel() const { return invisible; }
inline uint8 GetInvisibleUndeadLevel() const { return invisible_undead; }
inline bool SeeHide() const { return see_hide; }
inline bool SeeImprovedHide() const { return see_improved_hide; }
inline uint8 SeeInvisibleUndead() const { return see_invis_undead; }
inline uint8 SeeInvisible() const { return see_invis; }
inline void SetInnateSeeInvisible(uint8 val) { innate_see_invis = val; }
inline void SetSeeInvisibleUndead(uint8 val) { see_invis_undead = val; }
uint32 tmHidden; // timestamp of hide, only valid while hidden == true
uint8 invisible, nobuff_invisible, invisible_undead, invisible_animals;
uint8 see_invis, innate_see_invis, see_invis_undead; //TODO: do we need a see_invis_animal ?
bool sneaking, hidden, improved_hidden;
bool see_hide, see_improved_hide;
/**
************************************************
* Appearance
@ -254,16 +283,8 @@ public:
EQ::InternalTextureProfile mob_texture_profile = {};
bool IsInvisible(Mob* other = 0) const;
EQ::skills::SkillType AttackAnimation(int Hand, const EQ::ItemInstance* weapon, EQ::skills::SkillType skillinuse = EQ::skills::Skill1HBlunt);
inline bool GetSeeInvisible(uint8 see_invis);
inline bool SeeHide() const { return see_hide; }
inline bool SeeImprovedHide() const { return see_improved_hide; }
inline bool SeeInvisibleUndead() const { return see_invis_undead; }
inline uint8 SeeInvisible() const { return see_invis; }
int32 GetTextureProfileMaterial(uint8 material_slot) const;
int32 GetTextureProfileColor(uint8 material_slot) const;
int32 GetTextureProfileHeroForgeModel(uint8 material_slot) const;
@ -282,7 +303,6 @@ public:
void SendLevelAppearance();
void SendStunAppearance();
void SendTargetable(bool on, Client *specific_target = nullptr);
void SetInvisible(uint8 state);
void SetMobTextureProfile(uint8 material_slot, uint16 texture, uint32 color = 0, uint32 hero_forge_model = 0);
//Spell
@ -974,10 +994,7 @@ public:
inline const bodyType GetOrigBodyType() const { return orig_bodytype; }
void SetBodyType(bodyType new_body, bool overwrite_orig);
uint32 tmHidden; // timestamp of hide, only valid while hidden == true
uint8 invisible, see_invis;
bool invulnerable, invisible_undead, invisible_animals, sneaking, hidden, improved_hidden;
bool see_invis_undead, see_hide, see_improved_hide;
bool invulnerable;
bool qglobal;
virtual void SetAttackTimer();

View File

@ -916,6 +916,35 @@ XS(XS_Mob_SetInvisible) {
XSRETURN_EMPTY;
}
XS(XS_Mob_SetSeeInvisibleLevel); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetSeeInvisibleLevel) {
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::SetSeeInvisibleLevel(THIS, uint8 see_invis_level)"); // @categories Script Utility
{
Mob *THIS;
uint8 see_invis_level = (uint8)SvUV(ST(1));
VALIDATE_THIS_IS_MOB;
THIS->SetInnateSeeInvisible(see_invis_level);
THIS->CalcSeeInvisibleLevel();
}
XSRETURN_EMPTY;
}
XS(XS_Mob_SetSeeInvisibleUndeadLevel); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetSeeInvisibleUndeadLevel) {
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::SetSeeInvisibleUndeadLevel(THIS, uint8 see_invis_undead_level)"); // @categories Script Utility
{
Mob *THIS;
uint8 see_invis_undead_level = (uint8)SvUV(ST(1));
VALIDATE_THIS_IS_MOB;
THIS->SetSeeInvisibleUndead(see_invis_undead_level);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_FindBuff); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_FindBuff) {
dXSARGS;
@ -5777,6 +5806,41 @@ XS(XS_Mob_IsBlind) {
XSRETURN(1);
}
XS(XS_Mob_GetInvisibleLevel);
XS(XS_Mob_GetInvisibleLevel) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: Mob::GetInvisibleLevel(THIS)"); // @categories Stats and Attributes
{
Mob *THIS;
uint8 RETVAL;
dXSTARG;
VALIDATE_THIS_IS_MOB;
RETVAL = THIS->GetInvisibleLevel();
XSprePUSH;
PUSHu((UV)RETVAL);
}
XSRETURN(1);
}
XS(XS_Mob_GetInvisibleUndeadLevel);
XS(XS_Mob_GetInvisibleUndeadLevel) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: Mob::GetInvisibleUndeadLevel(THIS)"); // @categories Stats and Attributes
{
Mob *THIS;
uint8 RETVAL;
dXSTARG;
VALIDATE_THIS_IS_MOB;
RETVAL = THIS->GetInvisibleUndeadLevel();
XSprePUSH;
PUSHu((UV)RETVAL);
}
XSRETURN(1);
}
XS(XS_Mob_SeeInvisible);
XS(XS_Mob_SeeInvisible) {
dXSARGS;
@ -5801,11 +5865,12 @@ XS(XS_Mob_SeeInvisibleUndead) {
Perl_croak(aTHX_ "Usage: Mob::SeeInvisibleUndead(THIS)"); // @categories Stats and Attributes
{
Mob *THIS;
bool RETVAL;
uint8 RETVAL;
dXSTARG;
VALIDATE_THIS_IS_MOB;
RETVAL = THIS->SeeInvisibleUndead();
ST(0) = boolSV(RETVAL);
sv_2mortal(ST(0));
XSprePUSH;
PUSHu((UV)RETVAL);
}
XSRETURN(1);
}
@ -6698,6 +6763,8 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "GetHerosForgeModel"), XS_Mob_GetHerosForgeModel, file, "$$");
newXSproto(strcpy(buf, "GetID"), XS_Mob_GetID, file, "$");
newXSproto(strcpy(buf, "GetINT"), XS_Mob_GetINT, file, "$");
newXSproto(strcpy(buf, "GetInvisibleLevel"), XS_Mob_GetInvisibleLevel, file, "$");
newXSproto(strcpy(buf, "GetInvisibleUndeadLevel"), XS_Mob_GetInvisibleUndeadLevel, file, "$");
newXSproto(strcpy(buf, "GetInvul"), XS_Mob_GetInvul, file, "$");
newXSproto(strcpy(buf, "GetItemHPBonuses"), XS_Mob_GetItemHPBonuses, file, "$");
newXSproto(strcpy(buf, "GetItemStat"), XS_Mob_GetItemStat, file, "$$$");
@ -6877,6 +6944,8 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "SetRace"), XS_Mob_SetRace, file, "$$");
newXSproto(strcpy(buf, "SetRunAnimSpeed"), XS_Mob_SetRunAnimSpeed, file, "$$");
newXSproto(strcpy(buf, "SetRunning"), XS_Mob_SetRunning, file, "$$");
newXSproto(strcpy(buf, "SetSeeInvisibleLevel"), XS_Mob_SetSeeInvisibleLevel, file, "$$");
newXSproto(strcpy(buf, "SetSeeInvisibleUndeadLevel"), XS_Mob_SetSeeInvisibleUndeadLevel, file, "$$");
newXSproto(strcpy(buf, "SetSlotTint"), XS_Mob_SetSlotTint, file, "$$$$$");
newXSproto(strcpy(buf, "SetSpecialAbility"), XS_Mob_SetSpecialAbility, file, "$$$");
newXSproto(strcpy(buf, "SetSpecialAbilityParam"), XS_Mob_SetSpecialAbilityParam, file, "$$$$");

View File

@ -583,45 +583,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break;
}
case SE_Invisibility:
case SE_Invisibility2:
{
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Invisibility");
#endif
SetInvisible(spell.base_value[i]);
break;
}
case SE_InvisVsAnimals:
{
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Invisibility to Animals");
#endif
invisible_animals = true;
SetInvisible(Invisibility::Special);
break;
}
case SE_InvisVsUndead2:
case SE_InvisVsUndead:
{
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Invisibility to Undead");
#endif
invisible_undead = true;
SetInvisible(Invisibility::Special);
break;
}
case SE_SeeInvis:
{
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "See Invisible");
#endif
see_invis = spell.base_value[i];
break;
}
case SE_FleshToBone:
{
#ifdef SPELL_EFFECT_SPAM
@ -3277,6 +3238,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_Proc_Timer_Modifier:
case SE_FFItemClass:
case SE_SpellEffectResistChance:
case SE_SeeInvis:
{
break;
}
@ -3946,10 +3908,13 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
}
}
}
case SE_ImprovedInvisAnimals:
case SE_Invisibility2:
case SE_InvisVsUndead2: {
if (buff.ticsremaining <= 3 && buff.ticsremaining > 1) {
MessageString(Chat::Spells, INVIS_BEGIN_BREAK);
if (!IsBardSong(buff.spellid)) {
if (buff.ticsremaining <= 3 && buff.ticsremaining > 1) {
MessageString(Chat::Spells, INVIS_BEGIN_BREAK);
}
}
break;
}
@ -4186,32 +4151,6 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
break;
}
case SE_Invisibility2:
case SE_Invisibility:
{
SetInvisible(Invisibility::Visible);
break;
}
case SE_InvisVsUndead2:
case SE_InvisVsUndead:
{
invisible_undead = false; // Mongrel: No longer IVU
break;
}
case SE_InvisVsAnimals:
{
invisible_animals = false;
break;
}
case SE_SeeInvis:
{
see_invis = 0;
break;
}
case SE_Silence:
{
Silence(false);
@ -9580,18 +9519,19 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster)
void Mob::BreakInvisibleSpells()
{
if(invisible) {
ZeroInvisibleVars(InvisType::T_INVISIBLE);
BuffFadeByEffect(SE_Invisibility);
BuffFadeByEffect(SE_Invisibility2);
invisible = false;
}
if(invisible_undead) {
ZeroInvisibleVars(InvisType::T_INVISIBLE_VERSE_UNDEAD);
BuffFadeByEffect(SE_InvisVsUndead);
BuffFadeByEffect(SE_InvisVsUndead2);
invisible_undead = false;
}
if(invisible_animals){
ZeroInvisibleVars(InvisType::T_INVISIBLE_VERSE_ANIMAL);
BuffFadeByEffect(SE_ImprovedInvisAnimals);
BuffFadeByEffect(SE_InvisVsAnimals);
invisible_animals = false;
}
}
@ -9600,22 +9540,20 @@ 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();
IsCastersTarget = target && target == this;
Mob *spell_target = caster->GetTarget();
if (spell_target && spell_target == this) {
IsCastersTarget = true;
}
}
if (!IsCastersTarget) {
int chance =
spellbonuses.NoBreakAESneak + itembonuses.NoBreakAESneak + aabonuses.NoBreakAESneak;
if (IsResisted)
int chance = spellbonuses.NoBreakAESneak + itembonuses.NoBreakAESneak + aabonuses.NoBreakAESneak;
if (IsResisted) {
chance *= 2;
if (chance && zone->random.Roll(chance))
}
if (chance && zone->random.Roll(chance)) {
return; // Do not drop Sneak/Hide
}
}
CancelSneakHide();
}
}

View File

@ -3622,7 +3622,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes
// Prevent double invising, which made you uninvised
// Not sure if all 3 should be stacking
//This is not live like behavior (~Kayen confirmed 2/2/22)
if (!RuleB(Spells, AllowDoubleInvis)) {
if (IsEffectInSpell(spell_id, SE_Invisibility))
{

View File

@ -108,8 +108,8 @@ struct NPCType
int32 mana_regen;
int32 aggroradius; // added for AI improvement - neotokyo
int32 assistradius; // assist radius, defaults to aggroradis if not set
uint8 see_invis; // See Invis flag added
bool see_invis_undead; // See Invis vs. Undead flag added
uint16 see_invis; // See Invis flag added
uint16 see_invis_undead; // See Invis vs. Undead flag added
bool see_hide;
bool see_improved_hide;
bool qglobal;