[Spells] Implemented SPA 476 SE_Weapons_Stance and Live-like AA Enable/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>
This commit is contained in:
KayenEQ 2021-08-10 15:46:37 -04:00 committed by GitHub
parent c69446c460
commit 51ad6d65dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 548 additions and 35 deletions

View File

@ -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:

View File

@ -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
@ -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

View File

@ -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;
}
@ -1178,9 +1179,11 @@ 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;
}
@ -1188,7 +1191,6 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
// 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;
@ -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;
}
}
}
@ -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;
}
}
@ -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();
@ -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();
}

View File

@ -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)
@ -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
@ -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) {
@ -1583,6 +1608,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
break;
}
// to do
case SE_PetDiscipline:
break;
@ -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
@ -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) {

View File

@ -1412,7 +1412,7 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) {
mmc_points += (mir_points + m_pp.ldon_points_mir);
mir_points = (0 - m_pp.ldon_points_mir);
}
if(m_pp.ldon_points_mmc < (0 - mmc_points)) {
ruj_points += (mmc_points + m_pp.ldon_points_mmc);
mmc_points = (0 - m_pp.ldon_points_mmc);
@ -2419,9 +2419,9 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who,
parse->EventPlayer(EVENT_USE_SKILL, this, buffer, 0);
if (against_who) {
if (
against_who->GetSpecialAbility(IMMUNE_AGGRO) ||
against_who->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) ||
against_who->IsClient() ||
against_who->GetSpecialAbility(IMMUNE_AGGRO) ||
against_who->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) ||
against_who->IsClient() ||
GetLevelCon(against_who->GetLevel()) == CON_GRAY
) {
//false by default
@ -9966,7 +9966,7 @@ void Client::MovePCDynamicZone(const std::string& zone_name, int zone_version, b
MovePCDynamicZone(zone_id, zone_version, msg_if_invalid);
}
void Client::Fling(float value, float target_x, float target_y, float target_z, bool ignore_los, bool clipping) {
void Client::Fling(float value, float target_x, float target_y, float target_z, bool ignore_los, bool clipping) {
BuffFadeByEffect(SE_Levitate);
if (CheckLosFN(target_x, target_y, target_z, 6.0f) || ignore_los) {
auto outapp_fling = new EQApplicationPacket(OP_Fling, sizeof(fling_struct));
@ -9975,7 +9975,7 @@ void Client::Fling(float value, float target_x, float target_y, float target_z,
flingTo->collision = 0;
else
flingTo->collision = -1;
flingTo->travel_time = -1;
flingTo->unk3 = 1;
flingTo->disable_fall_damage = 1;
@ -10030,7 +10030,7 @@ std::vector<int> Client::GetLearnableDisciplines(uint8 min_level, uint8 max_leve
if (learnable) {
learnable_disciplines.push_back(spell_id);
}
}
}
return learnable_disciplines;
}
@ -10040,7 +10040,7 @@ std::vector<int> Client::GetLearnedDisciplines() {
if (IsValidSpell(m_pp.disciplines.values[index])) {
learned_disciplines.push_back(m_pp.disciplines.values[index]);
}
}
}
return learned_disciplines;
}
@ -10050,7 +10050,7 @@ std::vector<int> Client::GetMemmedSpells() {
if (IsValidSpell(m_pp.mem_spells[index])) {
memmed_spells.push_back(m_pp.mem_spells[index]);
}
}
}
return memmed_spells;
}
@ -10096,7 +10096,7 @@ std::vector<int> Client::GetScribeableSpells(uint8 min_level, uint8 max_level) {
if (scribeable) {
scribeable_spells.push_back(spell_id);
}
}
}
return scribeable_spells;
}
@ -10174,7 +10174,7 @@ void Client::SendToInstance(std::string instance_type, std::string zone_short_na
return;
}
DataBucket::SetData(full_bucket_name, itoa(instance_id), itoa(duration));
DataBucket::SetData(full_bucket_name, itoa(instance_id), itoa(duration));
}
AssignToInstance(instance_id);
@ -10221,7 +10221,7 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity)
};
int removed_count = 0;
const size_t size = sizeof(slots) / sizeof(slots[0]);
for (int slot_index = 0; slot_index < size; ++slot_index) {
for (int slot_index = 0; slot_index < size; ++slot_index) {
for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) {
if (removed_count == quantity) {
break;
@ -10249,3 +10249,220 @@ void Client::SetGMStatus(int newStatus) {
if (this->Admin() != newStatus)
database.UpdateGMStatus(this->AccountID(), newStatus);
}
void Client::ApplyWeaponsStance()
{
/*
If you have a weapons stance bonus from at least one bonus type, each time you change weapons this function will ensure the correct
associated buffs are applied, and previous buff is removed. If your weapon stance bonus is completely removed it will, ensure buff is
also removed (ie, removing an item that has worn effect with weapon stance, or clicking off a buff). If client no longer has/never had
any spells/item/aa bonuses with weapon stance effect this function will only do a simple bool check.
Note: Live like behavior is once you have the triggered buff you can manually click it off to remove it. Swaping any items in inventory will
reapply it automatically.
Only buff spells should be used as triggered spell effect. IsBuffSpell function also checks spell id validity.
WeaponStance bonus arrary: 0=2H Weapon 1=Shield 2=Dualweild
Toggling ON or OFF
- From spells, just remove the Primary buff that contains the WeaponStance effect in it.
- For items with worn effect, unequip the item.
- For AA abilities, a hotkey is used to Enable and Disable the effect. See. Client::TogglePassiveAlternativeAdvancement in aa.cpp for extensive details.
Rank
- Most important for AA, but if you have more than one of WeaponStance effect for a given type, the spell trigger buff will apply whatever has the highest
'rank' value from the spells table. AA's on live for this effect naturally do this. Be awere of this if making custom spells/worn effects/AA.
When creating weapon stance effects, you do not need to use all three types. For example, can make an effect where you only get a buff from equiping shield.
*/
if (!IsWeaponStanceEnabled()) {
return;
}
bool enabled = false;
bool item_bonus_exists = false;
bool aa_bonus_exists = false;
if (weaponstance.spellbonus_enabled) {
if (spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] || spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] ||
spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) {
enabled = true;
// Check if no longer has correct combination of weapon type and buff, if so remove buff.
if (!HasTwoHanderEquipped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]) &&
FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) {
BuffFadeBySpellID(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]);
}
else if (!HasShieldEquiped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]) &&
FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) {
BuffFadeBySpellID(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]);
}
else if (!HasDualWeaponsEquiped() &&
IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) &&
FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
BuffFadeBySpellID(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]);
}
// If you have correct combination of weapon type and bonus, and do not already have buff, then apply buff.
if (HasTwoHanderEquipped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) {
if (!FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) {
SpellOnTarget(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H], this);
}
weaponstance.spellbonus_buff_spell_id = spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H];
}
else if (HasShieldEquiped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) {
if (!FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) {
SpellOnTarget(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD], this);
}
weaponstance.spellbonus_buff_spell_id = spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD];
}
else if (HasDualWeaponsEquiped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
if (!FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
SpellOnTarget(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD], this);
}
weaponstance.spellbonus_buff_spell_id = spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD];
}
}
}
// Spellbonus effect removal is checked in BuffFadeBySlot(int slot, bool iRecalcBonuses) in spell_effects.cpp when the buff is clicked off or fades.
if (weaponstance.itembonus_enabled) {
if (itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] || itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] ||
itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) {
enabled = true;
item_bonus_exists = true;
// Edge case check if have multiple items with WeaponStance worn effect. Make sure correct buffs are applied if items are removed but others left on.
if (weaponstance.itembonus_buff_spell_id) {
bool buff_desync = true;
if (weaponstance.itembonus_buff_spell_id == itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] ||
weaponstance.itembonus_buff_spell_id == itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] ||
(weaponstance.itembonus_buff_spell_id == itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
buff_desync = false;
}
if (buff_desync) {
int fade_spell = weaponstance.itembonus_buff_spell_id;
weaponstance.itembonus_buff_spell_id = 0; //Need to zero this before we fade to prevent any recursive loops.
BuffFadeBySpellID(fade_spell);
}
}
// Check if no longer has correct combination of weapon type and buff, if so remove buff.
if (!HasTwoHanderEquipped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]) &&
FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) {
BuffFadeBySpellID(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]);
}
else if (!HasShieldEquiped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]) &&
FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) {
BuffFadeBySpellID(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]);
}
else if (!HasDualWeaponsEquiped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) &&
FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
BuffFadeBySpellID(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]);
}
// If you have correct combination of weapon type and bonus, and do not already have buff, then apply buff.
if (HasTwoHanderEquipped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) {
if (!FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) {
SpellOnTarget(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H], this);
}
weaponstance.itembonus_buff_spell_id = itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H];
}
else if (HasShieldEquiped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) {
if (!FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) {
SpellOnTarget(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD], this);
}
weaponstance.itembonus_buff_spell_id = itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD];
}
else if (HasDualWeaponsEquiped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
if (!FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
SpellOnTarget(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD], this);
}
weaponstance.itembonus_buff_spell_id = itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD];
}
}
}
// Itembonus effect removal when item is removed
if (!item_bonus_exists && weaponstance.itembonus_enabled) {
weaponstance.itembonus_enabled = false;
if (weaponstance.itembonus_buff_spell_id) {
BuffFadeBySpellID(weaponstance.itembonus_buff_spell_id);
weaponstance.itembonus_buff_spell_id = 0;
}
}
if (weaponstance.aabonus_enabled) {
if (aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] || aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] ||
aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) {
enabled = true;
aa_bonus_exists = true;
//Check if no longer has correct combination of weapon type and buff, if so remove buff.
if (!HasTwoHanderEquipped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]) &&
FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) {
BuffFadeBySpellID(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]);
}
else if (!HasShieldEquiped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]) &&
FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) {
BuffFadeBySpellID(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]);
}
else if (!HasDualWeaponsEquiped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) &&
FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
BuffFadeBySpellID(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]);
}
//If you have correct combination of weapon type and bonus, and do not already have buff, then apply buff.
if (HasTwoHanderEquipped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) {
if (!FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) {
SpellOnTarget(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H], this);
}
weaponstance.aabonus_buff_spell_id = aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H];
}
else if (HasShieldEquiped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) {
if (!FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) {
SpellOnTarget(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD], this);
}
weaponstance.aabonus_buff_spell_id = aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD];
}
else if (HasDualWeaponsEquiped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
if (!FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) {
SpellOnTarget(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD], this);
}
weaponstance.aabonus_buff_spell_id = aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD];
}
}
}
// AA bonus removal is checked in TogglePassiveAA in aa.cpp. when the hot key is toggled.
// If no bonuses remain present, prevent additional future checks until new bonus is applied.
if (!enabled) {
SetWeaponStanceEnabled(false);
weaponstance.aabonus_enabled = false;
weaponstance.itembonus_enabled = false;
weaponstance.spellbonus_enabled = false;
}
}

View File

@ -1554,6 +1554,13 @@ public:
void ShowNumHits(); // work around function for numhits not showing on buffs
void ApplyWeaponsStance();
void TogglePassiveAlternativeAdvancement(const AA::Rank &rank, uint32 ability_id);
bool UseTogglePassiveHotkey(const AA::Rank &rank);
void TogglePurchaseAlternativeAdvancementRank(int rank_id);
void ResetAlternateAdvancementRank(uint32 aa_id);
bool IsEffectinAlternateAdvancementRankEffects(const AA::Rank &rank, int effect_id);
void TripInterrogateInvState() { interrogateinv_flag = true; }
bool GetInterrogateInvState() { return interrogateinv_flag; }

View File

@ -1717,6 +1717,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Task Packets */
LoadClientTaskState();
ApplyWeaponsStance();
m_expedition_id = ExpeditionsRepository::GetIDByMemberID(database, CharacterID());
/**

View File

@ -106,6 +106,8 @@
#define PET_BUTTON_SPELLHOLD 9
#define AURA_HARDCAP 2
#define WEAPON_STANCE_TYPE_MAX 2
typedef enum { //focus types
focusSpellHaste = 1, //@Fc, SPA: 127, SE_IncreaseSpellHaste, On Caster, cast time mod pct, base: pct
@ -325,7 +327,7 @@ struct Buffs_Struct {
int32 ExtraDIChance;
int16 RootBreakChance; //Not saved to dbase
uint32 instrument_mod;
int16 focusproclimit_time; //timer to limit number of procs from focus effects
int16 focusproclimit_time; //timer to limit number of procs from focus effects
int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set
bool persistant_buff;
bool client; //True if the caster is a client
@ -545,7 +547,7 @@ struct StatBonuses {
int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce
int32 Pet_Crit_Melee_Damage_Pct_Owner; // base = percent mod for pet critcal damage from owner
int32 Pet_Add_Atk; // base = Pet ATK bonus from owner
int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW
// AAs
int8 Packrat; //weight reduction for items, 1 point = 10%
@ -682,6 +684,20 @@ struct Shielders_Struct {
uint16 shielder_bonus;
};
struct WeaponStance_Struct {
bool enabled;
bool spellbonus_enabled;
bool itembonus_enabled;
bool aabonus_enabled;
int spellbonus_buff_spell_id;
int itembonus_buff_spell_id;
int aabonus_buff_spell_id;
};
constexpr uint16 WEAPON_STANCE_TYPE_2H = 0;
constexpr uint16 WEAPON_STANCE_TYPE_SHIELD = 1;
constexpr uint16 WEAPON_STANCE_TYPE_DUAL_WIELD = 2;
typedef struct
{
uint16 increment;

View File

@ -2026,6 +2026,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
// Step 8: Re-calc stats
CalcBonuses();
ApplyWeaponsStance();
return true;
}

View File

@ -234,6 +234,7 @@ Mob::Mob(
has_shieldequiped = false;
has_twohandbluntequiped = false;
has_twohanderequipped = false;
has_duelweaponsequiped = false;
can_facestab = false;
has_numhits = false;
has_MGB = false;
@ -408,6 +409,14 @@ Mob::Mob(
viral_spells[i] = 0;
}
weaponstance.enabled = false;
weaponstance.spellbonus_enabled = false; //Set when bonus is applied
weaponstance.itembonus_enabled = false; //Set when bonus is applied
weaponstance.aabonus_enabled = false; //Controlled by function TogglePassiveAA
weaponstance.spellbonus_buff_spell_id = 0;
weaponstance.itembonus_buff_spell_id = 0;
weaponstance.aabonus_buff_spell_id = 0;
pStandingPetOrder = SPO_Follow;
pseudo_rooted = false;

View File

@ -432,6 +432,8 @@ public:
inline void SetTwoHandBluntEquiped(bool val) { has_twohandbluntequiped = val; }
bool HasTwoHanderEquipped() { return has_twohanderequipped; }
void SetTwoHanderEquipped(bool val) { has_twohanderequipped = val; }
bool HasDualWeaponsEquiped() const { return has_duelweaponsequiped; }
inline void SetDuelWeaponsEquiped(bool val) { has_duelweaponsequiped = val; }
bool CanFacestab() { return can_facestab; }
void SetFacestab(bool val) { can_facestab = val; }
virtual uint16 GetSkill(EQ::skills::SkillType skill_num) const { return 0; }
@ -1128,6 +1130,10 @@ public:
Shielders_Struct shielder[MAX_SHIELDERS];
Trade* trade;
WeaponStance_Struct weaponstance;
bool IsWeaponStanceEnabled() const { return weaponstance.enabled; }
inline void SetWeaponStanceEnabled(bool val) { weaponstance.enabled = val; }
inline glm::vec4 GetCurrentWayPoint() const { return m_CurrentWayPoint; }
inline float GetCWPP() const { return(static_cast<float>(cur_wp_pause)); }
inline int GetCWP() const { return(cur_wp); }
@ -1489,6 +1495,7 @@ protected:
bool has_shieldequiped;
bool has_twohandbluntequiped;
bool has_twohanderequipped;
bool has_duelweaponsequiped;
bool can_facestab;
bool has_numhits;
bool has_MGB;

View File

@ -2950,6 +2950,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break;
}
case SE_Weapon_Stance: {
if (IsClient()) {
CastToClient()->ApplyWeaponsStance();
}
break;
}
case SE_PersistentEffect:
MakeAura(spell_id);
break;
@ -3229,6 +3236,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_AddExtraAttackPct_1h_Primary:
case SE_AddExtraAttackPct_1h_Secondary:
case SE_Skill_Base_Damage_Mod:
case SE_Buy_AA_Rank:
{
break;
@ -4359,6 +4367,17 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
}
}
case SE_Weapon_Stance:
{
/*
If we click off the spell buff (or fades naturally) giving us
Weapon Stance effects it should remove all associated buff.
*/
if (weaponstance.spellbonus_buff_spell_id) {
BuffFadeBySpellID(weaponstance.spellbonus_buff_spell_id);
}
weaponstance.spellbonus_enabled = false;
}
}
}
@ -6585,7 +6604,7 @@ bool Mob::TryDivineSave()
}
}
SpellOnTarget(4789, this); //Touch of the Divine=4789, an Invulnerability/HoT/Purify effect
SpellOnTarget(SPELL_TOUCH_OF_THE_DIVINE, this); //Touch of the Divine=4789, an Invulnerability/HoT/Purify effect
SendHPUpdate();
return true;
}