[Spells] Implemented SPA 281 SE_PetFeignMinion (#1900)

* start

* update

* debugs in

* test

* clean up

* debugs removed

* Update mob_ai.cpp

* Update spdat.h

* [Spells] Implemented SPA 281 SE_PetFeignMinion

debug remoevd

* [Spells] Implemented SPA 281 SE_PetFeignMinion

npc forget timer

* [Spells] Implemented SPA 281 SE_PetFeignMinion
This commit is contained in:
KayenEQ 2021-12-27 11:33:57 -05:00 committed by GitHub
parent 7f23c93ce5
commit 323b35989c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 164 additions and 81 deletions

View File

@ -26,6 +26,7 @@
#define SPELLBOOK_UNKNOWN 0xFFFFFFFF //player profile spells are 32 bit
//some spell IDs which will prolly change, but are needed
#define SPELL_LIFEBURN 2755
#define SPELL_LEECH_TOUCH 2766
#define SPELL_LAY_ON_HANDS 87
#define SPELL_HARM_TOUCH 88
@ -998,7 +999,7 @@ 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 // implemented, ability allows you to instruct your pet to feign death via the '/pet feign' command, base: succeed chance, limit: none, max: none, Note: Only implemented as an AA.
#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

View File

@ -1402,45 +1402,62 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib
return std::max(0, AggroAmount);
}
void Mob::AddFeignMemory(Client* attacker) {
if(feign_memory_list.empty() && AI_feign_remember_timer != nullptr)
void Mob::AddFeignMemory(Mob* attacker) {
if (feign_memory_list.empty() && AI_feign_remember_timer != nullptr) {
AI_feign_remember_timer->Start(AIfeignremember_delay);
feign_memory_list.insert(attacker->CharacterID());
}
if (attacker) {
feign_memory_list.insert(attacker->GetID());
}
}
void Mob::RemoveFromFeignMemory(Client* attacker) {
feign_memory_list.erase(attacker->CharacterID());
if(feign_memory_list.empty() && AI_feign_remember_timer != nullptr)
void Mob::RemoveFromFeignMemory(Mob* attacker) {
if (!attacker) {
return;
}
feign_memory_list.erase(attacker->GetID());
if (feign_memory_list.empty() && AI_feign_remember_timer != nullptr) {
AI_feign_remember_timer->Disable();
}
if(feign_memory_list.empty())
{
minLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMin);
maxLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMax);
if(AI_feign_remember_timer != nullptr)
if (AI_feign_remember_timer != nullptr) {
AI_feign_remember_timer->Disable();
}
}
}
void Mob::ClearFeignMemory() {
auto RememberedCharID = feign_memory_list.begin();
while (RememberedCharID != feign_memory_list.end())
auto remembered_feigned_mobid = feign_memory_list.begin();
while (remembered_feigned_mobid != feign_memory_list.end())
{
Client* remember_client = entity_list.GetClientByCharID(*RememberedCharID);
if(remember_client != nullptr) //Still in zone
remember_client->RemoveXTarget(this, false);
++RememberedCharID;
Mob* remembered_mob = entity_list.GetMob(*remembered_feigned_mobid);
if (remembered_mob->IsClient() && remembered_mob != nullptr) { //Still in zone
remembered_mob->CastToClient()->RemoveXTarget(this, false);
}
++remembered_feigned_mobid;
}
feign_memory_list.clear();
minLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMin);
maxLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMax);
if(AI_feign_remember_timer != nullptr)
if (AI_feign_remember_timer != nullptr) {
AI_feign_remember_timer->Disable();
}
}
bool Mob::IsOnFeignMemory(Client *attacker) const
bool Mob::IsOnFeignMemory(Mob *attacker) const
{
return feign_memory_list.find(attacker->CharacterID()) != feign_memory_list.end();
if (!attacker) {
return 0;
}
return feign_memory_list.find(attacker->GetID()) != feign_memory_list.end();
}
bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {

View File

@ -2813,7 +2813,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic);
if (other->IsClient() && !on_hatelist && !IsOnFeignMemory(other->CastToClient()))
if (other->IsClient() && !on_hatelist && !IsOnFeignMemory(other))
other->CastToClient()->AddAutoXTarget(this);
#ifdef BOTS

View File

@ -1571,8 +1571,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
break;
case SE_FeignedMinion:
if (newbon->FeignedMinionChance < base_value)
if (newbon->FeignedMinionChance < base_value) {
newbon->FeignedMinionChance = base_value;
}
newbon->PetCommands[PET_FEIGN] = true;
break;
case SE_AdditionalAura:

View File

@ -145,7 +145,6 @@ Client::Client(EQStreamInterface* ieqs)
global_channel_timer(1000),
fishing_timer(8000),
endupkeep_timer(1000),
forget_timer(0),
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)),
client_zone_wide_full_position_update_timer(5 * 60 * 1000),
@ -186,7 +185,6 @@ Client::Client(EQStreamInterface* ieqs)
character_id = 0;
conn_state = NoPacketsReceived;
client_data_loaded = false;
feigned = false;
berserk = false;
dead = false;
eqs = ieqs;
@ -2677,22 +2675,6 @@ void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing, uint32 re
safe_delete(outapp);
}
void Client::SetFeigned(bool in_feigned) {
if (in_feigned)
{
if(RuleB(Character, FeignKillsPet))
{
SetPet(0);
}
SetHorseId(0);
entity_list.ClearFeignAggro(this);
forget_timer.Start(FeignMemoryDuration);
} else {
forget_timer.Disable();
}
feigned=in_feigned;
}
void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const EQ::ItemData* item, bool buying)
{
if(!player || !merchant || !item)

View File

@ -835,9 +835,6 @@ public:
inline uint8 GetBecomeNPCLevel() const { return npclevel; }
inline void SetBecomeNPC(bool flag) { npcflag = flag; }
inline void SetBecomeNPCLevel(uint8 level) { npclevel = level; }
void SetFeigned(bool in_feigned);
/// this cures timing issues cuz dead animation isn't done but server side feigning is?
inline bool GetFeigned() const { return(feigned); }
EQStreamInterface* Connection() { return eqs; }
#ifdef PACKET_PROFILER
void DumpPacketProfile() { if(eqs) eqs->DumpPacketProfile(); }
@ -1850,7 +1847,6 @@ private:
Timer global_channel_timer;
Timer fishing_timer;
Timer endupkeep_timer;
Timer forget_timer; // our 2 min everybody forgets you timer
Timer autosave_timer;
Timer client_scan_npc_aggro_timer;
Timer client_zone_wide_full_position_update_timer;
@ -1890,7 +1886,6 @@ private:
bool npcflag;
uint8 npclevel;
bool feigned;
bool bZoning;
bool tgb;
bool instalog;

View File

@ -10255,6 +10255,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {
if (target != this && DistanceSquaredNoZ(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) {
mypet->SetFeigned(false);
if (mypet->IsPetStop()) {
mypet->SetPetStop(false);
SetPetCommandState(PET_BUTTON_STOP, 0);
@ -10301,6 +10302,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {
if (GetTarget() != this && DistanceSquaredNoZ(mypet->GetPosition(), GetTarget()->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) {
mypet->SetFeigned(false);
if (mypet->IsPetStop()) {
mypet->SetPetStop(false);
SetPetCommandState(PET_BUTTON_STOP, 0);
@ -10376,6 +10378,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
if (mypet->IsNPC()) {
// Set Sit button to unpressed - send stand anim/end hpregen
mypet->SetFeigned(false);
SetPetCommandState(PET_BUTTON_SIT, 0);
mypet->SendAppearancePacket(AT_Anim, ANIM_STAND);
@ -10396,6 +10399,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF
if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {
mypet->SetFeigned(false);
mypet->SayString(this, Chat::PetResponse, PET_FOLLOWING);
mypet->SetPetOrder(SPO_Follow);
@ -10443,6 +10447,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF
if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {
mypet->SetFeigned(false);
mypet->SayString(this, Chat::PetResponse, PET_GUARDME_STRING);
mypet->SetPetOrder(SPO_Follow);
@ -10463,12 +10468,14 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {
if (mypet->GetPetOrder() == SPO_Sit)
{
mypet->SetFeigned(false);
mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING);
mypet->SetPetOrder(SPO_Follow);
mypet->SendAppearancePacket(AT_Anim, ANIM_STAND);
}
else
{
mypet->SetFeigned(false);
mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING);
mypet->SetPetOrder(SPO_Sit);
mypet->SetRunAnimSpeed(0);
@ -10483,6 +10490,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF
if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {
mypet->SetFeigned(false);
mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING);
SetPetCommandState(PET_BUTTON_SIT, 0);
mypet->SetPetOrder(SPO_Follow);
@ -10494,6 +10502,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF
if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {
mypet->SetFeigned(false);
mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING);
SetPetCommandState(PET_BUTTON_SIT, 1);
mypet->SetPetOrder(SPO_Sit);
@ -10687,6 +10696,38 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
}
break;
}
case PET_FEIGN: {
if (aabonuses.PetCommands[PetCommand] && mypet->IsNPC()) {
if (mypet->IsFeared())
break;
int pet_fd_chance = aabonuses.FeignedMinionChance;
if (zone->random.Int(0, 99) > pet_fd_chance) {
mypet->SetFeigned(false);
entity_list.MessageCloseString(this, false, 200, 10, STRING_FEIGNFAILED, mypet->GetCleanName());
}
else {
bool immune_aggro = GetSpecialAbility(IMMUNE_AGGRO);
mypet->SetSpecialAbility(IMMUNE_AGGRO, 1);
mypet->WipeHateList();
mypet->SetPetOrder(SPO_FeignDeath);
mypet->SetRunAnimSpeed(0);
mypet->StopNavigation();
mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH);
mypet->SetFeigned(true);
mypet->SetTarget(nullptr);
if (!mypet->UseBardSpellLogic()) {
mypet->InterruptSpell();
}
if (!immune_aggro) {
mypet->SetSpecialAbility(IMMUNE_AGGRO, 0);
}
}
}
break;
}
case PET_STOP: {
if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF
@ -10694,7 +10735,6 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
if (mypet->IsPetStop()) {
mypet->SetPetStop(false);
} else {
mypet->SetPetStop(true);
mypet->StopNavigation();
mypet->SetTarget(nullptr);
if (mypet->IsPetRegroup()) {

View File

@ -1515,10 +1515,10 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
continue;
if (RemoveFromXTargets) {
if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m->CastToClient())))
if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m)))
m->CastToClient()->RemoveXTarget(mob, false);
// FadingMemories calls this function passing the client.
else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob->CastToClient())))
else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob)))
mob->CastToClient()->RemoveXTarget(m, false);
}
@ -3488,7 +3488,7 @@ void EntityList::ClearFeignAggro(Mob *targ)
auto it = npc_list.begin();
while (it != npc_list.end()) {
// add Feign Memory check because sometimes weird stuff happens
if (it->second->CheckAggro(targ) || (targ->IsClient() && it->second->IsOnFeignMemory(targ->CastToClient()))) {
if (it->second->CheckAggro(targ) || (targ->IsClient() && it->second->IsOnFeignMemory(targ))) {
if (it->second->GetSpecialAbility(IMMUNE_FEIGN_DEATH)) {
++it;
continue;
@ -3514,22 +3514,31 @@ void EntityList::ClearFeignAggro(Mob *targ)
it->second->RemoveFromHateList(targ);
if (targ->IsClient()) {
if (it->second->GetLevel() >= 35 && zone->random.Roll(60))
it->second->AddFeignMemory(targ->CastToClient());
else
if (it->second->GetLevel() >= 35 && zone->random.Roll(60)) {
it->second->AddFeignMemory(targ);
}
else {
targ->CastToClient()->RemoveXTarget(it->second, false);
}
}
else if (targ->IsPet()){
if (it->second->GetLevel() >= 35 && zone->random.Roll(60)) {
it->second->AddFeignMemory(targ);
}
}
}
++it;
}
}
void EntityList::ClearZoneFeignAggro(Client *targ)
void EntityList::ClearZoneFeignAggro(Mob *targ)
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
it->second->RemoveFromFeignMemory(targ);
targ->CastToClient()->RemoveXTarget(it->second, false);
if (targ && targ->IsClient()) {
targ->CastToClient()->RemoveXTarget(it->second, false);
}
++it;
}
}

View File

@ -455,7 +455,7 @@ public:
void ClearAggro(Mob* targ);
void ClearWaterAggro(Mob* targ);
void ClearFeignAggro(Mob* targ);
void ClearZoneFeignAggro(Client* targ);
void ClearZoneFeignAggro(Mob* targ);
void AggroZone(Mob* who, uint32 hate = 0);
bool Fighting(Mob* targ);

View File

@ -109,6 +109,7 @@ Mob::Mob(
stunned_timer(0),
spun_timer(0),
bardsong_timer(6000),
forget_timer(0),
gravity_timer(1000),
viral_timer(0),
m_FearWalkTarget(-999999.0f, -999999.0f, -999999.0f),
@ -282,6 +283,8 @@ Mob::Mob(
InitializeBuffSlots();
feigned = false;
// clear the proc arrays
for (int j = 0; j < MAX_PROCS; j++) {
PermaProcs[j].spellID = SPELL_UNKNOWN;
@ -6505,6 +6508,24 @@ void Mob::ShieldAbilityClearVariables()
}
}
void Mob::SetFeigned(bool in_feigned) {
if (in_feigned) {
if (IsClient()) {
if (RuleB(Character, FeignKillsPet)){
SetPet(0);
}
CastToClient()->SetHorseId(0);
}
entity_list.ClearFeignAggro(this);
forget_timer.Start(FeignMemoryDuration);
}
else {
forget_timer.Disable();
}
feigned = in_feigned;
}
#ifdef BOTS
bool Mob::JoinHealRotationTargetPool(std::shared_ptr<HealRotation>* heal_rotation)
{

View File

@ -71,7 +71,7 @@ class Mob : public Entity {
public:
enum CLIENT_CONN_STATUS { CLIENT_CONNECTING, CLIENT_CONNECTED, CLIENT_LINKDEAD,
CLIENT_KICKED, DISCONNECTED, CLIENT_ERROR, CLIENT_CONNECTINGALL };
enum eStandingPetOrder { SPO_Follow, SPO_Sit, SPO_Guard };
enum eStandingPetOrder { SPO_Follow, SPO_Sit, SPO_Guard, SPO_FeignDeath };
struct SpecialAbility {
SpecialAbility() {
@ -670,10 +670,10 @@ public:
bool HateSummon();
void FaceTarget(Mob* mob_to_face = 0);
void WipeHateList();
void AddFeignMemory(Client* attacker);
void RemoveFromFeignMemory(Client* attacker);
void AddFeignMemory(Mob* attacker);
void RemoveFromFeignMemory(Mob* attacker);
void ClearFeignMemory();
bool IsOnFeignMemory(Client *attacker) const;
bool IsOnFeignMemory(Mob *attacker) const;
void PrintHateListToClient(Client *who) { hate_list.PrintHateListToClient(who); }
std::list<struct_HateList*>& GetHateList() { return hate_list.GetHateList(); }
std::list<struct_HateList*> GetHateListByDistance(int distance = 0) { return hate_list.GetHateListByDistance(distance); }
@ -1296,6 +1296,10 @@ public:
bool CanOpenDoors() const;
void SetCanOpenDoors(bool can_open);
void SetFeigned(bool in_feigned);
/// this cures timing issues cuz dead animation isn't done but server side feigning is?
inline bool GetFeigned() const { return(feigned); }
void DeleteBucket(std::string bucket_name);
std::string GetBucket(std::string bucket_name);
std::string GetBucketExpires(std::string bucket_name);
@ -1720,6 +1724,9 @@ protected:
AuraMgr aura_mgr;
AuraMgr trap_mgr;
bool feigned;
Timer forget_timer; // our 2 min everybody forgets you timer
bool m_can_open_doors;
MobMovementManager *mMovementManager;
@ -1727,7 +1734,7 @@ protected:
private:
void _StopSong(); //this is not what you think it is
Mob* target;
#ifdef BOTS
std::shared_ptr<HealRotation> m_target_of_heal_rotation;

View File

@ -838,20 +838,21 @@ void Client::AI_Process()
else
{
if(AI_feign_remember_timer->Check()) {
std::set<uint32>::iterator RememberedCharID;
RememberedCharID = feign_memory_list.begin();
while (RememberedCharID != feign_memory_list.end()) {
Client* remember_client = entity_list.GetClientByCharID(*RememberedCharID);
if (remember_client == nullptr) {
std::set<uint32>::iterator remembered_feigned_mobid;
remembered_feigned_mobid = feign_memory_list.begin();
while (remembered_feigned_mobid != feign_memory_list.end()) {
Mob* remembered_mob = entity_list.GetMob(*remembered_feigned_mobid);
if (remembered_mob == nullptr || remembered_mob->IsCorpse()) {
//they are gone now...
RememberedCharID = feign_memory_list.erase(RememberedCharID);
} else if (!remember_client->GetFeigned()) {
AddToHateList(remember_client->CastToMob(),1);
RememberedCharID = feign_memory_list.erase(RememberedCharID);
remembered_feigned_mobid = feign_memory_list.erase(remembered_feigned_mobid);
} else if (!remembered_mob->GetFeigned()) {
AddToHateList(remembered_mob,1);
remembered_feigned_mobid = feign_memory_list.erase(remembered_feigned_mobid);
break;
} else {
//they are still feigned, carry on...
++RememberedCharID;
++remembered_feigned_mobid;
}
}
}
@ -1373,22 +1374,22 @@ void Mob::AI_Process() {
// 6/14/06
// Improved Feign Death Memory
// check to see if any of our previous feigned targets have gotten up.
std::set<uint32>::iterator RememberedCharID;
RememberedCharID = feign_memory_list.begin();
while (RememberedCharID != feign_memory_list.end()) {
Client *remember_client = entity_list.GetClientByCharID(*RememberedCharID);
if (remember_client == nullptr) {
std::set<uint32>::iterator remembered_feigned_mobid;
remembered_feigned_mobid = feign_memory_list.begin();
while (remembered_feigned_mobid != feign_memory_list.end()) {
Mob *remembered_mob = entity_list.GetMob(*remembered_feigned_mobid);
if (remembered_mob == nullptr || remembered_mob->IsCorpse()) {
//they are gone now...
RememberedCharID = feign_memory_list.erase(RememberedCharID);
remembered_feigned_mobid = feign_memory_list.erase(remembered_feigned_mobid);
}
else if (!remember_client->GetFeigned()) {
AddToHateList(remember_client->CastToMob(), 1);
RememberedCharID = feign_memory_list.erase(RememberedCharID);
else if (!remembered_mob->GetFeigned()) {
AddToHateList(remembered_mob, 1);
remembered_feigned_mobid = feign_memory_list.erase(remembered_feigned_mobid);
break;
}
else {
//they are still feigned, carry on...
++RememberedCharID;
++remembered_feigned_mobid;
}
}
}
@ -1485,6 +1486,10 @@ void Mob::AI_Process() {
}
break;
}
case SPO_FeignDeath: {
SetAppearance(eaDead, false);
break;
}
}
if (IsPetRegroup()) {
return;
@ -1557,6 +1562,11 @@ void Mob::AI_Process() {
}
}
if (forget_timer.Check()) {
forget_timer.Disable();
entity_list.ClearZoneFeignAggro(this);
}
//Do Ranged attack here
if (doranged) {
RangedAttack(target);

View File

@ -1557,18 +1557,17 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Feign Death");
#endif
//todo, look up spell ID in DB
if(spell_id == 2488) //Dook- Lifeburn fix
if(spell_id == SPELL_LIFEBURN) //Dook- Lifeburn fix
break;
if(IsClient()) {
CastToClient()->SetHorseId(0); // dismount if have horse
if (zone->random.Int(0, 99) > spells[spell_id].base_value[i]) {
CastToClient()->SetFeigned(false);
SetFeigned(false);
entity_list.MessageCloseString(this, false, 200, 10, STRING_FEIGNFAILED, GetName());
} else {
CastToClient()->SetFeigned(true);
SetFeigned(true);
}
}
break;