[API] Apply spells with custom buff durations and adjust existing spell buff durations. (#1997)

* working

* Update spell_effects.cpp

* updates

* disable_buff_overwrite

* revert

* update

* working

* update

* updates

* Update spells.cpp

* getbuffstat done

* Update perl_mob.cpp

* [API] Apply spells with custom buff durations and adjust existing spell buff durations.

* [API] Apply spells with custom buff durations and adjust existing spell buff durations.

* [API] Apply spells with custom buff durations and adjust existing spell buff durations.

* [API] Apply spells with custom buff durations and adjust existing spell buff durations.

* https://github.com/EQEmu/Server/pull/1997

Lua added, thanks kinglykrab
This commit is contained in:
KayenEQ 2022-02-15 08:58:10 -05:00 committed by GitHub
parent 5fd62d82db
commit 615f4a5304
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 258 additions and 17 deletions

View File

@ -1264,8 +1264,9 @@ typedef enum {
// LAST
#define DF_Permanent 50
#define DF_Aura 51
#define DF_Permanent 50
#define DF_Aura 51
#define PERMANENT_BUFF_DURATION -1000 //this is arbitrary used when overriding spells regular buff duration to set it as permenant
// note this struct is historical, we don't actually need it to be
// aligned to anything, but for maintaining it it is kept in the order that

View File

@ -2448,6 +2448,36 @@ void Lua_Mob::SetSeeInvisibleUndeadLevel(uint8 invisible_level)
self->SetSeeInvisibleUndead(invisible_level);
}
void Lua_Mob::ApplySpellBuff(int spell_id) {
Lua_Safe_Call_Void();
self->ApplySpellBuff(spell_id, 0);
}
void Lua_Mob::ApplySpellBuff(int spell_id, int duration) {
Lua_Safe_Call_Void();
self->ApplySpellBuff(spell_id, duration);
}
int Lua_Mob::GetBuffStatValueBySlot(uint8 slot, const char* identifier) {
Lua_Safe_Call_Int();
return self->GetBuffStatValueBySlot(slot, identifier);
}
int Lua_Mob::GetBuffStatValueBySpell(int spell_id, const char* identifier) {
Lua_Safe_Call_Int();
return self->GetBuffStatValueBySpell(spell_id, identifier);
}
void Lua_Mob::SetBuffDuration(int spell_id) {
Lua_Safe_Call_Void();
self->SetBuffDuration(spell_id, 0);
}
void Lua_Mob::SetBuffDuration(int spell_id, int duration) {
Lua_Safe_Call_Void();
self->SetBuffDuration(spell_id, duration);
}
luabind::scope lua_register_mob() {
return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
.def(luabind::constructor<>())
@ -2458,6 +2488,8 @@ luabind::scope lua_register_mob() {
.def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int,bool))&Lua_Mob::AddToHateList)
.def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int,bool,bool))&Lua_Mob::AddToHateList)
.def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int,bool,bool,bool))&Lua_Mob::AddToHateList)
.def("ApplySpellBuff", (void(Lua_Mob::*)(int))&Lua_Mob::ApplySpellBuff)
.def("ApplySpellBuff", (void(Lua_Mob::*)(int, int))&Lua_Mob::ApplySpellBuff)
.def("Attack", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::Attack)
.def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::Attack)
.def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool))&Lua_Mob::Attack)
@ -2595,6 +2627,8 @@ luabind::scope lua_register_mob() {
.def("GetBucketKey", (std::string(Lua_Mob::*)(void))&Lua_Mob::GetBucketKey)
.def("GetBucketRemaining", (std::string(Lua_Mob::*)(std::string))&Lua_Mob::GetBucketRemaining)
.def("GetBuffSlotFromType", &Lua_Mob::GetBuffSlotFromType)
.def("GetBuffStatValueBySlot", (void(Lua_Mob::*)(uint8, const char*))& Lua_Mob::GetBuffStatValueBySlot)
.def("GetBuffStatValueBySpell", (void(Lua_Mob::*)(int, const char*))&Lua_Mob::GetBuffStatValueBySpell)
.def("GetCHA", &Lua_Mob::GetCHA)
.def("GetCR", &Lua_Mob::GetCR)
.def("GetCasterLevel", &Lua_Mob::GetCasterLevel)
@ -2810,6 +2844,8 @@ luabind::scope lua_register_mob() {
.def("SetBodyType", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetBodyType)
.def("SetBucket", (void(Lua_Mob::*)(std::string,std::string))&Lua_Mob::SetBucket)
.def("SetBucket", (void(Lua_Mob::*)(std::string,std::string,std::string))&Lua_Mob::SetBucket)
.def("SetBuffDuration", (void(Lua_Mob::*)(int))&Lua_Mob::SetBuffDuration)
.def("SetBuffDuration", (void(Lua_Mob::*)(int, int))&Lua_Mob::SetBuffDuration)
.def("SetCurrentWP", &Lua_Mob::SetCurrentWP)
.def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject)
.def("SetDisableMelee", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDisableMelee)

View File

@ -457,6 +457,12 @@ public:
bool IsHorse();
bool CanClassEquipItem(uint32 item_id);
bool CanRaceEquipItem(uint32 item_id);
void ApplySpellBuff(int spell_id);
void ApplySpellBuff(int spell_id, int duration);
int GetBuffStatValueBySlot(uint8 slot, const char* identifier);
int GetBuffStatValueBySpell(int spell_id, const char* identifier);
void SetBuffDuration(int spell_id);
void SetBuffDuration(int spell_id, int duration);
};
#endif

View File

@ -349,8 +349,8 @@ public:
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, bool from_casted_spell = false, uint32 aa_id = 0);
void SendBeginCast(uint16 spell_id, uint32 casttime);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0,
bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0, int32 duration_override = 0);
bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0, bool disable_buff_overrwrite = false);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0, int32 duration_override = 0, bool disable_buff_overrwrite = false);
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center,
CastAction_type &CastAction, EQ::spells::CastingSlot slot, bool isproc = false);
bool DoCastingChecksOnCaster(int32 spell_id);
@ -406,7 +406,7 @@ public:
bool IsAffectedByBuff(uint16 spell_id);
bool IsAffectedByBuffByGlobalGroup(GlobalGroup group);
void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration);
int AddBuff(Mob *caster, const uint16 spell_id, int duration = 0, int32 level_override = -1);
int AddBuff(Mob *caster, const uint16 spell_id, int duration = 0, int32 level_override = -1, bool disable_buff_overrwrite = false);
int CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite = false);
int CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caster_level_override = -1);
void SendPetBuffsToClient();
@ -461,6 +461,10 @@ public:
void GetAppearenceEffects();
void ClearAppearenceEffects();
void SendSavedAppearenceEffects(Client *receiver);
void SetBuffDuration(int32 spell_id, int32 duration);
void ApplySpellBuff(int32 spell_id, int32 duration);
int GetBuffStatValueBySpell(int32 spell_id, const char* stat_identifier);
int GetBuffStatValueBySlot(uint8 slot, const char* stat_identifier);
//Basic Stats/Inventory
virtual void SetLevel(uint8 in_level, bool command = false) { level = in_level; }

View File

@ -5662,6 +5662,46 @@ XS(XS_Mob_GetSpellStat) {
XSRETURN(1);
}
XS(XS_Mob_GetBuffStatValueBySpell); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_GetBuffStatValueBySpell) {
dXSARGS;
if (items != 3)
Perl_croak(aTHX_ "Usage: Mob::GetBuffStatValueBySpell(THIS, int32 spell_id, string stat)"); // @categories Spells and Disciplines
{
Mob *THIS;
int32 RETVAL;
int32 spellid = (int32)SvIV(ST(1));
Const_char *stat = (Const_char *)SvPV_nolen(ST(2));
dXSTARG;
VALIDATE_THIS_IS_MOB;
RETVAL = THIS->GetBuffStatValueBySpell(spellid, stat);
XSprePUSH;
PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
XS(XS_Mob_GetBuffStatValueBySlot); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_GetBuffStatValueBySlot) {
dXSARGS;
if (items != 3)
Perl_croak(aTHX_ "Usage: Mob::GetBuffStatValueBySlot(THIS, uint8 slot, string stat)"); // @categories Script Utility, Spells and Disciplines
{
Mob *THIS;
int32 RETVAL;
uint8 slot = (uint8)SvUV(ST(1));
Const_char *stat = (Const_char *)SvPV_nolen(ST(2));
dXSTARG;
VALIDATE_THIS_IS_MOB;
RETVAL = THIS->GetBuffStatValueBySlot(slot, stat);
XSprePUSH;
PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
XS(XS_Mob_GetSpecialAbility); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_GetSpecialAbility) {
dXSARGS;
@ -6567,6 +6607,46 @@ XS(XS_Mob_GetHateRandomNPC) {
XSRETURN(1);
}
XS(XS_Mob_SetBuffDuration); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetBuffDuration) {
dXSARGS;
if (items < 2 || items > 3)
Perl_croak(aTHX_ "Usage: Mob::SetBuffDuration(THIS, spell_id, [int duration = 0])"); // @categories Script Utility, Spells and Disciplines
{
Mob *THIS;
int spell_id = (int)SvIV(ST(1));
int duration = 0;
VALIDATE_THIS_IS_MOB;
if (items == 3) {
duration = (int)SvIV(ST(2));
}
THIS->SetBuffDuration(spell_id, duration);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_ApplySpellBuff); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_ApplySpellBuff) {
dXSARGS;
if (items < 2 || items > 3)
Perl_croak(aTHX_ "Usage: Mob::ApplySpellBuff(THIS, spell_id, [int duration = 0])"); // @categories Script Utility, Spells and Disciplines
{
Mob *THIS;
int spell_id = (int)SvIV(ST(1));
int duration = 0;
VALIDATE_THIS_IS_MOB;
if (items == 3) {
duration = (int)SvIV(ST(2));
}
THIS->ApplySpellBuff(spell_id, duration);
}
XSRETURN_EMPTY;
}
#ifdef BOTS
XS(XS_Mob_CastToBot); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_CastToBot)
@ -6622,6 +6702,7 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "AddFeignMemory"), XS_Mob_AddFeignMemory, file, "$$");
newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$");
newXSproto(strcpy(buf, "AddToHateList"), XS_Mob_AddToHateList, file, "$$;$$$$$");
newXSproto(strcpy(buf, "ApplySpellBuff"), XS_Mob_ApplySpellBuff, file, "$$;$");
newXSproto(strcpy(buf, "Attack"), XS_Mob_Attack, file, "$$;$$");
newXSproto(strcpy(buf, "BehindMob"), XS_Mob_BehindMob, file, "$;$$$");
newXSproto(strcpy(buf, "BuffCount"), XS_Mob_BuffCount, file, "$");
@ -6712,6 +6793,8 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "GetBucketKey"), XS_Mob_GetBucketKey, file, "$");
newXSproto(strcpy(buf, "GetBucketRemaining"), XS_Mob_GetBucketRemaining, file, "$$");
newXSproto(strcpy(buf, "GetBuffSlotFromType"), XS_Mob_GetBuffSlotFromType, file, "$$");
newXSproto(strcpy(buf, "GetBuffStatValueBySpell"), XS_Mob_GetBuffStatValueBySpell, file, "$$$");
newXSproto(strcpy(buf, "GetBuffStatValueBySlot"), XS_Mob_GetBuffStatValueBySlot, file, "$$$");
newXSproto(strcpy(buf, "GetCHA"), XS_Mob_GetCHA, file, "$");
newXSproto(strcpy(buf, "GetCR"), XS_Mob_GetCR, file, "$");
newXSproto(strcpy(buf, "GetCasterLevel"), XS_Mob_GetCasterLevel, file, "$$");
@ -6917,6 +7000,7 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "SetAppearance"), XS_Mob_SetAppearance, file, "$$;$");
newXSproto(strcpy(buf, "SetBodyType"), XS_Mob_SetBodyType, file, "$$;$");
newXSproto(strcpy(buf, "SetBucket"), XS_Mob_SetBucket, file, "$$$;$");
newXSproto(strcpy(buf, "SetBuffDuration"), XS_Mob_SetBuffDuration, file, "$$;$");
newXSproto(strcpy(buf, "SetCurrentWP"), XS_Mob_SetCurrentWP, file, "$$");
newXSproto(strcpy(buf, "SetDeltas"), XS_Mob_SetDeltas, file, "$$$$$");
newXSproto(strcpy(buf, "SetDisableMelee"), XS_Mob_SetDisableMelee, file, "$$");

View File

@ -46,7 +46,7 @@ extern WorldServer worldserver;
// the spell can still fail here, if the buff can't stack
// in this case false will be returned, true otherwise
bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override, int reflect_effectiveness, int32 duration_override)
bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override, int reflect_effectiveness, int32 duration_override, bool disable_buff_overrwrite)
{
int caster_level, buffslot, effect, effect_value, i;
EQ::ItemInstance *SummonedItem=nullptr;
@ -119,7 +119,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
}
else
{
buffslot = AddBuff(caster, spell_id, duration_override);
buffslot = AddBuff(caster, spell_id, duration_override, -1, disable_buff_overrwrite);
}
if(buffslot == -1) // stacking failure
return false;
@ -3662,7 +3662,8 @@ void Mob::BuffProcess()
// DF_Permanent uses -1 DF_Aura uses -4 but we need to check negatives for some spells for some reason?
if (spells[buffs[buffs_i].spellid].buff_duration_formula != DF_Permanent &&
spells[buffs[buffs_i].spellid].buff_duration_formula != DF_Aura) {
spells[buffs[buffs_i].spellid].buff_duration_formula != DF_Aura &&
buffs[buffs_i].ticsremaining != PERMANENT_BUFF_DURATION) {
if(!zone->BuffTimersSuspended() || !IsSuspendableSpell(buffs[buffs_i].spellid))
{
--buffs[buffs_i].ticsremaining;
@ -10204,3 +10205,116 @@ bool Mob::HasPersistDeathIllusion(int32 spell_id) {
}
return false;
}
void Mob::SetBuffDuration(int32 spell_id, int32 duration) {
/*
Will refresh the buff with specified spell_id to the specified duration
If spell is -1, then all spells will be set to the specified duration
If duration 0, then will set duration to buffs normal max duration.
*/
bool adjust_all_buffs = false;
if (spell_id == -1) {
adjust_all_buffs = true;
}
if (!adjust_all_buffs && !IsValidSpell(spell_id)){
return;
}
if (duration < -1) {
duration = PERMANENT_BUFF_DURATION;
}
int buff_count = GetMaxBuffSlots();
for (int slot = 0; slot < buff_count; slot++) {
if (!adjust_all_buffs) {
if (buffs[slot].spellid != SPELL_UNKNOWN && buffs[slot].spellid == spell_id) {
SpellOnTarget(buffs[slot].spellid, this, 0, false, 0, false, -1, duration, true);
return;
}
}
else {
if (buffs[slot].spellid != SPELL_UNKNOWN) {
SpellOnTarget(buffs[slot].spellid, this, 0, false, 0, false, -1, duration, true);
}
}
}
}
void Mob::ApplySpellBuff(int32 spell_id, int32 duration)
{
/*
Used for quest command to apply a new buff with custom duration.
Duration set to 0 will apply with normal duration.
*/
if (!IsValidSpell(spell_id)) {
return;
}
if (!spells[spell_id].buff_duration) {
return;
}
if (duration < -1) {
duration = PERMANENT_BUFF_DURATION;
}
SpellOnTarget(spell_id, this, 0, false, 0, false, -1, duration);
}
int Mob::GetBuffStatValueBySpell(int32 spell_id, const char* stat_identifier)
{
if (!IsValidSpell(spell_id)) {
return 0;
}
if (!stat_identifier) {
return 0;
}
std::string id = str_tolower(stat_identifier);
int buff_count = GetMaxBuffSlots();
for (int slot = 0; slot < buff_count; slot++) {
if (buffs[slot].spellid != SPELL_UNKNOWN && buffs[slot].spellid == spell_id) {
return GetBuffStatValueBySlot(slot, stat_identifier);
}
}
return 0;
}
int Mob::GetBuffStatValueBySlot(uint8 slot, const char* stat_identifier)
{
if (slot > GetMaxBuffSlots()) {
return 0;
}
if (!stat_identifier) {
return 0;
}
std::string id = str_tolower(stat_identifier);
if (id == "caster_level") { return buffs[slot].casterlevel; }
else if (id == "spell_id") { return buffs[slot].spellid; }
else if (id == "caster_id") { return buffs[slot].spellid;; }
else if (id == "ticsremaining") { return buffs[slot].ticsremaining; }
else if (id == "counters") { return buffs[slot].counters; }
else if (id == "hit_number") { return buffs[slot].hit_number; }
else if (id == "melee_rune") { return buffs[slot].melee_rune; }
else if (id == "magic_rune") { return buffs[slot].magic_rune; }
else if (id == "dot_rune") { return buffs[slot].dot_rune; }
else if (id == "caston_x") { return buffs[slot].caston_x; }
else if (id == "caston_y") { return buffs[slot].caston_y; }
else if (id == "caston_z") { return buffs[slot].caston_z; }
else if (id == "instrument_mod") { return buffs[slot].instrument_mod; }
else if (id == "persistant_buff") { return buffs[slot].persistant_buff; }
else if (id == "client") { return buffs[slot].client; }
else if (id == "extra_di_chance") { return buffs[slot].ExtraDIChance; }
else if (id == "root_break_chance") { return buffs[slot].RootBreakChance; }
else if (id == "virus_spread_time") { return buffs[slot].virus_spread_time; }
return 0;
}

View File

@ -3200,9 +3200,8 @@ bool Mob::HasDiscBuff()
// stacking problems, and -2 if this is not a buff
// if caster is null, the buff will be added with the caster level being
// the level of the mob
int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_override)
int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_override, bool disable_buff_overrwrite)
{
int buffslot, ret, caster_level, emptyslot = -1;
bool will_overwrite = false;
std::vector<int> overwrite_slots;
@ -3223,7 +3222,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
LogSpells("Buff [{}] failed to add because its duration came back as 0", spell_id);
return -2; // no duration? this isn't a buff
}
LogSpells("Trying to add buff [{}] cast by [{}] (cast level [{}]) with duration [{}]",
spell_id, caster?caster->GetName():"UNKNOWN", caster_level, duration);
@ -3297,7 +3296,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
// at this point we know that this buff will stick, but we have
// to remove some other buffs already worn if will_overwrite is true
if (will_overwrite) {
if (will_overwrite && !disable_buff_overrwrite) {
std::vector<int>::iterator cur, end;
cur = overwrite_slots.begin();
end = overwrite_slots.end();
@ -3314,9 +3313,6 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
}
}
// now add buff at emptyslot
assert(buffs[emptyslot].spellid == SPELL_UNKNOWN); // sanity check
buffs[emptyslot].spellid = spell_id;
buffs[emptyslot].casterlevel = caster_level;
if (caster && !caster->IsAura()) // maybe some other things we don't want to ...
@ -3446,7 +3442,7 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite)
// break stuff
//
bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectiveness, bool use_resist_adjust, int16 resist_adjust,
bool isproc, int level_override, int32 duration_override)
bool isproc, int level_override, int32 duration_override, bool disable_buff_overrwrite)
{
// well we can't cast a spell on target without a target
if(!spelltar)
@ -3994,7 +3990,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes
}
// cause the effects to the target
if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override, reflect_effectiveness, duration_override))
if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override, reflect_effectiveness, duration_override, disable_buff_overrwrite))
{
// if SpellEffect returned false there's a problem applying the
// spell. It's most likely a buff that can't stack.