bool Trap::Process() { if (chkarea_timer.Enabled() && chkarea_timer.Check() && !reset_timer.Enabled()) { Mob* trigger = entity_list.GetTrapTrigger(this); if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM())) { Trigger(trigger); } } else if (reset_timer.Enabled() && reset_timer.Check()) { Log.Out(Logs::General, Logs::Traps, "Reset timer disabled in Reset Check Process for trap %d.", trap_id); reset_timer.Disable(); charid = 0; } if (respawn_timer.Enabled() && respawn_timer.Check()) { detected = false; disarmed = false; chkarea_timer.Enable(); respawn_timer.Disable(); } return true; }
void HateList::Wipe() { LinkedListIterator<tHateEntry*> iterator(list); iterator.Reset(); while(iterator.MoreElements()) { Mob* m = iterator.GetData()->ent; parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0); iterator.RemoveCurrent(); if(m->IsClient()) m->CastToClient()->DecrementAggroCount(); } }
void HateList::Wipe() { auto iterator = list.begin(); while(iterator != list.end()) { Mob* m = (*iterator)->ent; parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0); //iterator delete (*iterator); iterator = list.erase(iterator); if(m->IsClient()) m->CastToClient()->DecrementAggroCount(); } }
bool Trap::Process() { if (chkarea_timer.Enabled() && chkarea_timer.Check() /*&& zone->GetClientCount() > 0*/ ) { Mob* trigger = entity_list.GetTrapTrigger(this); if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM())) { Trigger(trigger); } } if (respawn_timer.Enabled() && respawn_timer.Check()) { detected = false; disarmed = false; chkarea_timer.Enable(); respawn_timer.Disable(); } return true; }
/* If you change this function, you should update the above function to keep the #aggro command accurate. */ bool Mob::CheckWillAggro(Mob *mob) { if(!mob) return false; //sometimes if a client has some lag while zoning into a dangerous place while either invis or a GM //they will aggro mobs even though it's supposed to be impossible, to lets make sure we've finished connecting if (mob->IsClient()) { if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn()) return false; } Mob *ownr = mob->GetOwner(); if(ownr && ownr->IsClient() && !ownr->CastToClient()->ClientFinishedLoading()) return false; float iAggroRange = GetAggroRange(); // Check If it's invisible and if we can see invis // Check if it's a client, and that the client is connected and not linkdead, // and that the client isn't Playing an NPC, with thier gm flag on // Check if it's not a Interactive NPC // Trumpcard: The 1st 3 checks are low cost calcs to filter out unnessecary distance checks. Leave them at the beginning, they are the most likely occurence. // Image: I moved this up by itself above faction and distance checks because if one of these return true, theres no reason to go through the other information float t1, t2, t3; t1 = mob->GetX() - GetX(); t2 = mob->GetY() - GetY(); t3 = mob->GetZ() - GetZ(); //Cheap ABS() if(t1 < 0) t1 = 0 - t1; if(t2 < 0) t2 = 0 - t2; if(t3 < 0) t3 = 0 - t3; if(( t1 > iAggroRange) || ( t2 > iAggroRange) || ( t3 > iAggroRange) ||(mob->IsInvisible(this)) || (mob->IsClient() && (!mob->CastToClient()->Connected() || mob->CastToClient()->IsLD() || mob->CastToClient()->IsBecomeNPC() || mob->CastToClient()->GetGM() ) )) { return(false); } // Don't aggro new clients if we are already engaged unless PROX_AGGRO is set if (IsEngaged() && (!GetSpecialAbility(PROX_AGGRO) || (GetSpecialAbility(PROX_AGGRO) && !CombatRange(mob)))) { Log.Out(Logs::Moderate, Logs::Aggro, "%s is in combat, and does not have prox_aggro, or does and is out of combat range with %s", GetName(), mob->GetName()); return false; } //im not sure I understand this.. //if I have an owner and it is not this mob, then I cannot //aggro this mob...??? //changed to be 'if I have an owner and this is it' if(mob == GetOwner()) { return(false); } float dist2 = DistanceSquared(mob->GetPosition(), m_Position); float iAggroRange2 = iAggroRange*iAggroRange; if( dist2 > iAggroRange2 ) { // Skip it, out of range return(false); } //Image: Get their current target and faction value now that its required //this function call should seem backwards FACTION_VALUE fv = mob->GetReverseFactionCon(this); // Make sure they're still in the zone // Are they in range? // Are they kos? // Are we stupid or are they green // and they don't have their gm flag on int heroicCHA_mod = mob->itembonuses.HeroicCHA/25; // 800 Heroic CHA cap if(heroicCHA_mod > THREATENLY_ARRGO_CHANCE) heroicCHA_mod = THREATENLY_ARRGO_CHANCE; if ( //old InZone check taken care of above by !mob->CastToClient()->Connected() ( ( GetINT() <= RuleI(Aggro, IntAggroThreshold) ) ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GREEN ) ) && ( ( fv == FACTION_SCOWLS || (mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == nullptr) || ( fv == FACTION_THREATENLY && zone->random.Roll(THREATENLY_ARRGO_CHANCE - heroicCHA_mod) ) ) ) ) { //FatherNiwtit: make sure we can see them. last since it is very expensive if(CheckLosFN(mob)) { Log.Out(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName()); return( mod_will_aggro(mob, this) ); } } Log.Out(Logs::Detail, Logs::Aggro, "Is In zone?:%d\n", mob->InZone()); Log.Out(Logs::Detail, Logs::Aggro, "Dist^2: %f\n", dist2); Log.Out(Logs::Detail, Logs::Aggro, "Range^2: %f\n", iAggroRange2); Log.Out(Logs::Detail, Logs::Aggro, "Faction: %d\n", fv); Log.Out(Logs::Detail, Logs::Aggro, "Int: %d\n", GetINT()); Log.Out(Logs::Detail, Logs::Aggro, "Con: %d\n", GetLevelCon(mob->GetLevel())); return(false); }
// causes caster to hit every mob within dist range of center with // spell_id. // NPC spells will only affect other NPCs with compatible faction void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust) { Mob *curmob; float dist = caster->GetAOERange(spell_id); float dist2 = dist * dist; float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; float dist_targ = 0; bool bad = IsDetrimentalSpell(spell_id); bool isnpc = caster->IsNPC(); int MAX_TARGETS_ALLOWED = 4; if (spells[spell_id].aemaxtargets) MAX_TARGETS_ALLOWED = spells[spell_id].aemaxtargets; int iCounter = 0; for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { curmob = it->second; // test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading()) continue; if (curmob == center) //do not affect center continue; if (curmob == caster && !affect_caster) //watch for caster too continue; if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient()) continue; if (spells[spell_id].targettype == ST_AreaClientOnly && !curmob->IsClient()) continue; if (spells[spell_id].targettype == ST_AreaNPCOnly && !curmob->IsNPC()) continue; if (spells[spell_id].targettype == ST_Ring) { dist_targ = DistanceSquared(static_cast<glm::vec3>(curmob->GetPosition()), caster->GetTargetRingLocation()); } else if (center) { dist_targ = DistanceSquared(curmob->GetPosition(), center->GetPosition()); } if (dist_targ > dist2) //make sure they are in range continue; if (dist_targ < min_range2) //make sure they are in range continue; if (isnpc && curmob->IsNPC()) { //check npc->npc casting FACTION_VALUE f = curmob->GetReverseFactionCon(caster); if (bad) { //affect mobs that are on our hate list, or //which have bad faction with us if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) continue; } else { //only affect mobs we would assist. if (!(f <= FACTION_AMIABLE)) continue; } } //finally, make sure they are within range if (bad) { if (!caster->IsAttackAllowed(curmob, true)) continue; if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob)) continue; if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize())) continue; } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... // This does not check faction for beneficial AE buffs..only agro and attackable. // I've tested for spells that I can find without problem, but a faction-based // check may still be needed. Any changes here should also reflect in BardAEPulse() if (caster->IsAttackAllowed(curmob, true)) continue; if (caster->CheckAggro(curmob)) continue; } curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); //if we get here... cast the spell. if (IsTargetableAESpell(spell_id) && bad) { if (iCounter < MAX_TARGETS_ALLOWED) { caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } } else { if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); if (!spells[spell_id].aemaxtargets) caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } if (!isnpc || spells[spell_id].aemaxtargets) //npcs are not target limited (unless casting a spell with a target limit)... iCounter++; } }
// solar: causes caster to hit every mob within dist range of center with // spell_id. // NPC spells will only affect other NPCs with compatible faction void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust) { Mob *curmob; float dist = caster->GetAOERange(spell_id); float dist2 = dist * dist; bool bad = IsDetrimentalSpell(spell_id); bool isnpc = caster->IsNPC(); const int MAX_TARGETS_ALLOWED = 4; int iCounter = 0; for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { curmob = it->second; // test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading()) continue; if (curmob == center) //do not affect center continue; if (curmob == caster && !affect_caster) //watch for caster too continue; if (center->DistNoRoot(*curmob) > dist2) //make sure they are in range continue; if (isnpc && curmob->IsNPC()) { //check npc->npc casting FACTION_VALUE f = curmob->GetReverseFactionCon(caster); if (bad) { //affect mobs that are on our hate list, or //which have bad faction with us if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) continue; } else { //only affect mobs we would assist. if (!(f <= FACTION_AMIABLE)) continue; } } //finally, make sure they are within range if (bad) { if (!caster->IsAttackAllowed(curmob, true)) continue; if (!center->CheckLosFN(curmob)) continue; } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... // This does not check faction for beneficial AE buffs..only agro and attackable. // I've tested for spells that I can find without problem, but a faction-based // check may still be needed. Any changes here should also reflect in BardAEPulse() -U if (caster->IsAttackAllowed(curmob, true)) continue; if (caster->CheckAggro(curmob)) continue; } //if we get here... cast the spell. if (IsTargetableAESpell(spell_id) && bad) { if (iCounter < MAX_TARGETS_ALLOWED) { caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } } else { caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } if (!isnpc) //npcs are not target limited... iCounter++; } }
//o-------------------------------------------------------------- //| BuffFadeBySlot; Yeahlight, Nov 16, 2008 //o-------------------------------------------------------------- //| Adapted from EQEMU 7.0: Removes the buff in the supplied //| buff slot 'slot' //o-------------------------------------------------------------- void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) { bool debugFlag = true; if(slot < 0 || slot > BUFF_COUNT) return; if(!buffs[slot].spell || !buffs[slot].spell->IsValidSpell()) return; if(IsClient()) CastToClient()->MakeBuffFadePacket(buffs[slot].spell, slot); if(debugFlag && this->IsClient() && this->CastToClient()->GetDebugMe()) this->Message(LIGHTEN_BLUE, "Debug: Fading buff %d from slot %d", buffs[slot].spell->GetSpellID(), slot); for(int i = 0; i < EFFECT_COUNT; i++) { if(buffs[slot].spell->IsBlankSpellEffect(i)) continue; switch(buffs[slot].spell->GetSpellEffectID(i)) { case SE_WeaponProc: { SetBonusProcSpell(0); break; } case SE_Illusion: case SE_IllusionCopy: { SendIllusionPacket(GetBaseRace(), GetBaseGender(), GetTexture(), GetHelmTexture()); SendAppearancePacket(this->GetID(), SAT_Size, GetDefaultSize(), true); break; } case SE_Levitate: { SendAppearancePacket(this->GetID(), SAT_Levitate, 0, true); break; } case SE_Invisibility: { SetInvisible(false); break; } case SE_InvisVsUndead: { SetInvisibleUndead(false); break; } case SE_InvisVsAnimals: { SetInvisibleAnimal(false); break; } case SE_Silence: { break; } case SE_DivineAura: { SetInvulnerable(false); break; } case SE_Rune: { break; } case SE_AbsorbMagicAtt: { break; } case SE_Mez: { //Yeahlight: Unfreeze the PC's UI if(IsClient()) SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Sitting_To_Standing, true); this->mesmerized = false; break; } case SE_Charm: { Mob* charmer = entity_list.GetMob(this->GetOwnerID()); if(charmer && charmer->IsClient()) { char npcNameSuffix[] = "_CHARM00"; char npcNamePrefix[100] = ""; strcpy(npcNamePrefix, charmer->GetName()); strcat(npcNamePrefix, npcNameSuffix); Mob* charmPH = entity_list.GetMob(npcNamePrefix); if(charmPH && charmPH->IsNPC()) { charmPH->Depop(); } //Yeahlight: Check for _CHARM01 NPC, too npcNamePrefix[strlen(npcNamePrefix) - 1] = '1'; charmPH = entity_list.GetMob(npcNamePrefix); if(charmPH && charmPH->IsNPC()) { charmPH->Depop(); } //Yeahlight: Generate hate for towards the charmer if the charmer is not FD'ed if(this->IsNPC() && charmer->IsClient() && !charmer->CastToClient()->GetFeigned()) { CastToNPC()->AddToHateList(charmer, 0, GetSpellHate(SE_Charm, this->GetLevel(), false)); } } //Yeahlight: Unfreeze the PC's UI if(IsClient()) { SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Sitting_To_Standing, true); CastToClient()->SetCharmed(false); CastToClient()->charmPositionUpdate_timer->Disable(); } //Yeahlight: Deflag the NPC as charmed else if(IsNPC()) { CastToNPC()->SetCharmed(false); } this->SetOwnerID(0, false); break; } case SE_Root: { break; } case SE_Fear: { //Yeahlight: Unfreeze the PC's UI if(IsClient()) { CastToClient()->SetFeared(false); SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Sitting_To_Standing, true); } else if(IsNPC()) { CastToNPC()->SetFeared(false); CastToNPC()->SetOnFearPath(false); } break; } case SE_SpinTarget: { //Yeahlight: Unfreeze the PC's UI if(IsClient()) SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Sitting_To_Standing, true); break; } case SE_EyeOfZomm: { //Yeahlight: Clear the eye of zomm pointer and depop the eye if(IsClient() && CastToClient()->myEyeOfZomm) { CastToClient()->myEyeOfZomm->Depop(); CastToClient()->myEyeOfZomm = NULL; } break; } case SE_DeathSave: { //Yeahlight: Clear the death save chance if(IsClient()) CastToClient()->SetDeathSave(0); break; } case SE_VoiceGraft: { //Yeahlight: Drop the voice grafting flag from the client if(GetOwner() && GetOwner()->IsClient()) { GetOwner()->CastToClient()->SetVoiceGrafting(false); } break; } case SE_SeeInvis: { //Yeahlight: Mob may no longer see through invis SetCanSeeThroughInvis(false); break; } } } buffs[slot].spell = NULL; if(iRecalcBonuses) CalcBonuses(true, true); }
/////////////////////////////////////////////////// // Quagmire - that case above getting to long and spells are gonna have a lot of cases of their own // Cofruben - Reorganised this a little. void Mob::SpellEffect(Mob* caster, Spell* spell, int8 caster_level, bool partialResist) { //Spells not loaded! if(!spells_handler.SpellsLoaded()) return; //Spell not loaded! if(!spell) return; //Yeahlight: Caster was not supplied if(!caster) return; int i = 0; const int16 spell_id = spell->GetSpellID(); const char* teleport_zone = spell->GetSpellTeleportZone(); // 1. Is it a buff? If so, handle its time based effects. if (spell->IsBuffSpell()) spells_handler.HandleBuffSpellEffects(caster, this, spell); // 2. Handle its single-time effect. for (i = 0; i < EFFECT_COUNT; i++) { TSpellEffect effect_id = spell->GetSpellEffectID(i); if(effect_id == SE_Blank || effect_id == 0xFF) continue; int8 formula = spell->GetSpellFormula(i); sint16 base = spell->GetSpellBase(i); sint16 max = spell->GetSpellMax(i); sint32 amount = spells_handler.CalcSpellValue(spell, i, caster_level); //Yeahlight: This is an NPC and had a detremental spell casted upon it if(this->IsNPC() && (spell->IsDetrimentalSpell() || spell->IsUtilitySpell())) { CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): aggroing %s because of the spell effect!", spell->GetSpellName(), this->GetName()); //Yeahlight: Generate hate based on the spells's effect type sint16 tempHate = GetSpellHate(effect_id, spell->GetMinLevel(), false, amount); if(tempHate) { this->CastToNPC()->AddToHateList(caster, 0, tempHate); } } switch(effect_id) { case SE_CurrentHP: case SE_CurrentHPOnce: { sint32 OldHP = this->GetHP(); sint32 damage = amount; //Yeahlight: Partial resist calculations if(partialResist) { damage = damage / 2; damage = damage * (float)((float)(rand()%90 + 10) / 100.00f); if(caster->IsClient() && caster->CastToClient()->GetDebugMe()) caster->Message(YELLOW, "Debug: Your direct damage spell resist has been upgrade to a partial resist."); } this->ChangeHP(caster, damage, spell_id); sint32 NewHP = this->GetHP(); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You changed %s's hp by %+i.", spell->GetSpellName(), this->GetName(), damage); break; } case SE_MovementSpeed: { //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a Movement Speed spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_AttackSpeed: { //Yeahlight: There should not be any work to be done here CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a Attack Speed spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Invisibility: { this->SetInvisible(true); //Yeahlight: Castee has a pet; remove it if(GetPet()) { Mob* myPet = GetPet(); //Yeahlight: Castee's pet is an NPC if(myPet->IsNPC()) { //Yeahlight: Castee's pet is a charmed NPC if(myPet->CastToNPC()->IsCharmed()) { myPet->CastToNPC()->BuffFadeByEffect(SE_Charm); } //Yeahlight: Castee's pet is a summoned NPC else { myPet->Depop(); } } //Yeahlight: Castee's pet is a charmed PC else if(myPet->IsClient() && myPet->CastToClient()->IsCharmed()) { myPet->CastToClient()->BuffFadeByEffect(SE_Charm); } } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted an invisibility spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_CurrentMana: { SetMana(GetMana() + amount); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a mana recovery spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_AddFaction: { //Yeahlight: Only continue if the target is an NPC and the caster is a PC if(this->IsNPC() && caster->IsClient()) { caster->CastToClient()->SetCharacterFactionLevelModifier(this->CastToNPC()->GetPrimaryFactionID(), amount); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted an add faction spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Stun: { if (IsClient()) { CastToClient()->Stun(base); } else if(IsNPC()) { //Yeahlight: NPC is immune to stun effects if(CastToNPC()->GetCannotBeStunned()) { if(caster->IsClient()) { caster->Message(RED, "Your target is immune to the stun portion of this effect"); } } else { CastToNPC()->Stun(base); } } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a stun spell, amount: %i.", spell->GetSpellName(), base); break; } case SE_Charm: { //Yeahlight: Can only charm a non-pet and the caster may only have one pet if(this->GetOwner() == NULL && caster->GetPet() == NULL && caster != this) { //Yeahlight: Flag the NPC as a pet if(this->IsNPC()) { caster->SetPet(this); this->SetOwnerID(caster->GetID()); this->CastToNPC()->SetCharmed(true); this->SetPetOrder(SPO_Follow); if(caster->IsClient()) caster->CastToClient()->SendCharmPermissions(); this->CastToNPC()->WhipeHateList(); this->CastToNPC()->StartTaunting(); } else if(this->IsClient()) { if(caster->IsNPC()) { caster->SetPet(this); this->SetOwnerID(caster->GetID()); Mob* myTarget = caster->CastToNPC()->GetHateTop(); if(!myTarget) myTarget = caster->CastToMob(); this->SetTarget(myTarget); this->animation = 0; this->delta_heading = 0; this->delta_x = 0; this->delta_y = 0; this->delta_z = 0; this->SendPosUpdate(true, PC_UPDATE_RANGE, false); this->CastToClient()->charmPositionUpdate_timer->Start(200); this->CastToClient()->SetCharmed(true); this->SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Lose_Control, false); this->SetPetOrder(SPO_Follow); } else if(caster->IsClient()) { caster->SetPet(this); this->SetOwnerID(caster->GetID()); this->SetTarget(caster); this->animation = 0; this->delta_heading = 0; this->delta_x = 0; this->delta_y = 0; this->delta_z = 0; this->SendPosUpdate(true, PC_UPDATE_RANGE, false); this->CastToClient()->charmPositionUpdate_timer->Start(200); this->CastToClient()->SetCharmed(true); this->SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Lose_Control, false); this->SetPetOrder(SPO_Follow); caster->CastToClient()->SendCharmPermissions(); } } } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a charm spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Fear: { //Yeahlight: Victim is a PC if(this->IsClient()) { this->CastToClient()->SetFeared(true); this->SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Lose_Control, false); this->CastToClient()->GetFearDestination(GetX(), GetY(), GetZ()); } //Yeahlight: Victim is an NPC else if(this->IsNPC()) { this->CastToNPC()->SetFeared(true); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a fear spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Stamina: { CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a stamina spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_BindAffinity: { //Yeahlight: Target of the bind affinity spell is a client if(this->IsClient()) { this->CastToClient()->SetBindPoint(); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a Bind Affinity spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Gate: { if(IsClient()) CastToClient()->GoToBind(); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a gate spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_CancelMagic: { for(int i = 0; i < 15; i++) { //Yeahlight: Buff must exist and the buff may not have any poison or disease counters if(buffs[i].spell && buffs[i].spell->IsValidSpell() && buffs[i].casterlevel <= (caster_level + base) && buffs[i].spell->GetDiseaseCounters() == 0 && buffs[i].spell->GetPoisonCounters() == 0) { this->BuffFadeBySlot(i, true); break; } } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a cancel magic spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_InvisVsUndead: { this->SetInvisibleUndead(true); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted an Invis VS Undead spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_InvisVsAnimals: { this->SetInvisibleAnimal(true); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted an Invis VS Animal spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Mez: { // Pinedepain // When a mezz spell is casted, we mesmerize this mob Mesmerize(); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a mesmerize spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_SummonItem: { if(this->IsClient()) { if(amount == 0) this->CastToClient()->SummonItem(base, 1); else this->CastToClient()->SummonItem(base, (amount > 20) ? 20 : amount); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a summon item spell: %i.", spell->GetSpellName(), base); break; } case SE_NecPet: case SE_SummonPet: { if (this->GetPetID() != 0) { Message(RED, "You\'ve already got a pet."); break; } this->MakePet(teleport_zone); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a Summon pet / nec pet spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_DivineAura: { this->SetInvulnerable(true); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a Divine Aura, amount: %i.", spell->GetSpellName(), amount); break; } case SE_ShadowStep: { //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a Shadow Step spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Rune: { //Yeahlight: Flag entity with rune for damage calculations hasRuneOn = true; CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a Rune spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Levitate: { this->SendAppearancePacket(0, SAT_Levitate, 2, true); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a levitate spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_SummonCorpse: { bool permit = false; Mob* corpseOwner = target; //Yeahlight: The target of this spell is not a PC or the caster is targeting themself if(!target || (target && !target->IsClient()) || target == this) { corpseOwner = this; permit = true; } //Yeahlight: Can only summon a PC's corpse if(corpseOwner && corpseOwner->IsClient()) { //Yeahlight: PCs must be grouped to summon a corpse if(!permit) { Group* targetGroup = entity_list.GetGroupByClient(corpseOwner->CastToClient()); Group* myGroup = NULL; if(this->IsClient()) myGroup = entity_list.GetGroupByClient(this->CastToClient()); //Yeahlight: Caster is in a group and they share the same group as the target if(myGroup != NULL && myGroup == targetGroup) permit = true; } //Yeahlight: Caster may proceed with the summon if(permit) { Corpse *corpse = entity_list.GetCorpseByOwner(corpseOwner->CastToClient()); //Yeahlight: Corpse has been located if(corpse) { corpse->Summon(corpseOwner->CastToClient(), caster->CastToClient(), true); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a summon corpse spell: %s.", spell->GetSpellName(), this->GetName()); } //Yeahlight: There is no corpse available else { //Yeahlight: Caster failed to locate his/her corpse if(caster == corpseOwner) { caster->Message(RED, "You do not have a corpse in this zone."); } //Yeahlight: Caster failed to locate their target's corpse else { caster->Message(RED, "Your target does not have a corpse in this zone."); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): Summon corpse: you can't sense the corpse: %i.", spell->GetSpellName()); } } else { //Yeahlight: TODO: This is not the correct message Message(RED, "You and your target must be in the same group to perform this action."); } } break; } case SE_Illusion: { SendIllusionPacket(base, GetDefaultGender(base, GetBaseGender()), GetTexture(), GetHelmTexture()); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted an illusion spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Identify: { //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted an identify spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_WipeHateList: { //Yeahlight: NOTE: Do NOT wipe the rampage list here; that never goes away until the mob resets if(this->IsNPC()) { //Yeahlight: TODO: I don't remember this message, look into this entity_list.MessageClose(this, true, DEFAULT_MESSAGE_RANGE, DARK_BLUE, "My mind fogs. Who are my friends? Who are my enemies?... it was all so clear a moment ago..."); this->CastToNPC()->WhipeHateList(); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a whipe hate list spell, amount: %i.", spell->GetSpellName(), amount); } break; } case SE_SpinTarget: { Spin(caster, spell); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a spin target spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_EyeOfZomm: { //Yeahlignt: Only produce eyes of zomm for PCs if(this->IsClient()) { if(this->CastToClient()->myEyeOfZomm == 0) MakeEyeOfZomm(this); else Message(RED, "You may only have one eye of zomm out at a time!"); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a Eye Of Zomm spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_ReclaimPet: { //Yeahlight: Target of the spell is an uncharmed NPC, has an owner and the owner is the caster of the spell if(IsNPC() && CastToNPC()->IsCharmed() == false && GetOwnerID() && caster->GetID() == GetOwnerID()) { //Yeahlight: TODO: Research this formula caster->SetMana(caster->GetMana()+(GetLevel()*4)); if(caster->IsClient()) { caster->CastToClient()->SetPet(0); } SetOwnerID(0); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a reclaim pet spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_FeignDeath: { if(this->IsClient()) this->CastToClient()->FeignDeath(this->CastToClient()->GetSkill(ABJURATION)); break; } case SE_VoiceGraft: { //Yeahlight: Only allow voice graft to be casted on NPCs (we don't want PCs griefing other charmed PCs with /say) if(IsNPC() && caster->IsClient() && CastToNPC()->GetOwner() == caster) { caster->CastToClient()->SetVoiceGrafting(true); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a voice graft spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Revive: { //Yeahlight: Handled in client_process.cpp CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a revive spell, amount: %i, corpse: %s.", spell->GetSpellName(), amount, this->GetName()); break; } case SE_Teleport: { char teleport_zone_char[64]; if(this->IsClient()) strcpy(teleport_zone_char, teleport_zone); this->CastToClient()->MovePC(teleport_zone_char, spell->GetSpellBase(1), spell->GetSpellBase(0), spell->GetSpellBase(2), false, false); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a teleport spell to %s (%f, %f, %f).", spell->GetSpellName(), teleport_zone, spell->GetSpellBase(1), spell->GetSpellBase(0), spell->GetSpellBase(2)); break; } case SE_Translocate: { bool permit = false; Mob* translocatee = CastToMob(); //Enraged: The target of this spell is an NPC. //if(translocatee && !translocatee->IsClient()) //{ // //Enraged: TODO: This is not the correct message? // Message(RED, "You cannot cast that spell on your current target."); // break; //} //Enraged: The target of this spell is the caster. //TODO: Can players target themselves with translocate spells? if(translocatee && (translocatee == this)) permit = true; //Enraged: Check if the targetted client is in the casters group. //TODO: Translocate only worked on group players, right? if(!permit && translocatee) { Group* translocateeGroup = entity_list.GetGroupByClient(translocatee->CastToClient()); Group* casterGroup = NULL; if(this->IsClient()) casterGroup = entity_list.GetGroupByClient(this->CastToClient()); //Enraged: The translocatee is in a group and they share the same group as the target if(casterGroup != NULL && casterGroup == translocateeGroup) permit = true; } //Enraged: Target is clear to be translocated. if(permit) { CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a translocation spell on %s.", spell->GetSpellName(), translocatee->GetName()); //TODO: Translocate code here translocatee->CastToClient()->SendTranslocateConfirmation(caster, spell); } else { //The translocatee was not in the casters group. //Enraged: TODO: This is not the correct message Message(RED, "You can only cast that spell on players in your group."); } break; } case SE_InfraVision: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted an infravision spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_UltraVision: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted an ultravision spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_BindSight: CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a bind sight spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_SeeInvis: SetCanSeeThroughInvis(true); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a see invis spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_WaterBreathing: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted an water breathing spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_SenseDead: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a sense dead spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_SenseSummoned: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a sense summoned spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_TrueNorth: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a true north spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_SenseAnimals: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a sense animals spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_DamageShield: //Yeahlight: There should not be any work to be done here CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a damage shield spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_Sentinel: CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a sentinel spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_LocateCorpse: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a locate corpse spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_ModelSize: { //Yeahlight: Grow/Shrink float newSize = (GetSize() * (float)base) / 100.00f; //Yeahlight: Size of a gnome (minimum) if(newSize < 3) newSize = 3; //Yeahlight: Size of an ogre (maximum) else if(newSize > 9) newSize = 9; this->size = newSize; this->SendAppearancePacket(GetID(), SAT_Size, GetSize(), true); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a model size spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Root: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a root spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_Blind: //Yeahlight: Handled client side CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a blind spell, amount: %i.", spell->GetSpellName(), amount); break; case SE_DiseaseCounter: { //Yeahlight: Spell is a cure disease spell if(amount < 0) { //Yeahlight: Iterate through all the debuffs on the target and check for the chance to cure it for(int i = 0; i < 15; i++) { if(buffs[i].spell && buffs[i].diseasecounters) { buffs[i].diseasecounters = buffs[i].diseasecounters + amount; if(buffs[i].diseasecounters <= 0) BuffFadeBySlot(i, true); break; } } } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a disease counter spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_PoisonCounter: { //Yeahlight: Spell is a cure poison spell if(amount < 0) { //Yeahlight: Iterate through all the debuffs on the target and check for the chance to cure it for(int i = 0; i < 15; i++) { if(buffs[i].spell && buffs[i].poisoncounters) { buffs[i].poisoncounters = buffs[i].poisoncounters + amount; if(buffs[i].poisoncounters <= 0) BuffFadeBySlot(i, true); break; } } } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a poison counter spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_Calm: { //Yeahlight: Only add/remove hate from NPCs if(this->IsNPC()) { this->CastToNPC()->AddToHateList(caster, 0, amount); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): You casted a calm hate spell, amount: %i.", spell->GetSpellName(), amount); break; } case SE_WeaponProc: { Spell* spell = spells_handler.GetSpellPtr(base); //Yeahlight: Legit spell proc bonus found if(spell) SetBonusProcSpell(spell); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell name = %s): Weapon proc bonus of spell ID %i.", spell->GetSpellName(), base); break; } case SE_StopRain: { zone->zone_weather = 0; zone->weatherSend(); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::ApplySpellsBonuses(spell name = %s): You casted a stop rain spell, amount: %i.", spell->GetSpellName(), base); break; } case SE_CallOfHero: { //Yeahlight: Call of the Hero may only be used on PCs int32 zoneid = 0; if(this->IsClient()) this->CastToClient()->MovePC(zoneid, caster->GetX(), caster->GetY(), caster->GetZ(), false, true); else caster->Message(RED, "This spell may only be cast on players."); CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::ApplySpellsBonuses(spell name = %s): You casted a call of the hero spell, amount: %i.", spell->GetSpellName(), base); break; } case SE_CallPet: { //Yeahlight: This spell line may only be used on NPC pets if(GetPet() && GetPet()->IsNPC()) { GetPet()->CastToNPC()->GMMove(GetX(), GetY(), GetZ(), GetHeading()); GetPet()->pStandingPetOrder = SPO_Follow; } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::ApplySpellsBonuses(spell name = %s): You casted a call pet spell, amount: %i.", spell->GetSpellName(), base); break; } case SE_DeathSave: { //Yeahlight: Only apply divine intervention to players if(this->IsClient()) { sint16 successChance = 0; float baseChance = 0.00f; switch(base) { //Yeahlight: Death Pact (CLR: 51) case 1: { baseChance = 0.10f; break; } //Yeahlight: Divine Intervention (CLR: 60) case 2: { baseChance = 0.30f; break; } default: { baseChance = 0.10f; } } //Yeahlight: The target's CHA is calculated into the bonus save chance successChance = (((float)CastToClient()->GetCHA() * 0.0005f) + baseChance) * 100; //Yeahlight: The worst possible save chance is the spell's base chance if(successChance < baseChance) successChance = baseChance; else if(successChance > 100) successChance = 100; this->CastToClient()->SetDeathSave(successChance); } CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::ApplySpellsBonuses(spell name = %s): You casted a death save spell, amount: %i.", spell->GetSpellName(), base); break; } case SE_Succor: { //Yeahlight: There should be nothing to do here CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::ApplySpellsBonuses(spell name = %s): You casted a succor spell, amount: %i.", spell->GetSpellName(), base); break; } case 0xFE: case 0xFF: case SE_Harmony: case SE_ChangeFrenzyRad: case SE_Lull: case SE_TotalHP: case SE_ArmorClass: case SE_MagnifyVision: case SE_ATK: case SE_STR: case SE_DEX: case SE_AGI: case SE_STA: case SE_INT: case SE_WIS: case SE_CHA: case SE_ResistFire: case SE_ResistCold: case SE_ResistPoison: case SE_ResistDisease: case SE_ResistMagic: { // Buffs are handeled elsewhere break; } default: { CAST_CLIENT_DEBUG_PTR(caster)->Log(CP_SPELL, "Mob::SpellEffect(spell_name = %s): unknown effect (%i) for amount: %i.", spell->GetSpellName(), effect_id, amount); break; } } } if(this->IsClient()) this->CastToClient()->Save(); }
//Yeahlight: Searches for the group that will be rewarded with exp and first loot rights returnGroup HateList::GetRewardingGroup() { groupDamage groupsToConsider[255] = {0}; int16 groupCounter = 0; int16 groupID = 0; int16 leadingGroupID = 0; sint32 highestGroupDamage = 0; sint32 entityDamage = 0; Mob* entity = NULL; returnGroup groupToReward; Group* group = NULL; bool groupFound = false; for(int i = 0; i < 255; i++) { groupsToConsider[i].groupID = -1; groupsToConsider[i].groupDamageTotal = -1; } groupToReward.damage = 0; groupToReward.group = NULL; LinkedListIterator<tHateEntry*> iterator(list); iterator.Reset(); while(iterator.MoreElements()) { entityDamage = 0; if(iterator.GetData()) entityDamage = iterator.GetData()->damage; if(entityDamage > 0) { entity = iterator.GetData()->ent; //Yeahlight: Entity exists in the zone if(entity) { //Yeahlight: Entity is a client if(entity->IsClient()) { //Yeahlight: PC is grouped if(entity->CastToClient()->IsGrouped()) { group = entity_list.GetGroupByClient(entity->CastToClient()); //Yeahlight: Group exists if(group) { groupID = group->GetID(); //Yeahlight: We do not have a group list started yet, so start it off with this group if(groupCounter == 0) { groupsToConsider[0].groupID = groupID; groupsToConsider[0].groupDamageTotal = iterator.GetData()->damage; groupsToConsider[0].group = group; groupCounter++; } //Yeahlight: A group list has been created already else { //Yeahlight: Iterate through the group list for(int i = 0; i < groupCounter; i++) { //Yeahlight: Found the group for which this PC resides if(groupID == groupsToConsider[i].groupID) { //Yeahlight: Add the client's damage to the group's total groupsToConsider[i].groupDamageTotal += iterator.GetData()->damage; //Yeahlight: Flag the PC as found groupFound = true; //Yeahlight: "Break out" of the loop i = groupCounter; } } //Yeahlight: This grouped PC did not find their group in the list, so we need to add their group if(!groupFound) { groupsToConsider[groupCounter].groupID = groupID; groupsToConsider[groupCounter].groupDamageTotal = iterator.GetData()->damage; groupsToConsider[groupCounter].group = group; groupCounter++; } } } //Yeahlight: Group does not exist else { } } //Yeahlight: PC is not grouped else { } } //Yeahlight: Entity is the pet of a client else if(entity->IsNPC() && entity->GetOwner() && entity->GetOwner()->IsClient()) { //Yeahlight: Owner of the pet is grouped if(entity->GetOwner()->CastToClient()->IsGrouped()) { group = entity_list.GetGroupByClient(entity->GetOwner()->CastToClient()); //Yeahlight: Group exists if(group) { groupID = group->GetID(); //Yeahlight: We do not have a group list started yet, so start it off with this group if(groupCounter == 0) { groupsToConsider[0].groupID = groupID; groupsToConsider[0].groupDamageTotal = iterator.GetData()->damage; groupsToConsider[0].group = group; groupCounter++; } //Yeahlight: A group list has been created already else { //Yeahlight: Iterate through the group list for(int i = 0; i < groupCounter; i++) { //Yeahlight: Found the group for which this pet resides if(groupID == groupsToConsider[i].groupID) { //Yeahlight: Add the pet's damage to the group's total groupsToConsider[i].groupDamageTotal += iterator.GetData()->damage; //Yeahlight: Flag the pet as found groupFound = true; //Yeahlight: "Break out" of the loop i = groupCounter; } } //Yeahlight: This grouped pet did not find their group in the list, so we need to add their group if(!groupFound) { groupsToConsider[groupCounter].groupID = groupID; groupsToConsider[groupCounter].groupDamageTotal = iterator.GetData()->damage; groupsToConsider[groupCounter].group = group; groupCounter++; } } } //Yeahlight: Group does not exist else { } } //Yeahlight: Pet's owner is not grouped else { } } } //Yeahlight: Entity does not exist in the zone else { } } iterator.Advance(); //Yeahlight: Zone freeze debug if(ZONE_FREEZE_DEBUG && rand()%ZONE_FREEZE_DEBUG == 1) EQC_FREEZE_DEBUG(__LINE__, __FILE__); } //Yeahlight: Iterate through the group list and record for the highest damaging group for(int i = 0; i < groupCounter; i++) { //Yeahlight: Found a group with more total damage than our current leader if(groupsToConsider[i].groupDamageTotal > highestGroupDamage) { highestGroupDamage = groupsToConsider[i].groupDamageTotal; groupToReward.damage = groupsToConsider[i].groupDamageTotal; groupToReward.group = groupsToConsider[i].group; } } return groupToReward; }
// solar: causes caster to hit every mob within dist range of center with // spell_id. // NPC spells will only affect other NPCs with compatible faction void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust) { Mob *curmob; float dist = caster->GetAOERange(spell_id); float dist2 = dist * dist; float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; float dist_targ = 0; bool detrimental = IsDetrimentalSpell(spell_id); bool clientcaster = caster->IsClient(); const int MAX_TARGETS_ALLOWED = 4; int targets_hit = 0; if(center->IsBeacon()) targets_hit = center->CastToBeacon()->GetTargetsHit(); for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { curmob = it->second; // test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading()) continue; if (curmob == center) //do not affect center continue; if (curmob == caster && !affect_caster) //watch for caster too continue; dist_targ = DistanceSquared(curmob->GetPosition(), center->GetPosition()); if (dist_targ > dist2) //make sure they are in range continue; if (dist_targ < min_range2) //make sure they are in range continue; if (!clientcaster && curmob->IsNPC()) { //check npc->npc casting FACTION_VALUE f = curmob->GetReverseFactionCon(caster); if (detrimental) { //affect mobs that are on our hate list, or //which have bad faction with us if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS)) continue; } else { //only affect mobs we would assist. if (!(f <= FACTION_AMIABLE)) continue; } } //finally, make sure they are within range if (detrimental) { if (!caster->IsAttackAllowed(curmob, true)) { Log.Out(Logs::Detail, Logs::Spells, "Attempting to cast a detrimental AE spell/song on a player."); continue; } if (!zone->SkipLoS() && !spells[spell_id].npc_no_los && curmob != caster && !center->CheckLosFN(curmob)) continue; } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... // This does not check faction for beneficial AE buffs..only agro and attackable. // I've tested for spells that I can find without problem, but a faction-based // check may still be needed. Any changes here should also reflect in BardAEPulse() -U if (caster->IsAttackAllowed(curmob, true)) { Log.Out(Logs::Detail, Logs::Spells, "Attempting to cast a beneficial AE spell/song on a NPC."); caster->Message_StringID(MT_SpellFailure, SPELL_NO_HOLD); continue; } if (caster->CheckAggro(curmob)) continue; } curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); //if we get here... cast the spell. if (IsTargetableAESpell(spell_id) && detrimental) { if (targets_hit < MAX_TARGETS_ALLOWED || (clientcaster && curmob == caster)) { caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); Log.Out(Logs::Detail, Logs::Spells, "AE Rain Spell: %d has hit target #%d: %s", spell_id, targets_hit, curmob->GetCleanName()); if (clientcaster && curmob != caster) //npcs are not target limited, pc caster does not count towards the limit. ++targets_hit; } } else { caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } } if(center->IsBeacon()) center->CastToBeacon()->SetTargetsHit(targets_hit); }