Mob* Mob::GetPet() { if(GetPetID() == 0) return(NULL); Mob* tmp = entity_list.GetMob(GetPetID()); if(tmp == NULL) { SetPetID(0); return(NULL); } if(tmp->GetOwnerID() != GetID()) { SetPetID(0); return(NULL); } return(tmp); }
void Client::ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm) { // From what I have read, dragged corpses should stay with the player for Intra-zone summons etc, but we can implement that later. ClearDraggedCorpses(); if(zoneID == 0) zoneID = zone->GetZoneID(); if(zoneID == zone->GetZoneID() && instance_id == zone->GetInstanceID()) { // TODO: Determine if this condition is necessary. if(IsAIControlled()) { GMMove(x, y, z); return; } if(GetPetID() != 0) { //if they have a pet and they are staying in zone, move with them Mob *p = GetPet(); if(p != nullptr){ p->SetPetOrder(SPO_Follow); p->GMMove(x+15, y, z); //so it dosent have to run across the map. } } } switch(zm) { case GateToBindPoint: ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); break; case EvacToSafeCoords: case ZoneToSafeCoords: ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); break; case GMSummon: Message(15, "You have been summoned by a GM!"); ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); break; case ZoneToBindPoint: ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); break; case ZoneSolicited: ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); break; case SummonPC: Message(15, "You have been summoned!"); ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); break; case Rewind: Message(15, "Rewinding to previous location."); ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); break; default: LogFile->write(EQEMuLog::Error, "Client::ProcessMovePC received a reguest to perform an unsupported client zone operation."); break; } }
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); }
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; } } }
void Client::HandleAAAction(aaID activate) { if (activate < 0 || activate >= aaHighestID) return; uint8 activate_val = GetAA(activate); if (activate_val == 0) 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[activate][activate_val]; uint16 timer_id = 0; uint16 timer_duration = caa->duration; aaTargetType target = aaTargetUser; uint16 spell_id = SPELL_UNKNOWN; //gets cast at the end if not still unknown switch (caa->action) { case aaActionAETaunt: entity_list.AETaunt(this); break; case aaActionMassBuff: EnableAAEffect(aaEffectMassGroupBuff, 3600); Message_StringID(MT_Disciplines, MGB_STRING); //The next group buff you cast will hit all targets in range. break; case aaActionFlamingArrows: //toggle it if (CheckAAEffect(aaEffectFlamingArrows)) EnableAAEffect(aaEffectFlamingArrows); else DisableAAEffect(aaEffectFlamingArrows); break; case aaActionFrostArrows: if (CheckAAEffect(aaEffectFrostArrows)) EnableAAEffect(aaEffectFrostArrows); else DisableAAEffect(aaEffectFrostArrows); break; case aaActionRampage: EnableAAEffect(aaEffectRampage, 10); break; case aaActionSharedHealth: if (CheckAAEffect(aaEffectSharedHealth)) EnableAAEffect(aaEffectSharedHealth); else DisableAAEffect(aaEffectSharedHealth); break; case aaActionCelestialRegen: { //special because spell_id depends on a different AA switch (GetAA(aaCelestialRenewal)) { case 1: spell_id = 3250; break; case 2: spell_id = 3251; break; default: spell_id = 2740; break; } target = aaTargetCurrent; break; } case aaActionDireCharm: { //special because spell_id depends on class switch (GetClass()) { case DRUID: spell_id = 2760; //2644? break; case NECROMANCER: spell_id = 2759; //2643? break; case ENCHANTER: spell_id = 2761; //2642? break; } target = aaTargetCurrent; break; } case aaActionImprovedFamiliar: { //Spell IDs might be wrong... if (GetAA(aaAllegiantFamiliar)) spell_id = 3264; //1994? else spell_id = 2758; //2155? break; } case aaActionActOfValor: if (GetTarget() != nullptr) { int curhp = GetTarget()->GetHP(); target = aaTargetCurrent; GetTarget()->HealDamage(curhp, this); Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand); } break; case aaActionSuspendedMinion: if (GetPet()) { target = aaTargetPet; switch (GetAA(aaSuspendedMinion)) { case 1: spell_id = 3248; break; case 2: spell_id = 3249; break; } //do we really need to cast a spell? Message(0, "You call your pet to your side."); GetPet()->WipeHateList(); GetPet()->GMMove(GetX(), GetY(), GetZ()); if (activate_val > 1) entity_list.ClearFeignAggro(GetPet()); } else { Message(0, "You have no pet to call."); } break; case aaActionProjectIllusion: EnableAAEffect(aaEffectProjectIllusion, 3600); Message(10, "The power of your next illusion spell will flow to your grouped target in your place."); break; case aaActionEscape: Escape(); break; // Don't think this code is used any longer for Bestial Alignment as the aa.has a spell_id and no nonspell_action. case aaActionBeastialAlignment: switch (GetBaseRace()) { case BARBARIAN: spell_id = AA_Choose3(activate_val, 4521, 4522, 4523); break; case TROLL: spell_id = AA_Choose3(activate_val, 4524, 4525, 4526); break; case OGRE: spell_id = AA_Choose3(activate_val, 4527, 4527, 4529); break; case IKSAR: spell_id = AA_Choose3(activate_val, 4530, 4531, 4532); break; case VAHSHIR: spell_id = AA_Choose3(activate_val, 4533, 4534, 4535); break; } case aaActionLeechTouch: target = aaTargetCurrent; spell_id = SPELL_HARM_TOUCH2; EnableAAEffect(aaEffectLeechTouch, 1000); break; case aaActionFadingMemories: // Do nothing since spell effect works correctly, but mana isn't used. break; default: Log.Out(Logs::General, Logs::Error, "Unknown AA nonspell action type %d", caa->action); return; } uint16 target_id = 0; //figure out our target switch (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, timer_id + pTimerAAEffectStart); 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; } //cast the spell, if we have one if (IsValidSpell(spell_id)) { int aatid = GetAATimerID(activate); if (!CastSpell(spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, pTimerAAStart + aatid, CalcAAReuseTimer(caa), 1)) { SendAATimer(activate, 0, 0xFFFFFF); Message_StringID(CC_Yellow, ABILITY_FAILED); p_timers.Clear(&database, pTimerAAStart + aatid); return; } } //handle the duration timer if we have one. if (timer_id > 0 && timer_duration > 0) { p_timers.Start(pTimerAAEffectStart + timer_id, timer_duration); } }