mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-12 01:11:29 +00:00
[Spells] Update SPA 339 SE_TriggerOnCast (#1478)
* Recoded SE_TriggerOnCast Focus effect Recoded SE_TriggerOnCast focus effect to be consistent with how all other focuses are checked. No longer an arbitrary limit as to number of a focus effects of this type you can have. * new command: resetdisc_timer usage: #resetdisc_timer [all | timer_id] * syntax fixes syntax improvements * minor fix changed numhits check * Update spell_effects.cpp * added better support for spell procs that don't require target. * syntax * Formatting and syntax tweaks Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
parent
93b0264a8b
commit
f01cf74fa6
@ -1282,3 +1282,14 @@ const char* GetSpellName(uint16 spell_id)
|
||||
return spells[spell_id].name;
|
||||
}
|
||||
|
||||
bool SpellRequiresTarget(int spell_id)
|
||||
{
|
||||
if (spells[spell_id].targettype == ST_AEClientV1 ||
|
||||
spells[spell_id].targettype == ST_Self ||
|
||||
spells[spell_id].targettype == ST_AECaster ||
|
||||
spells[spell_id].targettype == ST_Ring ||
|
||||
spells[spell_id].targettype == ST_Beam) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1151,6 +1151,7 @@ bool IsStackableDot(uint16 spell_id);
|
||||
bool IsBardOnlyStackEffect(int effect);
|
||||
bool IsCastWhileInvis(uint16 spell_id);
|
||||
bool IsEffectIgnoredInStacking(int spa);
|
||||
bool SpellRequiresTarget(int targettype);
|
||||
|
||||
int CalcPetHp(int levelb, int classb, int STA = 75);
|
||||
int GetSpellEffectDescNum(uint16 spell_id);
|
||||
|
||||
@ -1065,19 +1065,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_TriggerOnCast:
|
||||
|
||||
for (int i = 0; i < MAX_SPELL_TRIGGER; i++) {
|
||||
if (newbon->SpellTriggers[i] == rank.id)
|
||||
break;
|
||||
|
||||
if (!newbon->SpellTriggers[i]) {
|
||||
// Save the 'rank.id' of each triggerable effect to an array
|
||||
newbon->SpellTriggers[i] = rank.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_CriticalHitChance: {
|
||||
// Bad data or unsupported new skill
|
||||
@ -2538,19 +2525,6 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_TriggerOnCast:
|
||||
{
|
||||
for(int e = 0; e < MAX_SPELL_TRIGGER; e++)
|
||||
{
|
||||
if(!new_bonus->SpellTriggers[e])
|
||||
{
|
||||
new_bonus->SpellTriggers[e] = spell_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_SpellCritChance:
|
||||
new_bonus->CriticalSpellChance += effect_value;
|
||||
break;
|
||||
@ -3816,8 +3790,7 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff
|
||||
case SE_ReduceReuseTimer:
|
||||
return focusReduceRecastTime;
|
||||
case SE_TriggerOnCast:
|
||||
//return focusTriggerOnCast;
|
||||
return 0; //This is calculated as an actual bonus
|
||||
return focusTriggerOnCast;
|
||||
case SE_FcSpellVulnerability:
|
||||
return focusSpellVulnerability;
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
@ -4433,17 +4406,6 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_TriggerOnCast:
|
||||
{
|
||||
for(int e = 0; e < MAX_SPELL_TRIGGER; e++)
|
||||
{
|
||||
spellbonuses.SpellTriggers[e] = effect_value;
|
||||
aabonuses.SpellTriggers[e] = effect_value;
|
||||
itembonuses.SpellTriggers[e] = effect_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_SpellCritChance:
|
||||
spellbonuses.CriticalSpellChance = effect_value;
|
||||
aabonuses.CriticalSpellChance = effect_value;
|
||||
|
||||
@ -359,6 +359,7 @@ int command_init(void)
|
||||
command_add("repop", "[delay] - Repop the zone with optional delay", 100, command_repop) ||
|
||||
command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", 200, command_resetaa) ||
|
||||
command_add("resetaa_timer", "Command to reset AA cooldown timers.", 200, command_resetaa_timer) ||
|
||||
command_add("resetdisc_timer", "Command to reset all discipline cooldown timers.", 200, command_resetdisc_timer) ||
|
||||
command_add("revoke", "[charname] [1/0] - Makes charname unable to talk on OOC", 200, command_revoke) ||
|
||||
command_add("roambox", "Manages roambox settings for an NPC", 200, command_roambox) ||
|
||||
command_add("rules", "(subcommand) - Manage server rules", 250, command_rules) ||
|
||||
@ -8121,31 +8122,31 @@ void command_summonitem(Client *c, const Seperator *sep)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 2 && sep->IsNumber(2)) {
|
||||
charges = atoi(sep->arg[2]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 3 && sep->IsNumber(3)) {
|
||||
augment_one = atoi(sep->arg[3]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 4 && sep->IsNumber(4)) {
|
||||
augment_two = atoi(sep->arg[4]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 5 && sep->IsNumber(5)) {
|
||||
augment_three = atoi(sep->arg[5]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 6 && sep->IsNumber(6)) {
|
||||
augment_four = atoi(sep->arg[6]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 7 && sep->IsNumber(7)) {
|
||||
augment_five = atoi(sep->arg[7]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments == 8 && sep->IsNumber(8)) {
|
||||
augment_six = atoi(sep->arg[8]);
|
||||
}
|
||||
@ -8189,7 +8190,7 @@ void command_giveitem(Client *c, const Seperator *sep)
|
||||
c->Message(Chat::Red, "Usage: #giveitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Client *client_target = c->GetTarget()->CastToClient();
|
||||
uint8 item_status = 0;
|
||||
uint8 current_status = c->Admin();
|
||||
@ -8197,7 +8198,7 @@ void command_giveitem(Client *c, const Seperator *sep)
|
||||
if (item) {
|
||||
item_status = item->MinStatus;
|
||||
}
|
||||
|
||||
|
||||
if (item_status > current_status) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
@ -8209,23 +8210,23 @@ void command_giveitem(Client *c, const Seperator *sep)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 2 && sep->IsNumber(2)) {
|
||||
charges = atoi(sep->arg[2]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 3 && sep->IsNumber(3)) {
|
||||
augment_one = atoi(sep->arg[3]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 4 && sep->IsNumber(4)) {
|
||||
augment_two = atoi(sep->arg[4]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 5 && sep->IsNumber(5)) {
|
||||
augment_three = atoi(sep->arg[5]);
|
||||
}
|
||||
|
||||
|
||||
if (arguments >= 6 && sep->IsNumber(6)) {
|
||||
augment_four = atoi(sep->arg[6]);
|
||||
}
|
||||
@ -13725,6 +13726,27 @@ void command_resetaa_timer(Client *c, const Seperator *sep) {
|
||||
}
|
||||
}
|
||||
|
||||
void command_resetdisc_timer(Client *c, const Seperator *sep)
|
||||
{
|
||||
Client *target = c->GetTarget()->CastToClient();
|
||||
if (!c->GetTarget() || !c->GetTarget()->IsClient()) {
|
||||
target = c;
|
||||
}
|
||||
|
||||
if (sep->IsNumber(1)) {
|
||||
int timer_id = atoi(sep->arg[1]);
|
||||
c->Message(Chat::White, "Reset of disc timer %i for %s", timer_id, c->GetName());
|
||||
c->ResetDisciplineTimer(timer_id);
|
||||
}
|
||||
else if (!strcasecmp(sep->arg[1], "all")) {
|
||||
c->Message(Chat::White, "Reset all disc timers for %s", c->GetName());
|
||||
c->ResetAllDisciplineTimers();
|
||||
}
|
||||
else {
|
||||
c->Message(Chat::White, "usage: #resetdisc_timer [all | timer_id]");
|
||||
}
|
||||
}
|
||||
|
||||
void command_reloadaa(Client *c, const Seperator *sep) {
|
||||
c->Message(Chat::White, "Reloading Alternate Advancement Data...");
|
||||
zone->LoadAlternateAdvancement();
|
||||
|
||||
@ -256,6 +256,7 @@ void command_reloadzps(Client *c, const Seperator *sep);
|
||||
void command_repop(Client *c, const Seperator *sep);
|
||||
void command_resetaa(Client* c,const Seperator *sep);
|
||||
void command_resetaa_timer(Client *c, const Seperator *sep);
|
||||
void command_resetdisc_timer(Client *c, const Seperator *sep);
|
||||
void command_revoke(Client *c, const Seperator *sep);
|
||||
void command_roambox(Client *c, const Seperator *sep);
|
||||
void command_rules(Client *c, const Seperator *sep);
|
||||
|
||||
53
zone/mob.cpp
53
zone/mob.cpp
@ -3488,59 +3488,6 @@ void Mob::SetNimbusEffect(uint32 nimbus_effect)
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::TryTriggerOnCast(uint32 spell_id, bool aa_trigger)
|
||||
{
|
||||
if(!IsValidSpell(spell_id))
|
||||
return;
|
||||
|
||||
if (aabonuses.SpellTriggers[0] || spellbonuses.SpellTriggers[0] || itembonuses.SpellTriggers[0]){
|
||||
|
||||
for(int i = 0; i < MAX_SPELL_TRIGGER; i++){
|
||||
|
||||
if(aabonuses.SpellTriggers[i] && IsClient())
|
||||
TriggerOnCast(aabonuses.SpellTriggers[i], spell_id,1);
|
||||
|
||||
if(spellbonuses.SpellTriggers[i])
|
||||
TriggerOnCast(spellbonuses.SpellTriggers[i], spell_id,0);
|
||||
|
||||
if(itembonuses.SpellTriggers[i])
|
||||
TriggerOnCast(spellbonuses.SpellTriggers[i], spell_id,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger)
|
||||
{
|
||||
if (!IsValidSpell(focus_spell) || !IsValidSpell(spell_id))
|
||||
return;
|
||||
|
||||
uint32 trigger_spell_id = 0;
|
||||
|
||||
if (aa_trigger && IsClient()) {
|
||||
// focus_spell = aaid
|
||||
auto rank = zone->GetAlternateAdvancementRank(focus_spell);
|
||||
if (rank)
|
||||
trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, *rank, spell_id);
|
||||
|
||||
if (IsValidSpell(trigger_spell_id) && GetTarget())
|
||||
SpellFinished(trigger_spell_id, GetTarget(), EQ::spells::CastingSlot::Item, 0, -1,
|
||||
spells[trigger_spell_id].ResistDiff);
|
||||
}
|
||||
|
||||
else {
|
||||
trigger_spell_id = CalcFocusEffect(focusTriggerOnCast, focus_spell, spell_id);
|
||||
|
||||
if (IsValidSpell(trigger_spell_id) && GetTarget()) {
|
||||
SpellFinished(trigger_spell_id, GetTarget(), EQ::spells::CastingSlot::Item, 0, -1,
|
||||
spells[trigger_spell_id].ResistDiff);
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect)
|
||||
{
|
||||
if (!target || !IsValidSpell(spell_id))
|
||||
|
||||
@ -791,8 +791,8 @@ public:
|
||||
bool TryDeathSave();
|
||||
bool TryDivineSave();
|
||||
void DoBuffWearOffEffect(uint32 index);
|
||||
void TryTriggerOnCast(uint32 spell_id, bool aa_trigger);
|
||||
void TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger);
|
||||
void TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id);
|
||||
bool TryTriggerOnCastProc(uint16 focusspellid, uint16 spell_id, uint16 proc_spellid);
|
||||
bool TrySpellTrigger(Mob *target, uint32 spell_id, int effect);
|
||||
void TryTriggerOnValueAmount(bool IsHP = false, bool IsMana = false, bool IsEndur = false, bool IsPet = false);
|
||||
void TryTwincast(Mob *caster, Mob *target, uint32 spell_id);
|
||||
|
||||
@ -4463,7 +4463,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
when the next valid focus effect is found.
|
||||
*/
|
||||
|
||||
if (IsFocusEffect(0, 0, true, effect) || (effect == SE_TriggerOnCast)) {
|
||||
if (IsFocusEffect(0, 0, true, effect)) {
|
||||
FocusCount++;
|
||||
// If limit found on prior check next, else end loop.
|
||||
if (FocusCount > 1) {
|
||||
@ -5504,6 +5504,125 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
return (value * lvlModifier / 100);
|
||||
}
|
||||
|
||||
void Mob::TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id)
|
||||
{
|
||||
if (IsBardSong(spell_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32 focus_spell_id = 0;
|
||||
int32 proc_spellid = 0;
|
||||
|
||||
// item focus
|
||||
if (IsClient() && itembonuses.FocusEffects[type]) {
|
||||
const EQ::ItemData *temp_item = nullptr;
|
||||
|
||||
for (int x = EQ::invslot::EQUIPMENT_BEGIN; x <= EQ::invslot::EQUIPMENT_END; x++) {
|
||||
temp_item = nullptr;
|
||||
EQ::ItemInstance *ins = CastToClient()->GetInv().GetItem(x);
|
||||
if (!ins) {
|
||||
continue;
|
||||
}
|
||||
temp_item = ins->GetItem();
|
||||
if (temp_item && temp_item->Focus.Effect > 0 && IsValidSpell(temp_item->Focus.Effect)) {
|
||||
focus_spell_id = temp_item->Focus.Effect;
|
||||
if (!IsEffectInSpell(focus_spell_id, SE_TriggerOnCast)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
proc_spellid = CalcFocusEffect(type, focus_spell_id, spell_id);
|
||||
if (proc_spellid) {
|
||||
TryTriggerOnCastProc(focus_spell_id, spell_id, proc_spellid);
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = EQ::invaug::SOCKET_BEGIN; y <= EQ::invaug::SOCKET_END; ++y) {
|
||||
EQ::ItemInstance *aug = ins->GetAugment(y);
|
||||
if (aug) {
|
||||
const EQ::ItemData *temp_item_aug = aug->GetItem();
|
||||
if (temp_item_aug && temp_item_aug->Focus.Effect > 0 && IsValidSpell(temp_item_aug->Focus.Effect)) {
|
||||
focus_spell_id = temp_item_aug->Focus.Effect;
|
||||
|
||||
if (!IsEffectInSpell(focus_spell_id, SE_TriggerOnCast)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
proc_spellid = CalcFocusEffect(type, focus_spell_id, spell_id);
|
||||
if (proc_spellid) {
|
||||
TryTriggerOnCastProc(focus_spell_id, spell_id, proc_spellid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spell Focus
|
||||
if (spellbonuses.FocusEffects[type]) {
|
||||
int buff_slot = 0;
|
||||
for (buff_slot = 0; buff_slot < GetMaxTotalSlots(); buff_slot++) {
|
||||
focus_spell_id = buffs[buff_slot].spellid;
|
||||
if (!IsValidSpell(focus_spell_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsEffectInSpell(focus_spell_id, SE_TriggerOnCast)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
proc_spellid = CalcFocusEffect(type, focus_spell_id, spell_id);
|
||||
if (proc_spellid) {
|
||||
TryTriggerOnCastProc(focus_spell_id, spell_id, proc_spellid);
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, buff_slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only use of this focus per AA effect.
|
||||
if (IsClient() && aabonuses.FocusEffects[type]) {
|
||||
for (const auto &aa : aa_ranks) {
|
||||
auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first);
|
||||
auto ability = ability_rank.first;
|
||||
auto rank = ability_rank.second;
|
||||
|
||||
if (!ability) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rank->effects.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
proc_spellid = CastToClient()->CalcAAFocus(type, *rank, spell_id);
|
||||
if (proc_spellid) {
|
||||
TryTriggerOnCastProc(0, spell_id, proc_spellid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::TryTriggerOnCastProc(uint16 focusspellid, uint16 spell_id, uint16 proc_spellid)
|
||||
{
|
||||
// We confirm spell_id and focuspellid are valid before passing into this.
|
||||
if (IsValidSpell(proc_spellid) && spell_id != focusspellid && spell_id != proc_spellid) {
|
||||
Mob* proc_target = GetTarget();
|
||||
if (proc_target) {
|
||||
SpellFinished(proc_spellid, proc_target, EQ::spells::CastingSlot::Item, 0, -1, spells[proc_spellid].ResistDiff);
|
||||
return true;
|
||||
}
|
||||
// Edge cases where proc spell does not require a target such as PBAE, allows proc to still occur even if target potentially dead. Live spells exist with PBAE procs.
|
||||
else if (!SpellRequiresTarget(proc_spellid)) {
|
||||
SpellFinished(proc_spellid, this, EQ::spells::CastingSlot::Item, 0, -1, spells[proc_spellid].ResistDiff);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
|
||||
|
||||
if (IsBardSong(spell_id))
|
||||
|
||||
@ -1384,11 +1384,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
|
||||
TrySympatheticProc(target, spell_id);
|
||||
}
|
||||
|
||||
TryOnSpellFinished(this, target, spell_id);
|
||||
TryOnSpellFinished(this, target, spell_id); //Use for effects that should be checked after SpellFinished is completed.
|
||||
|
||||
TryTwincast(this, target, spell_id);
|
||||
|
||||
TryTriggerOnCast(spell_id, 0);
|
||||
TryTriggerOnCastFocusEffect(focusTriggerOnCast, spell_id);
|
||||
|
||||
if(DeleteChargeFromSlot >= 0)
|
||||
CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user