void Client::SendAlternateAdvancementRank(int aa_id, int level) { if(!zone) return; auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa_id, level); auto ability = ability_rank.first; auto rank = ability_rank.second; if(!ability) { return; } if(!(ability->classes & (1 << GetClass()))) { return; } if(!CanUseAlternateAdvancementRank(rank)) { return; } int size = sizeof(AARankInfo_Struct) + (sizeof(AARankEffect_Struct) * rank->effects.size()) + (sizeof(AARankPrereq_Struct) * rank->prereqs.size()); auto outapp = new EQApplicationPacket(OP_SendAATable, size); AARankInfo_Struct *aai = (AARankInfo_Struct*)outapp->pBuffer; aai->id = rank->id; aai->upper_hotkey_sid = rank->upper_hotkey_sid; aai->lower_hotkey_sid = rank->lower_hotkey_sid; aai->title_sid = rank->title_sid; aai->desc_sid = rank->desc_sid; aai->cost = rank->cost; aai->seq = aa_id; aai->type = ability->type; aai->spell = rank->spell; aai->spell_type = rank->spell_type; aai->spell_refresh = rank->recast_time; aai->classes = ability->classes; aai->level_req = rank->level_req; aai->current_level = level; aai->max_level = ability->GetMaxLevel(this); aai->prev_id = rank->prev_id; if((rank->next && !CanUseAlternateAdvancementRank(rank->next)) || ability->charges > 0) { aai->next_id = -1; } else { aai->next_id = rank->next_id; } aai->total_cost = rank->total_cost; aai->expansion = rank->expansion; aai->category = ability->category; aai->charges = ability->charges; aai->grant_only = ability->grant_only; aai->total_effects = rank->effects.size(); aai->total_prereqs = rank->prereqs.size(); outapp->SetWritePosition(sizeof(AARankInfo_Struct)); for(auto &effect : rank->effects) { outapp->WriteSInt32(effect.effect_id); outapp->WriteSInt32(effect.base1); outapp->WriteSInt32(effect.base2); outapp->WriteSInt32(effect.slot); } for(auto &prereq : rank->prereqs) { outapp->WriteSInt32(prereq.first); outapp->WriteSInt32(prereq.second); } QueuePacket(outapp); safe_delete(outapp); }
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 is not a passive if(!rank->effects.empty()) { 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; //check cooldown if(!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) { 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; } if (!IsCastWhileInvis(rank->spell)) CommonBreakInvisible(); if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) { Message_StringID(MT_SpellFailure, SNEAK_RESTRICT); return; } // // Modern clients don't require pet targeted for AA casts that are ST_Pet if (spells[rank->spell].targettype == ST_Pet || spells[rank->spell].targettype == ST_SummonedPet) target_id = GetPetID(); // extra handling for cast_not_standing spells if (!spells[rank->spell].cast_not_standing) { if (GetAppearance() == eaSitting) // we need to stand! SetAppearance(eaStanding, false); if (GetAppearance() != eaStanding) { Message_StringID(MT_SpellFailure, STAND_TO_CAST); return; } } // 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), EQEmu::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) { return; } ExpendAlternateAdvancementCharge(ability->id); } else { if(!CastSpell(rank->spell, target_id, EQEmu::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) { return; } } CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown); SendAlternateAdvancementTimer(rank->spell_type, 0, 0); }
bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price, bool check_grant) { AA::Ability *ability = rank->base_ability; if(!ability) return false; if(!CanUseAlternateAdvancementRank(rank)) { return false; } //You can't purchase grant only AAs they can only be assigned if(check_grant && ability->grant_only) { return false; } //check level req if(rank->level_req > GetLevel()) { return false; } uint32 current_charges = 0; auto points = GetAA(rank->id, ¤t_charges); //check that we are on previous rank already (if exists) //grant ignores the req to own the previous rank. if(check_grant && rank->prev) { if(points != rank->prev->current_value) { return false; } } //check that we aren't already on this rank or one ahead of us if(points >= rank->current_value) { return false; } //if expendable only let us purchase if we have no charges already //not quite sure on how this functions client side atm //I intend to look into it later to make sure the behavior is right if(ability->charges > 0 && current_charges > 0) { return false; } //check prereqs for(auto &prereq : rank->prereqs) { AA::Ability *prereq_ability = zone->GetAlternateAdvancementAbility(prereq.first); if(prereq_ability) { auto ranks = GetAA(prereq_ability->first_rank_id); if(ranks < prereq.second) { return false; } } } //check price, if client if(check_price && IsClient()) { if(rank->cost > CastToClient()->GetAAPoints()) { return false; } } return true; }
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 is not a passive if(!rank->effects.empty()) { 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; //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; } if (!IsCastWhileInvis(rank->spell)) CommonBreakInvisible(); // 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), ALTERNATE_ABILITY_SPELL_SLOT, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) { return; } ExpendAlternateAdvancementCharge(ability->id); } else { if(!CastSpell(rank->spell, target_id, ALTERNATE_ABILITY_SPELL_SLOT, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) { return; } } CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown); SendAlternateAdvancementTimer(rank->spell_type, 0, 0); }