[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_ASSASSINATE, "Immune to Assassinate" },
{ IMMUNE_HEADSHOT, "Immune to Headshot" },
{ IMMUNE_AGGRO_BOT, "Immune to Bot Aggro" },
{ IMMUNE_DAMAGE_BOT, "Immune to Bot Damage" },
};
return special_ability_map;

View File

@ -656,7 +656,9 @@ enum {
IMMUNE_OPEN = 53,
IMMUNE_ASSASSINATE = 54,
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;
int reverse;
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)){
if (!zone->CanDoCombat()) {
return false;
}
if (target->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT) && IsClient())
// some special cases
if (!target) {
return false;
}
if (target->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC())
return false;
if (this == target) { // you can attack yourself
return true;
}
if (target->IsHorse())
if (target->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
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)
Mob *target_owner = target->GetOwner();

View File

@ -3091,26 +3091,37 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
TryTriggerOnCastRequirement();
}
if (IsClient() && !IsAIControlled())
if (IsClient() && !IsAIControlled()) {
return;
}
if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO))
if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO)) {
return;
}
if (GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC())
if (other->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) {
return;
}
if (GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient())
if (other->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) {
return;
}
if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id))
if (other->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC)) {
return;
}
if (other == myowner)
if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id)) {
return;
}
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON))
if (other == myowner) {
return;
}
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON)) {
return;
}
if (GetSpecialAbility(NPC_TUNNELVISION)) {
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
if (
!owner->GetSpecialAbility(IMMUNE_AGGRO) &&
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && owner->IsClient()) &&
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && owner->IsNPC())
!(owner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(owner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(owner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
) {
if (owner->IsClient() && !CheckAggro(owner)) {
owner->CastToClient()->AddAutoXTarget(this);
@ -3209,8 +3221,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
if (
!mypet->IsFamiliar() &&
!mypet->GetSpecialAbility(IMMUNE_AGGRO) &&
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && IsClient()) &&
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && IsNPC())
!(IsBot() && mypet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(IsClient() && mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(IsNPC() && mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC))
) {
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 (
myowner->IsAIControlled() &&
!myowner->GetSpecialAbility(IMMUNE_AGGRO) &&
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && myowner->IsClient()) &&
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && myowner->IsNPC())
!(myowner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(myowner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(myowner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
) {
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->IsEngaged() &&
attacker &&
!(pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) &&
!(pet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) &&
!(attacker->IsBot() && pet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(attacker->IsClient() && pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(attacker->IsNPC() && pet->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
attacker != this &&
!attacker->IsCorpse() &&
!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->GetSpecialAbility(IMMUNE_AGGRO) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient()) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC())
!(other->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(other->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(other->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC))
) {
n->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
}
@ -4405,8 +4406,9 @@ void EntityList::AddTempPetsToHateListOnOwnerDamage(Mob *owner, Mob* attacker, i
attacker != n &&
!n->IsEngaged() &&
!n->GetSpecialAbility(IMMUNE_AGGRO) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) &&
!(attacker->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(attacker->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(attacker->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
!attacker->IsTrap() &&
!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("immune_open", static_cast<int>(IMMUNE_OPEN)),
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.
// This should work for the majority of weapons.
if (!on) {
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.
return;
}
if (on->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT) && IsClient())
if (IsBot() && on->GetSpecialAbility(IMMUNE_DAMAGE_BOT)) {
return;
}
if (on->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC())
if (IsClient() && on->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT)) {
return;
}
if (IsNoCast())
if (IsNPC() && on->GetSpecialAbility(IMMUNE_DAMAGE_NPC)) {
return;
}
if(!IsValidSpell(spell_id)) { // Check for a valid spell otherwise it will crash through the function
if(IsClient()){
Message(0, "Invalid spell proc %u", spell_id);
if (IsNoCast()) {
return;
}
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);
}
return;
}
@ -5243,7 +5258,7 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
return;
}
if(inst && IsClient()) {
if (inst && IsClient()) {
//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.
//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;
int32 twinproc_chance = 0;
bool twin_proc = false;
int32 twin_proc_chance = 0;
if (IsClient() || IsBot()) {
twinproc_chance = GetFocusEffect(focusTwincast, spell_id);
twin_proc_chance = GetFocusEffect(focusTwincast, spell_id);
}
if (twinproc_chance && zone->random.Roll(twinproc_chance)) {
twinproc = true;
if (twin_proc_chance && zone->random.Roll(twin_proc_chance)) {
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
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
if (twinproc) {
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
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
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 {

View File

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