Skip to content

Commit

Permalink
[Spells] Implemented SPA 476 SE_Weapons_Stance and Live-like AA Enabl…
Browse files Browse the repository at this point in the history
…e/Disable Toggle (#1477)

* Work started on SPA 476

defines

* bonus structure add

bonus structure set up

* updates spa476

updates spa476

* spell bonus now functional

spell bonus working well.

* major update with debug messages

aa, item and spell now working

* Pre clean up, effect implemented

working for AA, spells, items, all checked for stacking issues.

* removed debug messages

removed debug messages

* spdat description added

spdat description added

* minor fix

removed debug shout
removed unneeded code check.

* syntax updates, minor fixes

syntax updates, minor fixes

* syntax fixes

syntax fixes

* improvements to code

moved function to check at swap item.  Easier to manage and more live like behavior. Required minor adjustment
Still working on AA toggle.

* updates to aa buy, functionalish

* Syntax / Formatting

* Add break / default to switch

* updates

* completed v2

* Major revisions

Main function check moved to when items are swapped and out of when ever bonus are recalculated.

AA Toggle and data structure now more accurate to live.

* Update aa.cpp

* debug removed

* implemented SE_Buy_AA_Rank

Closer to live.

* Update aa.cpp

broadening AA toggle to be more general use.

* improved various checks

aa toggle is now broadly implemented to be usable with any passive effect.

Co-authored-by: Akkadius <akkadius1@gmail.com>
  • Loading branch information
KayenEQ and Akkadius committed Aug 10, 2021
1 parent c69446c commit 51ad6d6
Show file tree
Hide file tree
Showing 12 changed files with 548 additions and 35 deletions.
1 change: 1 addition & 0 deletions common/spdat.cpp
Expand Up @@ -1236,6 +1236,7 @@ bool IsEffectIgnoredInStacking(int spa)
case SE_Ff_CasterClass:
case SE_Ff_Same_Caster:
case SE_Proc_Timer_Modifier:
case SE_Weapon_Stance:
case SE_TwinCastBlocker:
case SE_Fc_CastTimeAmt:
case SE_Fc_CastTimeMod2:
Expand Down
5 changes: 3 additions & 2 deletions common/spdat.h
Expand Up @@ -64,6 +64,7 @@
#define SPELL_SHAPECHANGE70 6503
#define SPELL_MANA_BURN 2751
#define SPELL_LIFE_BURN 2755
#define SPELL_TOUCH_OF_THE_DIVINE 4789
// these have known hardcoded behavior but we don't do anything yet, move them above this comment when fixed
#define SPELL_THE_DAINS_JUSTICE 1476
#define SPELL_MODULATION 1502
Expand Down Expand Up @@ -834,11 +835,11 @@ typedef enum {
#define SE_Chance_Best_in_Spell_Grp 469 // implemented - Chance to cast highest scribed spell within a spell group. All base2 spells share roll chance, only 1 cast.
#define SE_Trigger_Best_in_Spell_Grp 470 // implemented - Chance to cast highest scribed spell within a spell group. Each spell has own chance.
//#define SE_Double_Melee_Round 471 //
//#define SE_Buy_AA_Rank 472 //
#define SE_Buy_AA_Rank 472 // implemented, @Special, Used in AA abilities that have Enable/Disable toggle. Spell on Disabled Rank has this effect in it, base: 1, limit: none, max: none, Note: This will not just buy an AA
#define SE_Double_Backstab_Front 473 // implemented - Chance to double backstab from front
#define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 // implemenetd - Critical damage mod applied to pets from owner
#define SE_Trigger_Spell_Non_Item 475 // implemented - Trigger spell on cast only if not from item click.
//#define SE_Weapon_Stance 476 //
#define SE_Weapon_Stance 476 // implemented, @Misc, Apply a specific spell buffs automatically depending 2Hander, Shield or Duel Wield is equiped, base: spellid, base: 0=2H 1=Shield 2=DW, max: none
#define SE_Hatelist_To_Top_Index 477 // Implemented - Chance to be set to top of rampage list
#define SE_Hatelist_To_Tail_Index 478 // Implemented - Chance to be set to bottom of rampage list
#define SE_Ff_Value_Min 479 // implemented, @Ff, Minimum base value of a spell that can be focused, base: spells to be focused base1 value
Expand Down
208 changes: 191 additions & 17 deletions zone/aa.cpp
Expand Up @@ -1162,6 +1162,7 @@ void Client::IncrementAlternateAdvancementRank(int rank_id) {

void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id);

if(!rank) {
return;
}
Expand All @@ -1178,17 +1179,18 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
if(!CanUseAlternateAdvancementRank(rank)) {
return;
}

bool use_toggle_passive_hotkey = UseTogglePassiveHotkey(*rank);

//make sure it is not a passive
if(!rank->effects.empty()) {
if(!rank->effects.empty() && !use_toggle_passive_hotkey) {
return;
}

uint32 charges = 0;
// We don't have the AA
if (!GetAA(rank_id, &charges))
return;

//if expendable make sure we have charges
if(ability->charges > 0 && charges < 1)
return;
Expand Down Expand Up @@ -1241,15 +1243,21 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
}
}

// Bards can cast instant cast AAs while they are casting another song
if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
return;
if (use_toggle_passive_hotkey) {
TogglePassiveAlternativeAdvancement(*rank, ability->id);
}
else {
// Bards can cast instant cast AAs while they are casting another song
if (spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
return;
}
ExpendAlternateAdvancementCharge(ability->id);
}
ExpendAlternateAdvancementCharge(ability->id);
} else {
if(!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
return;
else {
if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
return;
}
}
}

Expand Down Expand Up @@ -1287,16 +1295,16 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) {
}

void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
for(auto &iter : aa_ranks) {
for (auto &iter : aa_ranks) {
AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first);
if(ability && aa_id == ability->id) {
if(iter.second.second > 0) {
if (ability && aa_id == ability->id) {
if (iter.second.second > 0) {
iter.second.second -= 1;

if(iter.second.second == 0) {
if(IsClient()) {
if (iter.second.second == 0) {
if (IsClient()) {
AA::Rank *r = ability->GetRankByPointsSpent(iter.second.first);
if(r) {
if (r) {
CastToClient()->GetEPP().expended_aa += r->cost;
}
}
Expand All @@ -1307,7 +1315,7 @@ void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
aa_ranks.erase(iter.first);
}

if(IsClient()) {
if (IsClient()) {
Client *c = CastToClient();
c->SaveAA();
c->SendAlternateAdvancementPoints();
Expand Down Expand Up @@ -1796,3 +1804,169 @@ bool Mob::CheckAATimer(int timer)
}
return false;
}

void Client::TogglePassiveAlternativeAdvancement(const AA::Rank &rank, uint32 ability_id)
{
/*
Certain AA, like Weapon Stance line use a special toggle Hotkey to enable or disable the AA's passive abilities.
This is occurs by doing the following. Each 'rank' of Weapon Stance is actually 2 actual ranks.
First rank is always the Disabled version which cost X amount of AA. Second rank is the Enabled version which cost 0 AA.
When you buy the first rank, you make a hotkey that on live say 'Weapon Stance Disabled', if you clik that it then BUYS the
next rank of AA (cost 0) which switches the hotkey to 'Enabled Weapon Stance' and you are given the passive buff effects.
If you click the Enabled hotkey, it causes you to lose an AA rank and once again be disabled. Thus, you are switching between
two AA ranks. Thefore when creating an AA using this ability, you need generate both ranks. Follow the same pattern for additional ranks.
IMPORTANT! The toggle system can be used to Enable or Disable ANY passive AA. You just need to follow the instructions on how to create it.
Example: Enable or Disable a buff that gives a large hate modifier. Play may Enable when tanking and Disable when DPS ect.
Note: On live the Enabled rank is shown having a Charge of 1, while Disabled rank has no charges. Our current code doesn't support that. Do not use charges.
Note: Live uses a spell 'Disable Ability' ID 46164 to trigger a script to do the AA rank changes. At present time it is not coded to require that, any spell id works.
Note: Discovered a bug on ROF2, where when you buy first rank of an AA with a hotkey, it will always display the title of the second rank in the database. Be aware. No easy fix.
Dev Note(Kayen 8/1/21): The system as set up is very similar to live, with exception that live gives the Enabled rank 1 Charge. The code here emulates what happens when a
charge would be expended.
Instructions for how to make the AA - assuming a basic level of knowledge of how AA's work.
- aa_abilities table : Create new ability with a hotkey, type 3, zero charges
- aa_ranks table : [Disabled rank] First rank, should have a cost > 0 (this is what you buy), Set hotkeys, MUST SET A SPELL CONTAINING EFFECT SE_Buy_AA_Rank(SPA 472), set a short recast timer.
[Enabled rank] Second rank, should have a cost = 0, Set hotkeys, Set any valid spell ID you want (it has to exist but does nothing), set a short recast timer.
*Recommend if doing custom, just make the hotkey titled 'Toggle <Ability Name>' and use for both.
- aa_rank_effects table : [Disabled rank] No data needed in the aa_ranks_effect table
[Enabled rank] Second rank set effect_id = 457 (weapon stance), slot 1,2,3, base1= spell triggers, base= weapon type (0=2H,1=SH,2=DW), for slot 1,2,3
Example SQL -Disabled
DO NOT ADD any data to the aa_rank_effects for this rank_id
-Enabled
INSERT INTO aa_rank_effects (rank_id, slot, effect_id, base1, base2) VALUES (20003, 1, 476, 145,0);
INSERT INTO aa_rank_effects (rank_id, slot, effect_id, base1, base2) VALUES (20003, 2, 476, 174,1);
INSERT INTO aa_rank_effects (rank_id, slot, effect_id, base1, base2) VALUES (20003, 3, 476, 172,2);
Warning: If you want to design an AA that only uses one weapon type to trigger, like will only apply buff if Shield. Do not include data for other types. Never have a base value=0
in the Enabled rank.
*/

bool enable_next_rank = IsEffectInSpell(rank.spell, SE_Buy_AA_Rank);

if (enable_next_rank) {

//Enable
TogglePurchaseAlternativeAdvancementRank(rank.next_id);
Message(Chat::Spells, "You enable an ability."); //Message live gives you. Should come from spell.

AA::Rank *rank_next = zone->GetAlternateAdvancementRank(rank.next_id);

//Add checks for any special cases for toggle.
if (IsEffectinAlternateAdvancementRankEffects(*rank_next, SE_Weapon_Stance)) {
weaponstance.aabonus_enabled = true;
ApplyWeaponsStance();
}
return;
}
else {

//Disable
ResetAlternateAdvancementRank(ability_id);
TogglePurchaseAlternativeAdvancementRank(rank.prev_id);
Message(Chat::Spells, "You disable an ability."); //Message live gives you. Should come from spell.

//Add checks for any special cases for toggle.
if (IsEffectinAlternateAdvancementRankEffects(rank, SE_Weapon_Stance)) {
weaponstance.aabonus_enabled = false;
BuffFadeBySpellID(weaponstance.aabonus_buff_spell_id);
}
return;
}
}

bool Client::UseTogglePassiveHotkey(const AA::Rank &rank) {

/*
Disabled rank needs a rank spell containing the SE_Buy_AA_Rank effect to return true.
Enabled rank checks to see if the prior rank contains a rank spell with SE_Buy_AA_Rank, if so true.
Note: On live the enabled rank is Expendable with Charge 1.
We have already confirmed the rank spell is valid before this function is called.
*/


if (IsEffectInSpell(rank.spell, SE_Buy_AA_Rank)) {//Checked when is Disabled.
return true;
}
else if (rank.prev_id != -1) {//Check when effect is Enabled.
AA::Rank *rank_prev = zone->GetAlternateAdvancementRank(rank.prev_id);

if (IsEffectInSpell(rank_prev->spell, SE_Buy_AA_Rank)) {
return true;
}
}
return false;
}

bool Client::IsEffectinAlternateAdvancementRankEffects(const AA::Rank &rank, int effect_id) {

for (const auto &e : rank.effects) {

if (e.effect_id == effect_id) {
return true;
}
}
return false;
}

void Client::ResetAlternateAdvancementRank(uint32 aa_id) {

/*
Resets your AA to baseline
*/

for(auto &iter : aa_ranks) {

AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first);

if(ability && aa_id == ability->id) {
RemoveExpendedAA(ability->first_rank_id);
aa_ranks.erase(iter.first);
SaveAA();
SendAlternateAdvancementPoints();
return;
}
}
}

void Client::TogglePurchaseAlternativeAdvancementRank(int rank_id){

/*
Stripped down version of purchasing AA. Will give no messages.
Used with toggle hotkey functions.
*/

AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id);
if (!rank) {
return;
}

if (!rank->base_ability) {
return;
}

if (!CanPurchaseAlternateAdvancementRank(rank, false, false)) {
return;
}

rank_id = rank->base_ability->first_rank_id;
SetAA(rank_id, rank->current_value, 0);

if (rank->next) {
SendAlternateAdvancementRank(rank->base_ability->id, rank->next->current_value);
}

SaveAA();
SendAlternateAdvancementPoints();
SendAlternateAdvancementStats();
CalcBonuses();
}

61 changes: 60 additions & 1 deletion zone/bonuses.cpp
Expand Up @@ -154,6 +154,7 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
SetShieldEquiped(false);
SetTwoHandBluntEquiped(false);
SetTwoHanderEquipped(false);
SetDuelWeaponsEquiped(false);

unsigned int i;
// Update: MainAmmo should only calc skill mods (TODO: Check for other cases)
Expand All @@ -171,8 +172,13 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
SetTwoHandBluntEquiped(true);
SetTwoHanderEquipped(true);
}
else if (i == EQ::invslot::slotPrimary && (item && (item->ItemType == EQ::item::ItemType2HSlash || item->ItemType == EQ::item::ItemType2HPiercing)))
else if (i == EQ::invslot::slotPrimary && (item && (item->ItemType == EQ::item::ItemType2HSlash || item->ItemType == EQ::item::ItemType2HPiercing))) {
SetTwoHanderEquipped(true);
}
}

if (CanThisClassDualWield()) {
SetDuelWeaponsEquiped(true);
}

//tribute items
Expand Down Expand Up @@ -1555,6 +1561,25 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
newbon->Pet_Add_Atk += base1;
break;

case SE_Weapon_Stance:
{
if (IsValidSpell(base1)) { //base1 is the spell_id of buff
if (base2 <= WEAPON_STANCE_TYPE_MAX) { //0=2H, 1=Shield, 2=DW
if (IsValidSpell(newbon->WeaponStance[base2])) { //Check if we already a spell_id saved for this effect
if (spells[newbon->WeaponStance[base2]].rank < spells[base1].rank) { //If so, check if any new spellids with higher rank exist (live spells for this are ranked).
newbon->WeaponStance[base2] = base1; //Overwrite with new effect
SetWeaponStanceEnabled(true);
}
}
else {
newbon->WeaponStance[base2] = base1; //If no prior effect exists, then apply
SetWeaponStanceEnabled(true);
}
}
}
break;
}

case SE_ExtraAttackChance:
{
if (newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] < base1) {
Expand Down Expand Up @@ -1583,6 +1608,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
break;
}


// to do
case SE_PetDiscipline:
break;
Expand All @@ -1603,6 +1629,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
case SE_TrapCircumvention:
break;


// not handled here
case SE_HastenedAASkill:
// not handled here but don't want to clutter debug log -- these may need to be verified to ignore
Expand Down Expand Up @@ -3474,6 +3501,38 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
new_bonus->Pet_Add_Atk += effect_value;
break;

case SE_Weapon_Stance: {
if (IsValidSpell(effect_value)) { //base1 is the spell_id of buff
if (base2 <= WEAPON_STANCE_TYPE_MAX) { //0=2H, 1=Shield, 2=DW
if (IsValidSpell(new_bonus->WeaponStance[base2])) { //Check if we already a spell_id saved for this effect
if (spells[new_bonus->WeaponStance[base2]].rank < spells[effect_value].rank) { //If so, check if any new spellids with higher rank exist (live spells for this are ranked).
new_bonus->WeaponStance[base2] = effect_value; //Overwrite with new effect
SetWeaponStanceEnabled(true);

if (WornType) {
weaponstance.itembonus_enabled = true;
}
else {
weaponstance.spellbonus_enabled = true;
}
}
}
else {
new_bonus->WeaponStance[base2] = effect_value; //If no prior effect exists, then apply
SetWeaponStanceEnabled(true);

if (WornType) {
weaponstance.itembonus_enabled = true;
}
else {
weaponstance.spellbonus_enabled = true;
}
}
}
}
break;
}

//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
if (IsAISpellEffect) {

Expand Down

0 comments on commit 51ad6d6

Please sign in to comment.