[Quest API] Add Bot Special Attacks for Immune Aggro/Damage (#4108)

* [Quest API] Add Bot Special Attacks for Immune Aggro/Damage

# Notes
- Adds `IMMUNE_AGGRO_BOT` and `IMMUNE_DAMAGE_BOT` for uses in special abilities.

* Cleanup

* Update attack.cpp
This commit is contained in:
Alex King 2024-03-02 16:19:31 -05:00 committed by GitHub
parent 1d38e473d7
commit 70ee95efc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 157 additions and 61 deletions

View File

@ -698,6 +698,8 @@ const std::map<uint32, std::string>& EQ::constants::GetSpecialAbilityMap()
{ IMMUNE_OPEN, "Immune to Open" }, { IMMUNE_OPEN, "Immune to Open" },
{ IMMUNE_ASSASSINATE, "Immune to Assassinate" }, { IMMUNE_ASSASSINATE, "Immune to Assassinate" },
{ IMMUNE_HEADSHOT, "Immune to Headshot" }, { IMMUNE_HEADSHOT, "Immune to Headshot" },
{ IMMUNE_AGGRO_BOT, "Immune to Bot Aggro" },
{ IMMUNE_DAMAGE_BOT, "Immune to Bot Damage" },
}; };
return special_ability_map; return special_ability_map;

View File

@ -656,7 +656,9 @@ enum {
IMMUNE_OPEN = 53, IMMUNE_OPEN = 53,
IMMUNE_ASSASSINATE = 54, IMMUNE_ASSASSINATE = 54,
IMMUNE_HEADSHOT = 55, IMMUNE_HEADSHOT = 55,
MAX_SPECIAL_ATTACK = 56 IMMUNE_AGGRO_BOT = 56,
IMMUNE_DAMAGE_BOT = 57,
MAX_SPECIAL_ATTACK = 58
}; };

View File

@ -621,28 +621,38 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
// NPC *npc1, *npc2; // NPC *npc1, *npc2;
int reverse; int reverse;
if(!zone->CanDoCombat()) if (!zone->CanDoCombat()) {
return false;
// some special cases
if(!target)
return false;
if(this == target) // you can attack yourself
return true;
if(target->GetSpecialAbility(NO_HARM_FROM_CLIENT)){
return false; return false;
} }
if (target->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT) && IsClient()) // some special cases
if (!target) {
return false; return false;
}
if (target->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC()) if (this == target) { // you can attack yourself
return false; return true;
}
if (target->IsHorse()) if (target->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
return false; return false;
}
if (IsBot() && target->GetSpecialAbility(IMMUNE_DAMAGE_BOT)) {
return false;
}
if (IsClient() && target->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT)) {
return false;
}
if (IsNPC() && target->GetSpecialAbility(IMMUNE_DAMAGE_NPC)) {
return false;
}
if (target->IsHorse()) {
return false;
}
// can't damage own pet (applies to everthing) // can't damage own pet (applies to everthing)
Mob *target_owner = target->GetOwner(); Mob *target_owner = target->GetOwner();

View File

@ -3091,26 +3091,37 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
TryTriggerOnCastRequirement(); TryTriggerOnCastRequirement();
} }
if (IsClient() && !IsAIControlled()) if (IsClient() && !IsAIControlled()) {
return; return;
}
if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO)) if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO)) {
return; return;
}
if (GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC()) if (other->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) {
return; return;
}
if (GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient()) if (other->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) {
return; return;
}
if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id)) if (other->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC)) {
return; return;
}
if (other == myowner) if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id)) {
return; return;
}
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON)) if (other == myowner) {
return; return;
}
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON)) {
return;
}
if (GetSpecialAbility(NPC_TUNNELVISION)) { if (GetSpecialAbility(NPC_TUNNELVISION)) {
int tv_mod = GetSpecialAbilityParam(NPC_TUNNELVISION, 0); int tv_mod = GetSpecialAbilityParam(NPC_TUNNELVISION, 0);
@ -3194,8 +3205,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
// owner must get on list, but he's not actually gained any hate yet // owner must get on list, but he's not actually gained any hate yet
if ( if (
!owner->GetSpecialAbility(IMMUNE_AGGRO) && !owner->GetSpecialAbility(IMMUNE_AGGRO) &&
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && owner->IsClient()) && !(owner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && owner->IsNPC()) !(owner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(owner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
) { ) {
if (owner->IsClient() && !CheckAggro(owner)) { if (owner->IsClient() && !CheckAggro(owner)) {
owner->CastToClient()->AddAutoXTarget(this); owner->CastToClient()->AddAutoXTarget(this);
@ -3209,8 +3221,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
if ( if (
!mypet->IsFamiliar() && !mypet->IsFamiliar() &&
!mypet->GetSpecialAbility(IMMUNE_AGGRO) && !mypet->GetSpecialAbility(IMMUNE_AGGRO) &&
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && IsClient()) && !(IsBot() && mypet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && IsNPC()) !(IsClient() && mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(IsNPC() && mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC))
) { ) {
mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
} }
@ -3219,8 +3232,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
if ( if (
myowner->IsAIControlled() && myowner->IsAIControlled() &&
!myowner->GetSpecialAbility(IMMUNE_AGGRO) && !myowner->GetSpecialAbility(IMMUNE_AGGRO) &&
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && myowner->IsClient()) && !(myowner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && myowner->IsNPC()) !(myowner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(myowner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
) { ) {
myowner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); myowner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
} }
@ -4060,8 +4074,9 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
!pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->GetSpecialAbility(IMMUNE_AGGRO) &&
!pet->IsEngaged() && !pet->IsEngaged() &&
attacker && attacker &&
!(pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) && !(attacker->IsBot() && pet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(pet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) && !(attacker->IsClient() && pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(attacker->IsNPC() && pet->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
attacker != this && attacker != this &&
!attacker->IsCorpse() && !attacker->IsCorpse() &&
!pet->IsGHeld() && !pet->IsGHeld() &&

View File

@ -4379,8 +4379,9 @@ void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy)
if (n->GetSwarmInfo()->owner_id == owner->GetID()) { if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
if ( if (
!n->GetSpecialAbility(IMMUNE_AGGRO) && !n->GetSpecialAbility(IMMUNE_AGGRO) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient()) && !(other->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC()) !(other->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(other->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC))
) { ) {
n->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); n->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
} }
@ -4405,8 +4406,9 @@ void EntityList::AddTempPetsToHateListOnOwnerDamage(Mob *owner, Mob* attacker, i
attacker != n && attacker != n &&
!n->IsEngaged() && !n->IsEngaged() &&
!n->GetSpecialAbility(IMMUNE_AGGRO) && !n->GetSpecialAbility(IMMUNE_AGGRO) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) && !(attacker->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) && !(attacker->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(attacker->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
!attacker->IsTrap() && !attacker->IsTrap() &&
!attacker->IsCorpse() !attacker->IsCorpse()
) { ) {

View File

@ -3866,7 +3866,9 @@ luabind::scope lua_register_special_abilities() {
luabind::value("modify_avoid_damage", static_cast<int>(MODIFY_AVOID_DAMAGE)), luabind::value("modify_avoid_damage", static_cast<int>(MODIFY_AVOID_DAMAGE)),
luabind::value("immune_open", static_cast<int>(IMMUNE_OPEN)), luabind::value("immune_open", static_cast<int>(IMMUNE_OPEN)),
luabind::value("immune_assassinate", static_cast<int>(IMMUNE_ASSASSINATE)), luabind::value("immune_assassinate", static_cast<int>(IMMUNE_ASSASSINATE)),
luabind::value("immune_headshot", static_cast<int>(IMMUNE_HEADSHOT)) luabind::value("immune_headshot", static_cast<int>(IMMUNE_HEADSHOT)),
luabind::value("immune_aggro_bot", static_cast<int>(IMMUNE_AGGRO_BOT)),
luabind::value("immune_damage_bot", static_cast<int>(IMMUNE_DAMAGE_BOT))
)]; )];
} }

View File

@ -5204,32 +5204,47 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime)
} }
void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, int level_override) { void Mob::ExecWeaponProc(const EQ::ItemInstance* inst, uint16 spell_id, Mob* on, int level_override)
{
// Changed proc targets to look up based on the spells goodEffect flag. // Changed proc targets to look up based on the spells goodEffect flag.
// This should work for the majority of weapons. // This should work for the majority of weapons.
if (!on) { if (!on) {
return; return;
} }
if(!IsValidSpell(spell_id) || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) { if (!IsValidSpell(spell_id) || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
//This is so 65535 doesn't get passed to the client message and to logs because it is not relavant information for debugging. //This is so 65535 doesn't get passed to the client message and to logs because it is not relavant information for debugging.
return; return;
} }
if (on->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT) && IsClient()) if (IsBot() && on->GetSpecialAbility(IMMUNE_DAMAGE_BOT)) {
return; return;
}
if (on->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC()) if (IsClient() && on->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT)) {
return; return;
}
if (IsNoCast()) if (IsNPC() && on->GetSpecialAbility(IMMUNE_DAMAGE_NPC)) {
return; return;
}
if(!IsValidSpell(spell_id)) { // Check for a valid spell otherwise it will crash through the function if (IsNoCast()) {
if(IsClient()){ return;
Message(0, "Invalid spell proc %u", spell_id); }
if (!IsValidSpell(spell_id)) { // Check for a valid spell otherwise it will crash through the function
if (IsClient()) {
Message(
Chat::White,
fmt::format(
"Invalid spell ID for proc {}.",
spell_id
).c_str()
);
LogSpells("Player [{}] Weapon Procced invalid spell [{}]", GetName(), spell_id); LogSpells("Player [{}] Weapon Procced invalid spell [{}]", GetName(), spell_id);
} }
return; return;
} }
@ -5243,7 +5258,7 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
return; return;
} }
if(inst && IsClient()) { if (inst && IsClient()) {
//const cast is dirty but it would require redoing a ton of interfaces at this point //const cast is dirty but it would require redoing a ton of interfaces at this point
//It should be safe as we don't have any truly const EQ::ItemInstance floating around anywhere. //It should be safe as we don't have any truly const EQ::ItemInstance floating around anywhere.
//So we'll live with it for now //So we'll live with it for now
@ -5263,30 +5278,76 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
} }
} }
bool twinproc = false; bool twin_proc = false;
int32 twinproc_chance = 0; int32 twin_proc_chance = 0;
if (IsClient() || IsBot()) { if (IsClient() || IsBot()) {
twinproc_chance = GetFocusEffect(focusTwincast, spell_id); twin_proc_chance = GetFocusEffect(focusTwincast, spell_id);
} }
if (twinproc_chance && zone->random.Roll(twinproc_chance)) { if (twin_proc_chance && zone->random.Roll(twin_proc_chance)) {
twinproc = true; twin_proc = true;
} }
if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id)) && spells[spell_id].target_type != ST_TargetsTarget) { // NPC innate procs don't take this path ever if (
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); IsBeneficialSpell(spell_id) &&
if (twinproc) { (
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); !IsNPC() ||
(
IsNPC() &&
CastToNPC()->GetInnateProcSpellID() != spell_id
)
) &&
spells[spell_id].target_type != ST_TargetsTarget
) { // NPC innate procs don't take this path ever
SpellFinished(
spell_id,
this,
EQ::spells::CastingSlot::Item,
0,
-1,
spells[spell_id].resist_difficulty,
true,
level_override
);
if (twin_proc) {
SpellFinished(
spell_id,
this,
EQ::spells::CastingSlot::Item,
0,
-1,
spells[spell_id].resist_difficulty,
true,
level_override
);
}
} else if (!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients
SpellFinished(
spell_id,
on,
EQ::spells::CastingSlot::Item,
0,
-1,
spells[spell_id].resist_difficulty,
true,
level_override
);
if (twin_proc && (!(on->IsClient() && on->CastToClient()->dead))) {
SpellFinished(
spell_id,
on,
EQ::spells::CastingSlot::Item,
0,
-1,
spells[spell_id].resist_difficulty,
true,
level_override
);
} }
} }
else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients
SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
if (twinproc && (!(on->IsClient() && on->CastToClient()->dead))) {
SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
}
}
return;
} }
uint32 Mob::GetZoneID() const { uint32 Mob::GetZoneID() const {

View File

@ -3798,6 +3798,8 @@ void NPC::DescribeSpecialAbilities(Client* c)
IMMUNE_OPEN, IMMUNE_OPEN,
IMMUNE_ASSASSINATE, IMMUNE_ASSASSINATE,
IMMUNE_HEADSHOT, IMMUNE_HEADSHOT,
IMMUNE_AGGRO_BOT,
IMMUNE_DAMAGE_BOT
}; };
// These abilities have parameters that need to be parsed out individually // These abilities have parameters that need to be parsed out individually