diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index e562a94e5..7b604fc47 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4319,7 +4319,7 @@ struct SendAA_Struct { struct AA_Action { /*00*/ uint32 action; /*04*/ uint32 ability; -/*08*/ uint32 unknown08; +/*08*/ uint32 target_id; /*12*/ uint32 exp_value; }; diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 0596ee95b..e76f33bbe 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2810,7 +2810,7 @@ namespace RoF eq->aa_spent = emu->aa_spent; // These fields may need to be correctly populated at some point - eq->aapoints_assigned = 0; + eq->aapoints_assigned = emu->aa_spent; eq->aa_spent_general = 0; eq->aa_spent_archetype = 0; eq->aa_spent_class = 0; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index ba2099710..ec5752f79 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -2900,7 +2900,7 @@ namespace RoF2 eq->aa_spent = emu->aa_spent; // These fields may need to be correctly populated at some point - eq->aapoints_assigned = 0; + eq->aapoints_assigned = emu->aa_spent; eq->aa_spent_general = 0; eq->aa_spent_archetype = 0; eq->aa_spent_class = 0; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 812e9c464..bc49fe88f 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4289,7 +4289,7 @@ struct AA_List { struct AA_Action { /*00*/ uint32 action; /*04*/ uint32 ability; -/*08*/ uint32 unknown08; +/*08*/ uint32 target_id; /*12*/ uint32 exp_value; /*16*/ }; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index a87b530c4..eb7c6edfb 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4288,7 +4288,7 @@ struct AA_List { struct AA_Action { /*00*/ uint32 action; /*04*/ uint32 ability; -/*08*/ uint32 unknown08; +/*08*/ uint32 target_id; /*12*/ uint32 exp_value; /*16*/ }; diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 2f5d2c2db..6e1a77b6b 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -3840,7 +3840,7 @@ struct AA_List { struct AA_Action { /*00*/ uint32 action; /*04*/ uint32 ability; -/*08*/ uint32 unknown08; +/*08*/ uint32 target_id; /*12*/ uint32 exp_value; }; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index afd34ba68..faa306ab0 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -3702,7 +3702,7 @@ struct AA_List { struct AA_Action { /*00*/ uint32 action; /*04*/ uint32 ability; -/*08*/ uint32 unknown08; +/*08*/ uint32 target_id; /*12*/ uint32 exp_value; }; diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index f836395c9..e5fa44a8e 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -3175,7 +3175,7 @@ struct AA_List { struct AA_Action { /*00*/ uint32 action; /*04*/ uint32 ability; -/*08*/ uint32 unknown08; +/*08*/ uint32 target_id; /*12*/ uint32 exp_value; }; diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index df6fa9615..ef224d574 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -2129,7 +2129,7 @@ namespace UF SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); eq->aa_spent = emu->aa_spent; - eq->aa_assigned = 0; + eq->aa_assigned = emu->aa_spent; eq->aa_spent3 = 0; eq->unknown012 = 0; eq->unknown016 = 0; diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 36d6dea74..882158492 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -3912,7 +3912,7 @@ struct AA_List { struct AA_Action { /*00*/ uint32 action; /*04*/ uint32 ability; -/*08*/ uint32 unknown08; +/*08*/ uint32 target_id; /*12*/ uint32 exp_value; }; diff --git a/common/spdat.h b/common/spdat.h index bf2d23f30..bb71c1e88 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -417,7 +417,7 @@ typedef enum { #define SE_SongModCap 261 // implemented[AA] - Song Mod cap increase (no longer used on live) #define SE_RaiseStatCap 262 // implemented //#define SE_TradeSkillMastery 263 // not implemented - lets you raise more than one tradeskill above master. -//#define SE_HastenedAASkill 264 // not implemented as bonus - Use redux field in aa_actions table for this effect +#define SE_HastenedAASkill 264 // implemented #define SE_MasteryofPast 265 // implemented[AA] - Spells less than effect values level can not be fizzled #define SE_ExtraAttackChance 266 // implemented - increase chance to score an extra attack with a 2-Handed Weapon. #define SE_PetDiscipline2 267 // *not implemented - /pet focus, /pet no cast diff --git a/zone/aa.cpp b/zone/aa.cpp index d6be92fe7..368eda225 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -37,32 +37,6 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net) extern QueryServ* QServ; -int Client::GetAATimerID(aaID activate) -{ - //SendAA_Struct* aa2 = zone->FindAA(activate); - // - //if(!aa2) - //{ - // for(int i = 1;i < MAX_AA_ACTION_RANKS; ++i) - // { - // int a = activate - i; - // - // if(a <= 0) - // break; - // - // aa2 = zone->FindAA(a); - // - // if(aa2 != nullptr) - // break; - // } - //} - // - //if(aa2) - // return aa2->spell_type; - - return 0; -} - int Client::CalcAAReuseTimer(const AA_DBAction *caa) { if(!caa) @@ -930,39 +904,6 @@ bool Client::CheckAAEffect(aaEffectType type) { return(false); } -void Client::SendAATimer(uint32 ability, uint32 begin, uint32 end) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction,sizeof(UseAA_Struct)); - UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; - uaaout->ability = ability; - uaaout->begin = begin; - uaaout->end = end; - QueuePacket(outapp); - safe_delete(outapp); -} - -//sends all AA timers. -void Client::SendAATimers() { - //we dont use SendAATimer because theres no reason to allocate the EQApplicationPacket every time - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction,sizeof(UseAA_Struct)); - UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; - - PTimerList::iterator c,e; - c = p_timers.begin(); - e = p_timers.end(); - for(; c != e; ++c) { - PersistentTimer *cur = c->second; - if(cur->GetType() < pTimerAAStart || cur->GetType() > pTimerAAEnd) - continue; //not an AA timer - //send timer - uaaout->begin = cur->GetStartTime(); - uaaout->end = static_cast(time(nullptr)); - uaaout->ability = cur->GetType() - pTimerAAStart; // uuaaout->ability is really a shared timer number - QueuePacket(outapp); - } - - safe_delete(outapp); -} - void Client::ResetAA(){ // RefundAA(); // uint32 i; @@ -1457,6 +1398,39 @@ void Client::SendAlternateAdvancementPoints() { safe_delete(outapp); } +void Client::SendAlternateAdvancementTimer(int ability, int begin, int end) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction, sizeof(UseAA_Struct)); + UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; + uaaout->ability = ability; + uaaout->begin = begin; + uaaout->end = end; + QueuePacket(outapp); + safe_delete(outapp); +} + +//sends all AA timers. +void Client::SendAlternateAdvancementTimers() { + //we dont use SendAATimer because theres no reason to allocate the EQApplicationPacket every time + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction, sizeof(UseAA_Struct)); + UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; + + PTimerList::iterator c, e; + c = p_timers.begin(); + e = p_timers.end(); + for(; c != e; ++c) { + PersistentTimer *cur = c->second; + if(cur->GetType() < pTimerAAStart || cur->GetType() > pTimerAAEnd) + continue; //not an AA timer + //send timer + uaaout->begin = cur->GetStartTime(); + uaaout->end = static_cast(time(nullptr)); + uaaout->ability = cur->GetType() - pTimerAAStart; // uuaaout->ability is really a shared timer number + QueuePacket(outapp); + } + + safe_delete(outapp); +} + void Client::PurchaseAlternateAdvancementRank(int rank_id) { AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id); if(!rank) { @@ -1495,23 +1469,21 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) { std::to_string(rank->cost).c_str(), std::to_string(AA_POINTS).c_str()); - //QS stuff broke with new aa, todo: fix later /* QS: Player_Log_AA_Purchases */ - // if (RuleB(QueryServ, PlayerLogAAPurchases)){ - // std::string event_desc = StringFormat("Ranked AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID()); - // QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc); - // } + if (RuleB(QueryServ, PlayerLogAAPurchases)){ + std::string event_desc = StringFormat("Ranked AA Purchase :: aa_id:%i at cost:%i in zoneid:%i instid:%i", rank->id, rank->cost, GetZoneID(), GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc); + } } else { Message_StringID(15, AA_GAIN_ABILITY, std::to_string(rank->title_sid).c_str(), std::to_string(rank->cost).c_str(), std::to_string(AA_POINTS).c_str()); - //QS stuff broke with new aa, todo: fix later /* QS: Player_Log_AA_Purchases */ - // if (RuleB(QueryServ, PlayerLogAAPurchases)){ - // std::string event_desc = StringFormat("Initial AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID()); - // QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc); - // } + if (RuleB(QueryServ, PlayerLogAAPurchases)){ + std::string event_desc = StringFormat("Initial AA Purchase :: aa_id:%i at cost:%i in zoneid:%i instid:%i", rank->id, rank->cost, GetZoneID(), GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc); + } } CalcBonuses(); @@ -1557,29 +1529,132 @@ void Client::IncrementAlternateAdvancementRank(int rank_id) { std::to_string(rank->cost).c_str(), std::to_string(AA_POINTS).c_str()); - //QS stuff broke with new aa, todo: fix later /* QS: Player_Log_AA_Purchases */ - // if (RuleB(QueryServ, PlayerLogAAPurchases)){ - // std::string event_desc = StringFormat("Ranked AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID()); - // QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc); - // } + if (RuleB(QueryServ, PlayerLogAAPurchases)){ + std::string event_desc = StringFormat("Ranked AA Purchase :: aa_id:%i at cost:%i in zoneid:%i instid:%i", rank->id, rank->cost, GetZoneID(), GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc); + } } else { Message_StringID(15, AA_GAIN_ABILITY, std::to_string(rank->title_sid).c_str(), std::to_string(rank->cost).c_str(), std::to_string(AA_POINTS).c_str()); - //QS stuff broke with new aa, todo: fix later + /* QS: Player_Log_AA_Purchases */ - // if (RuleB(QueryServ, PlayerLogAAPurchases)){ - // std::string event_desc = StringFormat("Initial AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID()); - // QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc); - // } + if (RuleB(QueryServ, PlayerLogAAPurchases)){ + std::string event_desc = StringFormat("Initial AA Purchase :: aa_id:%i at cost:%i in zoneid:%i instid:%i", rank->id, rank->cost, GetZoneID(), GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc); + } } CalcBonuses(); } +void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { + AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id); + if(!rank) { + return; + } + + AA::Ability *ability = rank->base_ability; + if(!ability) { + return; + } + + if(!IsValidSpell(rank->spell)) { + return; + } + + if(!CanUseAlternateAdvancementRank(rank)) { + return; + } + + //make sure it's activateable type + if(!(ability->type == 3 || ability->type == 4)) { + return; + } + + //check cooldown + if(!p_timers.Expired(&database, rank->spell_type + pTimerAAStart)) { + uint32 aaremain = p_timers.GetRemainingTime(rank->spell_type + pTimerAAStart); + uint32 aaremain_hr = aaremain / (60 * 60); + uint32 aaremain_min = (aaremain / 60) % 60; + uint32 aaremain_sec = aaremain % 60; + + if(aaremain_hr >= 1) { + Message(13, "You can use this ability again in %u hour(s) %u minute(s) %u seconds", + aaremain_hr, aaremain_min, aaremain_sec); + } + else { + Message(13, "You can use this ability again in %u minute(s) %u seconds", + aaremain_min, aaremain_sec); + } + + return; + } + + //calculate cooldown + int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank); + if(cooldown < 0) { + cooldown = 0; + } + + // 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), 10, -1, -1, spells[rank->spell].ResistDiff, false)) { + //Reset on failed cast + SendAlternateAdvancementTimer(rank->spell_type, 0, -1); + Message_StringID(15, ABILITY_FAILED); + p_timers.Clear(&database, rank->spell_type + pTimerAAStart); + return; + } + } else { + if(!CastSpell(rank->spell, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, 1)) { + //Reset on failed cast + SendAlternateAdvancementTimer(rank->spell_type, 0, -1); + Message_StringID(15, ABILITY_FAILED); + p_timers.Clear(&database, rank->spell_type + pTimerAAStart); + return; + } + } + + if(cooldown > 0) { + SendAlternateAdvancementTimer(rank->spell_type, 0, 0); + } +} + +int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { + if(!rank_in) { + return 0; + } + + AA::Ability *ability_in = rank_in->base_ability; + if(!ability_in) { + return 0; + } + + for(auto &aa : aa_ranks) { + AA::Ability *ability = zone->GetAlternateAdvancementAbility(aa.first); + if(!ability) { + continue; + } + + AA::Rank *rank = ability->GetRankByPointsSpent(aa.second.first); + if(!rank) { + continue; + } + + for(auto &effect : rank->effects) { + if(effect.effect_id == SE_HastenedAASkill && effect.base2 == ability_in->id) { + return effect.base1; + } + } + } + + return 0; +} + bool ZoneDatabase::LoadAlternateAdvancement(Client *c) { c->ClearAAs(); std::string query = StringFormat( diff --git a/zone/client.cpp b/zone/client.cpp index f36609b9c..5ef3e84e5 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -456,7 +456,7 @@ void Client::SendZoneInPackets() } safe_delete(outapp); - SendAATimers(); + SendAlternateAdvancementTimers(); outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; diff --git a/zone/client.h b/zone/client.h index 082d86fdc..dd283596d 100644 --- a/zone/client.h +++ b/zone/client.h @@ -765,7 +765,10 @@ public: void SendAlternateAdvancementStats(); void PurchaseAlternateAdvancementRank(int rank_id); void IncrementAlternateAdvancementRank(int rank_id); + void ActivateAlternateAdvancementAbility(int rank_id, int target_id); void SendAlternateAdvancementPoints(); + void SendAlternateAdvancementTimer(int ability, int begin, int end); + void SendAlternateAdvancementTimers(); void SetAAPoints(uint32 points) { m_pp.aapoints = points; SendAlternateAdvancementStats(); } void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); } @@ -782,11 +785,8 @@ public: inline uint32 GetMaxAAXP(void) const { return max_AAXP; } inline uint32 GetAAXP() const { return m_pp.expAA; } void SendAATable(); - void SendAATimers(); - int GetAATimerID(aaID activate); int CalcAAReuseTimer(const AA_DBAction *caa); void ActivateAA(aaID activate); - void SendAATimer(uint32 ability, uint32 begin, uint32 end); void EnableAAEffect(aaEffectType type, uint32 duration = 0); void DisableAAEffect(aaEffectType type); bool CheckAAEffect(aaEffectType type); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2068a815c..b76c11d8c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1082,7 +1082,7 @@ void Client::Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app) void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app) { - SendAATimers(); + SendAlternateAdvancementTimers(); EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAAStats, 0); QueuePacket(outapp); safe_delete(outapp); @@ -1749,7 +1749,7 @@ void Client::Handle_OP_AAAction(const EQApplicationPacket *app) if (action->action == aaActionActivate) {//AA Hotkey Log.Out(Logs::Detail, Logs::AA, "Activating AA %d", action->ability); - //ActivateAlternateAdvancementAbility(action->ability); + ActivateAlternateAdvancementAbility(action->ability, action->target_id); } else if (action->action == aaActionBuy) { PurchaseAlternateAdvancementRank(action->ability); @@ -1773,7 +1773,7 @@ void Client::Handle_OP_AAAction(const EQApplicationPacket *app) SendAlternateAdvancementTable(); } else { - Log.Out(Logs::General, Logs::AA, "Unknown AA action : %u %u 0x%x %d", action->action, action->ability, action->unknown08, action->exp_value); + Log.Out(Logs::General, Logs::AA, "Unknown AA action : %u %u %u %d", action->action, action->ability, action->target_id, action->exp_value); } } @@ -3847,8 +3847,6 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) return; } - m_TargetRing = glm::vec3(castspell->x_pos, castspell->y_pos, castspell->z_pos); - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); } /* Spell Slot or Potion Belt Slot */ diff --git a/zone/mob.h b/zone/mob.h index db3879257..08c6cd482 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -962,6 +962,7 @@ public: void ClearAAs() { aa_ranks.clear(); } bool CanUseAlternateAdvancementRank(AA::Rank *rank); bool CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price); + int GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in); protected: void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic); diff --git a/zone/spells.cpp b/zone/spells.cpp index 868180dff..4ef1337a7 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -817,8 +817,8 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) } if(casting_spell_type == 1 && IsClient()) { //Rest AA Timer on failed cast - CastToClient()->SendAATimer(casting_spell_timer - pTimerAAStart, 0, 0xFFFFFF); - CastToClient()->Message_StringID(15,ABILITY_FAILED); + CastToClient()->SendAlternateAdvancementTimer(casting_spell_timer - pTimerAAStart, 0, -1); + CastToClient()->Message_StringID(15, ABILITY_FAILED); CastToClient()->GetPTimers().Clear(&database, casting_spell_timer); } @@ -2276,6 +2276,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 { if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) { + //aa new todo: aa expendable charges here CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Setting custom reuse timer %d to %d", spell_id, casting_spell_timer, casting_spell_timer_duration); }