bool Client::UseDiscipline(uint8 disc_id) { // Dont let client waste a reuse timer if they can't use the disc if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad() || IsPet()) { return(false); } //Check the disc timer uint32 remain = p_timers.GetRemainingTime(pTimerDisciplineReuseStart); if(remain > 0 && !GetGM()) { char val1[20]= {0}; char val2[20]= {0}; Message_StringID(CC_User_Disciplines, DISCIPLINE_CANUSEIN, ConvertArray((remain)/60,val1), ConvertArray(remain%60,val2)); return(false); } bool active = disc_ability_timer.Enabled(); if(active) { Message(CC_User_Disciplines, "You must wait before using this discipline."); //find correct message return(false); } //can we use the disc? the client checks this for us, but we should also confirm server side. uint8 level_to_use = DisciplineUseLevel(disc_id); if(level_to_use > GetLevel() || level_to_use == 0) { Message_StringID(CC_User_Disciplines, DISC_LEVEL_USE_ERROR); return(false); } // Disciplines with no ability timer (ashenhand, silentfist, thunderkick, and unholyaura) will remain on the player until they either // use the skill the disc affects successfully, camp/zone, or attempt to use another disc. If we're here, clear that disc so they can // cast a new one. if(GetActiveDisc() != 0) { Log.Out(Logs::General, Logs::Discs, "Clearing disc %d so that disc %d can be cast.", GetActiveDisc(), disc_id); FadeDisc(); } //cast the disc if(CastDiscipline(disc_id, level_to_use)) return(true); else return(false); }
bool Client::UseDiscipline(uint32 spell_id, uint32 target) { // Dont let client waste a reuse timer if they can't use the disc if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad() || IsPet()) { return(false); } //make sure we have the spell... int r; for(r = 0; r < MAX_PP_DISCIPLINES; r++) { if(m_pp.disciplines.values[r] == spell_id) break; } if(r == MAX_PP_DISCIPLINES) return(false); //not found. //Check the disc timer pTimerType DiscTimer = pTimerDisciplineReuseStart + spells[spell_id].EndurTimerIndex; if(!p_timers.Expired(&database, DiscTimer)) { /*char val1[20]={0};*/ //unused /*char val2[20]={0};*/ //unused uint32 remain = p_timers.GetRemainingTime(DiscTimer); //Message_StringID(0, DISCIPLINE_CANUSEIN, ConvertArray((remain)/60,val1), ConvertArray(remain%60,val2)); Message(0, "You can use this discipline in %d minutes %d seconds.", ((remain)/60), (remain%60)); return(false); } //make sure we can use it.. if(!IsValidSpell(spell_id)) { Message(13, "This tome contains invalid knowledge."); return(false); } //can we use the spell? const SPDat_Spell_Struct &spell = spells[spell_id]; uint8 level_to_use = spell.classes[GetClass() - 1]; if(level_to_use == 255) { Message(13, "Your class cannot learn from this tome."); //should summon them a new one... return(false); } if(level_to_use > GetLevel()) { Message_StringID(13, DISC_LEVEL_USE_ERROR); //should summon them a new one... return(false); } if(GetEndurance() > spell.EndurCost) { SetEndurance(GetEndurance() - spell.EndurCost); } else { Message(11, "You are too fatigued to use this skill right now."); return(false); } if(spell.recast_time > 0) { uint32 reduced_recast = spell.recast_time / 1000; reduced_recast -= CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id); if(reduced_recast < 0) reduced_recast = 0; CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast); if(spells[spell_id].EndurTimerIndex < MAX_DISCIPLINE_TIMERS) { EQApplicationPacket *outapp = new EQApplicationPacket(OP_DisciplineTimer, sizeof(DisciplineTimer_Struct)); DisciplineTimer_Struct *dts = (DisciplineTimer_Struct *)outapp->pBuffer; dts->TimerID = spells[spell_id].EndurTimerIndex; dts->Duration = reduced_recast; QueuePacket(outapp); safe_delete(outapp); } } else { CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); } return(true); }
void Client::ActivateAA(aaID activate){ if (activate < 0 || activate >= aaHighestID) return; if (IsStunned() || IsFeared() || IsMezzed() || IsSilenced() || IsPet() || IsSitting() || GetFeigned()) return; int AATimerID = GetAATimerID(activate); SendAA_Struct* aa2 = nullptr; aaID aaid = activate; uint8 activate_val = GetAA(activate); //this wasn't taking into acct multi tiered act talents before... if (activate_val == 0){ aa2 = zone->FindAA(activate); if (!aa2){ int i; int a; for (i = 1; i<MAX_AA_ACTION_RANKS; i++){ a = activate - i; if (a <= 0) break; aa2 = zone->FindAA(a); if (aa2 != nullptr) break; } } if (aa2){ aaid = (aaID)aa2->id; activate_val = GetAA(aa2->id); } } if (activate_val == 0){ return; } if (aa2) { if (aa2->account_time_required) { if ((Timer::GetTimeSeconds() + account_creation) < aa2->account_time_required) { return; } } } if (!p_timers.Expired(&database, AATimerID + pTimerAAStart)) { uint32 aaremain = p_timers.GetRemainingTime(AATimerID + pTimerAAStart); uint32 aaremain_hr = aaremain / (60 * 60); uint32 aaremain_min = (aaremain / 60) % 60; uint32 aaremain_sec = aaremain % 60; if (aa2) { if (aaremain_hr >= 1) //1 hour or more Message(CC_Red, "You can use the ability %s again in %u hour(s) %u minute(s) %u seconds", aa2->name, aaremain_hr, aaremain_min, aaremain_sec); else //less than an hour Message(CC_Red, "You can use the ability %s again in %u minute(s) %u seconds", aa2->name, aaremain_min, aaremain_sec); } else { if (aaremain_hr >= 1) //1 hour or more Message(CC_Red, "You can use this ability again in %u hour(s) %u minute(s) %u seconds", aaremain_hr, aaremain_min, aaremain_sec); else //less than an hour Message(CC_Red, "You can use this ability again in %u minute(s) %u seconds", aaremain_min, aaremain_sec); } return; } if (activate_val > MAX_AA_ACTION_RANKS) activate_val = MAX_AA_ACTION_RANKS; activate_val--; //to get array index. //get our current node, now that the indices are well bounded const AA_DBAction *caa = &AA_Actions[aaid][activate_val]; if ((aaid == aaImprovedHarmTouch || aaid == aaLeechTouch) && !p_timers.Expired(&database, pTimerHarmTouch)){ Message(CC_Red, "Ability recovery time not yet met."); return; } //everything should be configured out now uint16 target_id = 0; //figure out our target switch (caa->target) { case aaTargetUser: case aaTargetGroup: target_id = GetID(); break; case aaTargetCurrent: case aaTargetCurrentGroup: if (GetTarget() == nullptr) { Message_StringID(MT_DefaultText, AA_NO_TARGET); //You must first select a target for this ability! p_timers.Clear(&database, AATimerID + pTimerAAStart); return; } target_id = GetTarget()->GetID(); break; case aaTargetPet: if (GetPet() == nullptr) { Message(0, "A pet is required for this skill."); return; } target_id = GetPetID(); break; } //handle non-spell action if (caa->action != aaActionNone) { if (caa->mana_cost > 0) { if (GetMana() < caa->mana_cost) { Message_StringID(CC_Red, INSUFFICIENT_MANA); return; } SetMana(GetMana() - caa->mana_cost); } if (caa->reuse_time > 0) { uint32 timer_base = CalcAAReuseTimer(caa); if (activate == aaImprovedHarmTouch || activate == aaLeechTouch) { p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); } p_timers.Start(AATimerID + pTimerAAStart, timer_base); SendAATimer(activate, static_cast<uint32>(time(nullptr)), static_cast<uint32>(time(nullptr))); } HandleAAAction(aaid); } //cast the spell, if we have one if (caa->spell_id > 0 && caa->spell_id < SPDAT_RECORDS) { if (caa->reuse_time > 0) { uint32 timer_base = CalcAAReuseTimer(caa); SendAATimer(activate, static_cast<uint32>(time(nullptr)), static_cast<uint32>(time(nullptr))); p_timers.Start(AATimerID + pTimerAAStart, timer_base); if (activate == aaImprovedHarmTouch || activate == aaLeechTouch) { p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); } // Bards can cast instant cast AAs while they are casting another song if (spells[caa->spell_id].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) { if (!SpellFinished(caa->spell_id, entity_list.GetMob(target_id), 10, -1, -1, spells[caa->spell_id].ResistDiff, false)) { //Reset on failed cast SendAATimer(activate, 0, 0xFFFFFF); Message_StringID(CC_Yellow, ABILITY_FAILED); p_timers.Clear(&database, AATimerID + pTimerAAStart); return; } } else { if (!CastSpell(caa->spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) { //Reset on failed cast SendAATimer(activate, 0, 0xFFFFFF); Message_StringID(CC_Yellow, ABILITY_FAILED); p_timers.Clear(&database, AATimerID + pTimerAAStart); return; } } } else { if (!CastSpell(caa->spell_id, target_id)) return; } } }