void Mob::SendToFixZ(float new_x, float new_y, float new_z) { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z + 0.1); } x_pos = new_x; y_pos = new_y; z_pos = new_z + 0.1; //fix up pathing Z, this shouldent be needed IF our waypoints //are corrected instead if(zone->HasMap() && RuleB(Map, FixPathingZOnSendTo)) { if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) { Map::Vertex dest(x_pos, y_pos, z_pos); float newz = zone->zonemap->FindBestZ(dest, nullptr); mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); if( (newz > -2000) && ABS(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check. z_pos = newz + 1; } } }
AString Faction::FactionTypeStr() { AString temp; if (IsNPC()) return AString("NPC"); if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_UNLIMITED) { return (AString("Unlimited")); } else if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_MAGE_COUNT) { return(AString("Normal")); } else if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_FACTION_TYPES) { int comma = 0; for (int i=0; i<NFACTYPES; i++) { if (type[i]) { if (comma) { temp += ", "; } else { comma = 1; } temp += AString(FactionStrs[i]) + " " + type[i]; } } if (!comma) return AString("none"); } return temp; }
void Mob::SendToFixZ(float new_x, float new_y, float new_z) { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z + 0.1); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z + 0.1; //fix up pathing Z, this shouldent be needed IF our waypoints //are corrected instead if(zone->HasMap() && RuleB(Map, FixPathingZOnSendTo)) { if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) { glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); float newz = zone->zonemap->FindBestZ(dest, nullptr); Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.x,m_Position.y,m_Position.z); if( (newz > -2000) && std::abs(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check. m_Position.z = newz + 1; } } }
bool Mob::CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ) { if(IsNPC() || IsClient() || IsPet()) { pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); speed *= NPC_SPEED_MULTIPLIER; } return MakeNewPositionAndSendUpdate(x, y, z, speed, checkZ); }
void NPC::MoveTo(float mtx, float mty, float mtz, float mth, bool saveguardspot) { // makes mob walk to specified location if (IsNPC() && GetGrid() != 0) { // he is on a grid if (GetGrid() < 0) { // currently stopped by a quest command SetGrid( 0 - GetGrid()); // get him moving again mlog(AI__WAYPOINTS, "MoveTo during quest wandering. Canceling quest wandering and going back to grid %d when MoveTo is done.", GetGrid()); } AIwalking_timer->Disable(); // disable timer in case he is paused at a wp if (cur_wp>=0) { // we've not already done a MoveTo() save_wp=cur_wp; // save the current waypoint cur_wp=-1; // flag this move as quest controlled } mlog(AI__WAYPOINTS, "MoveTo (%.3f, %.3f, %.3f), pausing regular grid wandering. Grid %d, save_wp %d", mtx, mty, mtz, -GetGrid(), save_wp); } else { // not on a grid roamer=true; save_wp=0; cur_wp=-2; // flag as quest controlled w/no grid mlog(AI__WAYPOINTS, "MoveTo (%.3f, %.3f, %.3f) without a grid.", mtx, mty, mtz); } if (saveguardspot) { guard_x = mtx; guard_y = mty; guard_z = mtz; guard_heading = mth; if(guard_heading == 0) guard_heading = 0.0001; //hack to make IsGuarding simpler if(guard_heading == -1) guard_heading = this->CalculateHeadingToTarget(mtx, mty); mlog(AI__WAYPOINTS, "Setting guard position to (%.3f, %.3f, %.3f)", guard_x, guard_y, guard_z); } cur_wp_x = mtx; cur_wp_y = mty; cur_wp_z = mtz; cur_wp_pause = 0; cur_wp_heading = mth; pLastFightingDelayMoving = 0; if(AIwalking_timer->Enabled()) AIwalking_timer->Start(100); }
void NPC::ResumeWandering() { // causes wandering to continue - overrides waypoint pause timer and PauseWandering() if(!IsNPC()) return; if (GetGrid() != 0) { if (GetGrid() < 0) { // we were paused by a quest AIwalking_timer->Disable(); SetGrid( 0 - GetGrid()); if (cur_wp==-1) { // got here by a MoveTo() cur_wp=save_wp; UpdateWaypoint(cur_wp); // have him head to last destination from here } Log.Out(Logs::Detail, Logs::Pathing, "Resume Wandering requested. Grid %d, wp %d", GetGrid(), cur_wp); } else if (AIwalking_timer->Enabled()) { // we are at a waypoint paused normally Log.Out(Logs::Detail, Logs::Pathing, "Resume Wandering on timed pause. Grid %d, wp %d", GetGrid(), cur_wp); AIwalking_timer->Trigger(); // disable timer to end pause now } else { Log.Out(Logs::General, Logs::Error, "NPC not paused - can't resume wandering: %lu", (unsigned long)GetNPCTypeID()); return; } if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) { // are we we at a waypoint? if so, trigger event and start to next char temp[100]; itoa(cur_wp,temp,10); //do this before updating to next waypoint CalculateNewWaypoint(); SetAppearance(eaStanding, false); parse->EventNPC(EVENT_WAYPOINT_DEPART, this, nullptr, temp, 0); } // if not currently at a waypoint, we continue on to the one we were headed to before the stop } else { Log.Out(Logs::General, Logs::Error, "NPC not on grid - can't resume wandering: %lu", (unsigned long)GetNPCTypeID()); } return; }
void NPC::MoveTo(const glm::vec4& position, bool saveguardspot) { // makes mob walk to specified location if (IsNPC() && GetGrid() != 0) { // he is on a grid if (GetGrid() < 0) { // currently stopped by a quest command SetGrid( 0 - GetGrid()); // get him moving again Log.Out(Logs::Detail, Logs::AI, "MoveTo during quest wandering. Canceling quest wandering and going back to grid %d when MoveTo is done.", GetGrid()); } AIwalking_timer->Disable(); // disable timer in case he is paused at a wp if (cur_wp>=0) { // we've not already done a MoveTo() save_wp=cur_wp; // save the current waypoint cur_wp=-1; // flag this move as quest controlled } Log.Out(Logs::Detail, Logs::AI, "MoveTo %s, pausing regular grid wandering. Grid %d, save_wp %d",to_string(static_cast<glm::vec3>(position)).c_str(), -GetGrid(), save_wp); } else { // not on a grid roamer=true; save_wp=0; cur_wp=-2; // flag as quest controlled w/no grid Log.Out(Logs::Detail, Logs::AI, "MoveTo %s without a grid.", to_string(static_cast<glm::vec3>(position)).c_str()); } if (saveguardspot) { m_GuardPoint = position; if(m_GuardPoint.w == 0) m_GuardPoint.w = 0.0001; //hack to make IsGuarding simpler if(m_GuardPoint.w == -1) m_GuardPoint.w = this->CalculateHeadingToTarget(position.x, position.y); Log.Out(Logs::Detail, Logs::AI, "Setting guard position to %s", to_string(static_cast<glm::vec3>(m_GuardPoint)).c_str()); } m_CurrentWayPoint = position; cur_wp_pause = 0; pLastFightingDelayMoving = 0; if(AIwalking_timer->Enabled()) AIwalking_timer->Start(100); }
void Faction::WriteReport(Areport *f, Game *pGame) { if (IsNPC() && num == 1) { if (Globals->GM_REPORT || (pGame->month == 0 && pGame->year == 1)) { int i, j; // Put all skills, items and objects in the GM report shows.DeleteAll(); for (i = 0; i < NSKILLS; i++) { for (j = 1; j < 6; j++) { shows.Add(new ShowSkill(i, j)); } } if (shows.Num()) { f->PutStr("Skill reports:"); forlist(&shows) { AString *string = ((ShowSkill *)elem)->Report(this); if (string) { f->PutStr(""); f->PutStr(*string); delete string; } } shows.DeleteAll(); f->EndLine(); } itemshows.DeleteAll(); for (i = 0; i < NITEMS; i++) { AString *show = ItemDescription(i, 1); if (show) { itemshows.Add(show); } } if (itemshows.Num()) { f->PutStr("Item reports:"); forlist(&itemshows) { f->PutStr(""); f->PutStr(*((AString *)elem)); } itemshows.DeleteAll(); f->EndLine(); }
bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ, bool calcHeading) { if(GetID()==0) return true; float nx = m_Position.x; float ny = m_Position.y; float nz = m_Position.z; // if NPC is rooted if (speed == 0) { SetHeading(CalculateHeadingToTarget(x, y)); if(moved){ SetCurrentSpeed(0); moved=false; } Log.Out(Logs::Detail, Logs::AI, "Rooted while calculating new position to (%.3f, %.3f, %.3f)", x, y, z); return true; } float old_test_vector=test_vector; m_TargetV.x = x - nx; m_TargetV.y = y - ny; m_TargetV.z = z - nz; if (m_TargetV.x == 0 && m_TargetV.y == 0) return false; SetCurrentSpeed((int8)(speed)); //*NPC_RUNANIM_RATIO); //speed *= NPC_SPEED_MULTIPLIER; Log.Out(Logs::Detail, Logs::AI, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z, speed, pRunAnimSpeed); // -------------------------------------------------------------------------- // 2: get unit vector // -------------------------------------------------------------------------- test_vector=sqrtf (x*x + y*y + z*z); tar_vector = speed / sqrtf (m_TargetV.x*m_TargetV.x + m_TargetV.y*m_TargetV.y + m_TargetV.z*m_TargetV.z); m_Position.w = CalculateHeadingToTarget(x, y); if (tar_vector >= 1.0) { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } m_Position.x = x; m_Position.y = y; m_Position.z = z; Log.Out(Logs::Detail, Logs::AI, "Close enough, jumping to waypoint"); } else { float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector; float new_z = m_Position.z + m_TargetV.z*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z; Log.Out(Logs::Detail, Logs::AI, "Next position (%.3f, %.3f, %.3f)", m_Position.x, m_Position.y, m_Position.z); } uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) { glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.x,m_Position.y,m_Position.z); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) { if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving)) m_Position.z = z; else m_Position.z = newz + 1; } else m_Position.z = newz+1; } } } //OP_MobUpdate if((old_test_vector!=test_vector) || tar_ndx>20){ //send update tar_ndx=0; this->SetMoving(true); moved=true; m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f); SendPosUpdate(); } tar_ndx++; // now get new heading SetAppearance(eaStanding, false); // make sure they're standing pLastChange = Timer::GetCurrentTime(); return true; }
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ) { if(GetID()==0) return true; if(speed <= 0) { SetCurrentSpeed(0); return true; } if ((m_Position.x-x == 0) && (m_Position.y-y == 0)) {//spawn is at target coords if(m_Position.z-z != 0) { m_Position.z = z; Log.Out(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f): Jumping pure Z.", x, y, z); return true; } Log.Out(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f) inWater=%d: We are there.", x, y, z, inWater); return false; } else if ((std::abs(m_Position.x - x) < 0.1) && (std::abs(m_Position.y - y) < 0.1)) { Log.Out(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f): X/Y difference <0.1, Jumping to target.", x, y, z); if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } m_Position.x = x; m_Position.y = y; m_Position.z = z; return true; } bool send_update = false; int compare_steps = 20; if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) { float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector; float new_z = m_Position.z + m_TargetV.z*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z; Log.Out(Logs::Detail, Logs::AI, "Calculating new position2 to (%.3f, %.3f, %.3f), old vector (%.3f, %.3f, %.3f)", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z); uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) { glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.x,m_Position.y,m_Position.z); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if ((std::abs(x - m_Position.x) < 0.5) && (std::abs(y - m_Position.y) < 0.5)) { if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving)) m_Position.z = z; else m_Position.z = newz + 1; } else m_Position.z = newz + 1; } } } tar_ndx++; return true; } if (tar_ndx>50) { tar_ndx--; } else { tar_ndx=0; } m_TargetLocation = glm::vec3(x, y, z); float nx = this->m_Position.x; float ny = this->m_Position.y; float nz = this->m_Position.z; // float nh = this->heading; m_TargetV.x = x - nx; m_TargetV.y = y - ny; m_TargetV.z = z - nz; SetCurrentSpeed((int8)speed); pRunAnimSpeed = speed; if(IsClient()) { animation = speed / 2; } //pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); //speed *= NPC_SPEED_MULTIPLIER; Log.Out(Logs::Detail, Logs::AI, "Calculating new position2 to (%.3f, %.3f, %.3f), new vector (%.3f, %.3f, %.3f) rate %.3f, RAS %d", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z, speed, pRunAnimSpeed); // -------------------------------------------------------------------------- // 2: get unit vector // -------------------------------------------------------------------------- float mag = sqrtf (m_TargetV.x*m_TargetV.x + m_TargetV.y*m_TargetV.y + m_TargetV.z*m_TargetV.z); tar_vector = (float)speed / mag; // mob move fix int numsteps = (int) ( mag * 16.0f / (float)speed + 0.5f); // mob move fix if (numsteps<20) { if (numsteps>1) { tar_vector=1.0f ; m_TargetV.x = m_TargetV.x/(float)numsteps; m_TargetV.y = m_TargetV.y/(float)numsteps; m_TargetV.z = m_TargetV.z/(float)numsteps; float new_x = m_Position.x + m_TargetV.x; float new_y = m_Position.y + m_TargetV.y; float new_z = m_Position.z + m_TargetV.z; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z; m_Position.w = CalculateHeadingToTarget(x, y); tar_ndx = 20 - numsteps; Log.Out(Logs::Detail, Logs::AI, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", m_Position.x, m_Position.y, m_Position.z, numsteps); } else { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } m_Position.x = x; m_Position.y = y; m_Position.z = z; Log.Out(Logs::Detail, Logs::AI, "Only a single step to get there... jumping."); } } else { tar_vector/=16.0f; float dur = Timer::GetCurrentTime() - pLastChange; if(dur < 1.0f) { dur = 1.0f; } tar_vector = (tar_vector * AImovement_duration) / 100.0f; float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector; float new_z = m_Position.z + m_TargetV.z*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z; m_Position.w = CalculateHeadingToTarget(x, y); Log.Out(Logs::Detail, Logs::AI, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", m_Position.x, m_Position.y, m_Position.z, numsteps); } uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) { glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); float newz = zone->zonemap->FindBestZ(dest, nullptr); Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.x, m_Position.y, m_Position.z); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) { if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving)) m_Position.z = z; else m_Position.z = newz + 1; } else m_Position.z = newz+1; } } } SetMoving(true); moved=true; m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f); if (IsClient()) { SendPosUpdate(1); CastToClient()->ResetPositionTimer(); } else { SendPosUpdate(); SetAppearance(eaStanding, false); } pLastChange = Timer::GetCurrentTime(); return true; }
void ZCharacterObject::Draw_SetLight(const rvector& vPosition) { // ±¤¿øÁ¤ÀÇ // 0¹ø - ambient : ij¸¯ÅÍÀÇ ±âº» ¹à±â // - diffuse : ¼ø°£ÀûÀÎ ±¤¿ø( ÃÑ·ù¸¦ ¹ß»çÇÒ¶§ Àû¿ë) // 1¹ø - ambient : »ç¿ë¾ÈÇÔ // - diffuse : ¸ÊÀÇ µðÇ»Áî ¶óÀÌÆ® // specular, emit ¼Ó¼ºÀº »ç¿ë¾ÈÇÔ #define CHARACTER_AMBIENT 0.0 D3DLIGHT9 Light; rvector pos; rvector char_pos = vPosition; char_pos.z += 180.f; memset( &Light, 0, sizeof( D3DLIGHT9 )); // ÃʱâÈ // 0¹ø ¶óÀÌÆ® begine Light.Type = D3DLIGHT_POINT; Light.Ambient.r = CHARACTER_AMBIENT; Light.Ambient.g = CHARACTER_AMBIENT; Light.Ambient.b = CHARACTER_AMBIENT; Light.Specular.r = 1.f; Light.Specular.g = 1.f; Light.Specular.b = 1.f; Light.Specular.a = 1.f; if( ZGetConfiguration()->GetVideo()->bDynamicLight && m_bDynamicLight ) { m_vLightColor.x -= 0.03f; m_vLightColor.y -= 0.03f; m_vLightColor.z -= 0.03f; max( m_vLightColor.x, 0.0f ); max( m_vLightColor.y, 0.0f ); max( m_vLightColor.z, 0.0f ); Light.Diffuse.r = m_vLightColor.x; Light.Diffuse.g = m_vLightColor.y; Light.Diffuse.b = m_vLightColor.z; Light.Range = g_CharLightList[m_iDLightType].fRange; float lastTime = m_fTime; m_fTime = timeGetTime(); float lap = m_fTime - lastTime; m_fLightLife -= lap; if( m_fLightLife <= 0.0f ) { m_bDynamicLight = false; m_fLightLife = 0; } } else { m_bDynamicLight = false; m_vLightColor.x = 0.0f; m_vLightColor.y = 0.0f; m_vLightColor.z = 0.0f; } if( IsDoubleGun() ) { if( m_bLeftShot ) GetWeaponTypePos( weapon_dummy_muzzle_flash, &pos ,true); else GetWeaponTypePos( weapon_dummy_muzzle_flash, &pos ); m_bLeftShot = !m_bLeftShot; } else { GetWeaponTypePos( weapon_dummy_muzzle_flash, &pos ); } Light.Position.x = pos.x ; Light.Position.y = pos.y ; Light.Position.z = pos.z ; Light.Attenuation1 = 0.05f; Light.Attenuation2 = 0.001f; if (IsNPC()) { Light.Ambient.r = 0.4f; Light.Ambient.g = 0.4f; Light.Ambient.b = 0.4f; Light.Range = 2000.0f; Light.Attenuation0 = 1.0f; Light.Attenuation1 = 0.0f; Light.Attenuation2 = 0.0f; } // RGetDevice()->SetLight( 0, &Light ); // RGetDevice()->LightEnable( 0, TRUE ); // if( RShaderMgr::mbUsingShader ) // { // RGetShaderMgr()->setLight( 0, &Light ); // RGetShaderMgr()->LightEnable( 0, TRUE ); // } m_pVMesh->SetLight(0,&Light,false); // 0¹ø ¶óÀÌÆ® end // 1¹ø ¶óÀÌÆ® begine ZeroMemory( &Light, sizeof(D3DLIGHT9) ); RLightList* pLightList = 0; RLIGHT* pLight = 0; RLIGHT* pSelectedLight = 0; rvector sunDir; float distance; float SelectedLightDistance = FLT_MAX; pLightList = ZGetGame()->GetWorld()->GetBsp()->GetSunLightList(); int nLight = (int)pLightList->size(); Light.Specular.r = 1.f; Light.Specular.g = 1.f; Light.Specular.b = 1.f; Light.Specular.a = 1.f; if( nLight > 0 ) { // Áß°£¿¡ ¸Ê ÇÈÅ·ÀÌ ¾ÈµÇ¸é.. // ÇØ°¡ ¿©·¯°³À̸é? °Å¸®·Î ¼±ÅÃ.. for( RLightList::iterator iter = pLightList->begin(); iter != pLightList->end(); ++iter ) { pLight = *iter; sunDir = pLight->sPosition - char_pos; distance = D3DXVec3LengthSq( &sunDir ); D3DXVec3Normalize( &sunDir, &sunDir ); RBSPPICKINFO info; if( ZGetGame()->GetWorld()->GetBsp()->Pick( char_pos, sunDir, &info,RM_FLAG_ADDITIVE ) ) { if( distance > D3DXVec3LengthSq(&( char_pos - info.PickPos )) ) { continue; } } if( distance < SelectedLightDistance ) { SelectedLightDistance = distance; pSelectedLight = pLight; } } Light.Type = D3DLIGHT_POINT; Light.Attenuation1 = 0.00001f; if( pSelectedLight != 0 ) { Light.Diffuse.r = pSelectedLight->Color.x * pSelectedLight->fIntensity * 0.15f; Light.Diffuse.g = pSelectedLight->Color.y * pSelectedLight->fIntensity * 0.15f; Light.Diffuse.b = pSelectedLight->Color.z * pSelectedLight->fIntensity * 0.15f; } } SelectedLightDistance = FLT_MAX ; if( pSelectedLight == 0 ) { pLightList = ZGetGame()->GetWorld()->GetBsp()->GetObjectLightList(); // ¿©±â ÃÖÀûÈ°¡ ÂÀ ÇÊ¿äÇÏ´Ù for( RLightList::iterator itor = pLightList->begin(); itor != pLightList->end(); itor++ ) { RLIGHT *pl = *itor; float fDist = Magnitude( pl->sPosition-char_pos ); if( SelectedLightDistance > fDist ) { SelectedLightDistance = fDist; pSelectedLight = pl; } } Light.Type = D3DLIGHT_POINT; Light.Attenuation1 = 0.0025f; if( pSelectedLight != 0 ) { Light.Diffuse.r = pSelectedLight->Color.x * pSelectedLight->fIntensity; Light.Diffuse.g = pSelectedLight->Color.y * pSelectedLight->fIntensity; Light.Diffuse.b = pSelectedLight->Color.z * pSelectedLight->fIntensity; } } if( pSelectedLight != NULL ) { Light.Position = pSelectedLight->sPosition; // Light.Diffuse.r = pSelectedLight->Color.x; // Light.Diffuse.g = pSelectedLight->Color.y; // Light.Diffuse.b = pSelectedLight->Color.z; Light.Range = pSelectedLight->fAttnEnd; m_pVMesh->SetLight(1,&Light,false); // RGetDevice()->SetLight( 1, &Light ); // RGetDevice()->LightEnable( 1, TRUE ); // if( RShaderMgr::mbUsingShader ) // { // RGetShaderMgr()->setLight( 1, &Light ); // RGetShaderMgr()->LightEnable( 1, TRUE ); // RGetShaderMgr()->setLight( 0x00cccccc ); // } } else { m_pVMesh->SetLight(1,NULL,false); // RGetDevice()->LightEnable( 1, FALSE ); // if( RShaderMgr::mbUsingShader ) // { // RGetShaderMgr()->LightEnable( 1, FALSE ); // } } // 1¹ø ¶óÀÌÆ® end RGetShaderMgr()->setAmbient( 0x00cccccc ); RGetDevice()->SetRenderState( D3DRS_LIGHTING, TRUE ); RGetDevice()->SetRenderState( D3DRS_AMBIENT, 0x00cccccc ); }
int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { if (spells[spell_id].targettype == ST_Self) return value; if (IsNPC()) value += value*CastToNPC()->GetSpellFocusDMG()/100; bool Critical = false; int32 value_BaseEffect = 0; int chance = 0; value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. if ((spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) value -= (GetLevel() - 40) * 20; //This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch. if (spell_id == SPELL_IMP_HARM_TOUCH && IsClient()) //Improved Harm Touch value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level) chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation; //Crtical Hit Calculation pathway if (chance > 0 || (IsClient() && GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) { int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. //Improved Harm Touch is a guaranteed crit if you have at least one level of SCF. if (spell_id == SPELL_IMP_HARM_TOUCH && IsClient() && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0)) chance = 100; if (zone->random.Roll(chance)) { Critical = true; ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack; } else if ((IsClient() && GetClass() == WIZARD) || (IsMerc() && GetClass() == CASTERDPS)) { if ((GetLevel() >= RuleI(Spells, WizCritLevel)) && zone->random.Roll(RuleI(Spells, WizCritChance))){ //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed) ratio += zone->random.Int(20,70); Critical = true; } } if (IsClient() && GetClass() == WIZARD) ratio += RuleI(Spells, WizCritRatio); //Default is zero if (Critical){ value = value_BaseEffect*ratio/100; value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; if (target) { value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; value -= target->GetFcDamageAmtIncoming(this, spell_id); } value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100; value -= GetFocusEffect(focusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; else if (IsNPC() && CastToNPC()->GetSpellScale()) value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f); entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_BLAST, GetName(), itoa(-value)); if (IsClient()) Message_StringID(MT_SpellCrits, YOU_CRIT_BLAST, itoa(-value)); return value; } } //Non Crtical Hit Calculation pathway value = value_BaseEffect; value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; if (target) { value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; value -= target->GetFcDamageAmtIncoming(this, spell_id); } value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id); value -= GetFocusEffect(focusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); if (IsNPC() && CastToNPC()->GetSpellScale()) value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f); return value; }
int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { if (target == nullptr) return value; if (IsNPC()) value += value*CastToNPC()->GetSpellFocusDMG()/100; int32 value_BaseEffect = 0; int32 extra_dmg = 0; int16 chance = 0; chance += itembonuses.CriticalDoTChance + spellbonuses.CriticalDoTChance + aabonuses.CriticalDoTChance; if (spellbonuses.CriticalDotDecay) chance += GetDecayEffectValue(spell_id, SE_CriticalDotDecay); value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); if (chance > 0 && (zone->random.Roll(chance))) { int32 ratio = 200; ratio += itembonuses.DotCritDmgIncrease + spellbonuses.DotCritDmgIncrease + aabonuses.DotCritDmgIncrease; value = value_BaseEffect*ratio/100; value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100; value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) + GetFocusEffect(focusFcDamageAmt, spell_id); if (extra_dmg) { int duration = CalcBuffDuration(this, this, spell_id); if (duration > 0) extra_dmg /= duration; } value -= extra_dmg; } else { value = value_BaseEffect; value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + GetFocusEffect(focusFcDamageAmtCrit, spell_id) + GetFocusEffect(focusFcDamageAmt, spell_id); if (extra_dmg) { int duration = CalcBuffDuration(this, this, spell_id); if (duration > 0) extra_dmg /= duration; } value -= extra_dmg; } if (IsNPC() && CastToNPC()->GetSpellScale()) value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f); return value; }
bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool checkZ) { if(GetID()==0) return true; float nx = x_pos; float ny = y_pos; float nz = z_pos; // if NPC is rooted if (speed == 0.0) { SetHeading(CalculateHeadingToTarget(x, y)); if(moved){ SendPosition(); SetMoving(false); moved=false; } SetRunAnimSpeed(0); mlog(AI__WAYPOINTS, "Rooted while calculating new position to (%.3f, %.3f, %.3f)", x, y, z); return true; } float old_test_vector=test_vector; tar_vx = x - nx; tar_vy = y - ny; tar_vz = z - nz; if (tar_vx == 0 && tar_vy == 0) return false; pRunAnimSpeed = (uint8)(speed*NPC_RUNANIM_RATIO); speed *= NPC_SPEED_MULTIPLIER; mlog(AI__WAYPOINTS, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, tar_vx, tar_vy, tar_vz, speed, pRunAnimSpeed); // -------------------------------------------------------------------------- // 2: get unit vector // -------------------------------------------------------------------------- test_vector=sqrtf (x*x + y*y + z*z); tar_vector = speed / sqrtf (tar_vx*tar_vx + tar_vy*tar_vy + tar_vz*tar_vz); heading = CalculateHeadingToTarget(x, y); if (tar_vector >= 1.0) { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } x_pos = x; y_pos = y; z_pos = z; mlog(AI__WAYPOINTS, "Close enough, jumping to waypoint"); } else { float new_x = x_pos + tar_vx*tar_vector; float new_y = y_pos + tar_vy*tar_vector; float new_z = z_pos + tar_vz*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } x_pos = new_x; y_pos = new_y; z_pos = new_z; mlog(AI__WAYPOINTS, "Next position (%.3f, %.3f, %.3f)", x_pos, y_pos, z_pos); } uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) { Map::Vertex dest(x_pos, y_pos, z_pos); float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if(ABS(x - x_pos) < 0.5 && ABS(y - y_pos) < 0.5) { if(ABS(z - z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving)) z_pos = z; else z_pos = newz + 1; } else z_pos = newz+1; } } } //OP_MobUpdate if((old_test_vector!=test_vector) || tar_ndx>20){ //send update tar_ndx=0; this->SetMoving(true); moved=true; delta_x=(x_pos-nx); delta_y=(y_pos-ny); delta_z=(z_pos-nz); delta_heading=0;//(heading-nh)*8; SendPosUpdate(); } tar_ndx++; // now get new heading SetAppearance(eaStanding, false); // make sure they're standing pLastChange = Timer::GetCurrentTime(); return true; }
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ) { if(GetID()==0) return true; if ((x_pos-x == 0) && (y_pos-y == 0)) {//spawn is at target coords if(z_pos-z != 0) { z_pos = z; mlog(AI__WAYPOINTS, "Calc Position2 (%.3f, %.3f, %.3f): Jumping pure Z.", x, y, z); return true; } mlog(AI__WAYPOINTS, "Calc Position2 (%.3f, %.3f, %.3f) inWater=%d: We are there.", x, y, z, inWater); return false; } else if ((ABS(x_pos - x) < 0.1) && (ABS(y_pos - y) < 0.1)) { mlog(AI__WAYPOINTS, "Calc Position2 (%.3f, %.3f, %.3f): X/Y difference <0.1, Jumping to target.", x, y, z); if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } x_pos = x; y_pos = y; z_pos = z; return true; } int compare_steps = IsBoat() ? 1 : 20; if(tar_ndx < compare_steps && tarx==x && tary==y) { float new_x = x_pos + tar_vx*tar_vector; float new_y = y_pos + tar_vy*tar_vector; float new_z = z_pos + tar_vz*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } x_pos = new_x; y_pos = new_y; z_pos = new_z; mlog(AI__WAYPOINTS, "Calculating new position2 to (%.3f, %.3f, %.3f), old vector (%.3f, %.3f, %.3f)", x, y, z, tar_vx, tar_vy, tar_vz); uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) { Map::Vertex dest(x_pos, y_pos, z_pos); float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if((ABS(x - x_pos) < 0.5) && (ABS(y - y_pos) < 0.5)) { if(ABS(z-z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving)) z_pos = z; else z_pos = newz + 1; } else z_pos = newz + 1; } } } tar_ndx++; return true; } if (tar_ndx>50) { tar_ndx--; } else { tar_ndx=0; } tarx=x; tary=y; tarz=z; float nx = this->x_pos; float ny = this->y_pos; float nz = this->z_pos; // float nh = this->heading; tar_vx = x - nx; tar_vy = y - ny; tar_vz = z - nz; //pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); //speed *= NPC_SPEED_MULTIPLIER; mlog(AI__WAYPOINTS, "Calculating new position2 to (%.3f, %.3f, %.3f), new vector (%.3f, %.3f, %.3f) rate %.3f, RAS %d", x, y, z, tar_vx, tar_vy, tar_vz, speed, pRunAnimSpeed); // -------------------------------------------------------------------------- // 2: get unit vector // -------------------------------------------------------------------------- float mag = sqrtf (tar_vx*tar_vx + tar_vy*tar_vy + tar_vz*tar_vz); tar_vector = speed / mag; // mob move fix int numsteps = (int) ( mag * 20 / speed) + 1; // mob move fix if (numsteps<20) { if (numsteps>1) { tar_vector=1.0f ; tar_vx = tar_vx/numsteps; tar_vy = tar_vy/numsteps; tar_vz = tar_vz/numsteps; float new_x = x_pos + tar_vx; float new_y = y_pos + tar_vy; float new_z = z_pos + tar_vz; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } x_pos = new_x; y_pos = new_y; z_pos = new_z; tar_ndx=22-numsteps; heading = CalculateHeadingToTarget(x, y); mlog(AI__WAYPOINTS, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", x_pos, y_pos, z_pos, numsteps); } else { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } x_pos = x; y_pos = y; z_pos = z; mlog(AI__WAYPOINTS, "Only a single step to get there... jumping."); } } else { tar_vector/=20; float new_x = x_pos + tar_vx*tar_vector; float new_y = y_pos + tar_vy*tar_vector; float new_z = z_pos + tar_vz*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } x_pos = new_x; y_pos = new_y; z_pos = new_z; heading = CalculateHeadingToTarget(x, y); mlog(AI__WAYPOINTS, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", x_pos, y_pos, z_pos, numsteps); } uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) { Map::Vertex dest(x_pos, y_pos, z_pos); float newz = zone->zonemap->FindBestZ(dest, nullptr); + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if(ABS(x - x_pos) < 0.5 && ABS(y - y_pos) < 0.5) { if(ABS(z - z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving)) z_pos = z; else z_pos = newz + 1; } else z_pos = newz+1; } } } SetMoving(true); moved=true; delta_x=x_pos-nx; delta_y=y_pos-ny; delta_z=z_pos-nz; delta_heading=0; if (IsClient()) SendPosUpdate(1); else SendPosUpdate(); SetAppearance(eaStanding, false); pLastChange = Timer::GetCurrentTime(); return true; }
void Aura::ProcessOnGroupMembersPets(Mob *owner) { auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline std::set<int> delayed_remove; bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter // This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura) auto group_member = owner->GetOwnerOrSelf(); if (group_member->IsRaidGrouped() && group_member->IsClient()) { // currently raids are just client, but safety check auto raid = group_member->GetRaid(); if (raid == nullptr) { // well shit owner->RemoveAura(GetID(), false, true); return; } auto group_id = raid->GetGroup(group_member->CastToClient()); // some lambdas so the for loop is less horrible ... auto verify_raid_client_pet = [&raid, &group_id, &group_member, this](Mob *m) { auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient()); if (m->GetOwner()->GetID() == group_member->GetID()) { return DistanceSquared(GetPosition(), m->GetPosition()) <= distance; } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; } else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { return false; } return true; }; auto verify_raid_client_swarm = [&raid, &group_id, &group_member, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); if (owner == nullptr) return false; auto idx = raid->GetPlayerIndex(owner->CastToClient()); if (owner->GetID() == group_member->GetID()) { return DistanceSquared(GetPosition(), n->GetPosition()) <= distance; } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; } else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { return false; } return true; }; for (auto &e : mob_list) { auto mob = e.second; // step 1: check if we're already managing this NPC's buff auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // verify still good! if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { if (!verify_raid_client_pet(mob)) delayed_remove.insert(mob->GetID()); } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { auto npc = mob->CastToNPC(); if (!verify_raid_client_swarm(npc)) delayed_remove.insert(mob->GetID()); } } else { // we're not on it! if (mob->IsClient()) { continue; // never hit client } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { casted_on.insert(mob->GetID()); if (is_buff) SpellFinished(spell_id, mob); } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { auto npc = mob->CastToNPC(); if (verify_raid_client_swarm(npc)) { casted_on.insert(mob->GetID()); if (is_buff) SpellFinished(spell_id, mob); } } } } } else if (group_member->IsGrouped()) { auto group = group_member->GetGroup(); if (group == nullptr) { // uh oh owner->RemoveAura(GetID(), false, true); return; } // lambdas to make for loop less ugly auto verify_group_pet = [&group, this](Mob *m) { auto owner = m->GetOwner(); if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance) return true; return false; }; auto verify_group_swarm = [&group, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance) return true; return false; }; for (auto &e : mob_list) { auto mob = e.second; auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // make sure we're still valid if (mob->IsPet()) { if (!verify_group_pet(mob)) delayed_remove.insert(mob->GetID()); } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { if (!verify_group_swarm(mob->CastToNPC())) delayed_remove.insert(mob->GetID()); } } else { // not on, check if we should be! if (mob->IsClient()) { continue; } else if (mob->IsPet() && verify_group_pet(mob)) { casted_on.insert(mob->GetID()); if (is_buff) SpellFinished(spell_id, mob); } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { casted_on.insert(mob->GetID()); if (is_buff) SpellFinished(spell_id, mob); } } } } else { auto verify_solo = [&group_member, this](Mob *m) { if (m->IsPet() && m->GetOwnerID() == group_member->GetID()) return true; else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == group_member->GetID()) return true; else return false; }; for (auto &e : mob_list) { auto mob = e.second; auto it = casted_on.find(mob->GetID()); bool good = verify_solo(mob); if (it != casted_on.end()) { // make sure still valid if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { delayed_remove.insert(mob->GetID()); } } else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { casted_on.insert(mob->GetID()); if (is_buff) SpellFinished(spell_id, mob); } } } for (auto &e : delayed_remove) { auto mob = entity_list.GetMob(e); if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); casted_on.erase(e); } // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) cast_timer.Start(); if (!cast_timer.Enabled() || !cast_timer.Check()) return; // some auras have to recast (DRU for example, non-buff too) for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); if (mob != nullptr) SpellFinished(spell_id, mob); } }
BOOL vMover::SubPK( vMover* pAttacker, int nReflect ) { if(!IsPlayer()) return TRUE; if( !g_gameEvent.GetState(EVE_PK) || GetWorld()->IsArena() || !g_pEventArena.IsActive() || ( GetWorld()->GetId() == WI_WORLD_GUILDWAR || pAttacker->GetWorld()->GetId() == WI_WORLD_GUILDWAR ) && g_pGuildCombatMng.GetState() != vGuildCombatManager::CLOSE_STATE || g_pGuildCombat1to1Mng.IsPossibleUser((vUser*)this) && g_pGuildCombat1to1Mng.IsPossibleUser((vUser*)pAttacker) ) return TRUE; if(g_gameEvent.GetState( EVE_PKCOST )) { if( IsPlayer() && IsChaotic()) { CHAO_PROPENSITY Propensity = g_prj.GetPropensityPenalty( GetPKPropensity() ); int nInvenDrop = xRandom(Propensity.nInvenDorpMin, Propensity.nInvenDorpMax); for(int i = 0; i < nInvenDrop; i++ ) { if( DoDropItemRandom( TRUE, pAttacker ) == 0 ) break; } int nEquipDrop = xRandom( Propensity.nEquipDorpMin, Propensity.nEquipDorpMax + 1 ); for( int i = 0; i < nEquipDrop; ++i ) { if( DoDropItemRandom( FALSE, pAttacker, TRUE ) == 0 ) break; } } if( pAttacker->IsPlayer() ) { m_idMurderer = pAttacker->GetId(); vWorld* pWorld = GetWorld(); if( pWorld ) { char szFormat[256]; strcpy_s( szFormat, pAttacker->GetName() ); // strcat_s( szFormat, g_prj.GetText( TID_PK_REWARDNOTICE ) ); // ((vUser*)this)->AddDefinedText( TID_GAME_PKDEAD ); g_pClient.SendWCWantedReward( GetId(), pAttacker->GetId(), szFormat, pWorld->GetId(), GetPos(), GetLayer() ); } } } Log( LOG_SUCCESS, ">> mue %d", m_idMurderer ); if( !pAttacker->IsPlayer() || IsNPC() ) return TRUE; bool bAdd = false; if( pAttacker->IsChaotic() ) { if( !IsChaotic() ) bAdd = true; } else { if( !(IsChaotic() || IsPKPink()) ) bAdd = true; } // if( bAdd && nReflect == 0 && pAttacker != this ) // { // if( IsPlayer() && pAttacker->IsPlayer() ) // { // if( !pAttacker->IsChaotic() ) // ((vUser*)pAttacker)->AddPlaySound( SND_PC_CHAOS ); // // char szBuff[64] = {0}; // // sprintf_s( szBuff, g_prj.GetText(TID_GAME_PK_DEFENDER), pAttacker->GetName() ); // ((vUser*)this)->AddText( szBuff ); // sprintf_s( szBuff, g_prj.GetText(TID_GAME_PK_ATTACKER) , GetName() ); // ((vUser*)pAttacker)->AddText( szBuff ); // } // // pAttacker->SetPKValue( pAttacker->GetPKValue() + 1 ); // ((vUser*)pAttacker)->AddPKValue(); // pAttacker->SetPKPropensity( pAttacker->GetPKPropensity() + NextPKPropensity( pAttacker->GetPKValue() ) ); // g_UserMng.AddPKPropensity( pAttacker ); // g_dpDBClient.SendLogPkPvp( pAttacker, this, 0, 'P' ); // ((vUser*)pAttacker)->SetHonorAdd(HS_PK_COUNT,HI_COUNT_CHECK); // } return 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(); }
int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (target == nullptr) target = this; if (IsNPC()) value += value*CastToNPC()->GetSpellFocusHeal()/100; int32 value_BaseEffect = 0; int16 chance = 0; int8 modifier = 1; bool Critical = false; value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); value = value_BaseEffect; value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100); // Instant Heals if(spells[spell_id].buffduration < 1) { chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); if (spellbonuses.CriticalHealDecay) chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); if(chance && (zone->random.Roll(chance))) { Critical = true; modifier = 2; //At present time no critical heal amount modifier SPA exists. } value *= modifier; value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier; value += GetFocusEffect(focusFcHealAmt, spell_id); value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; value += value*target->GetHealRate(spell_id, this)/100; if (IsNPC() && CastToNPC()->GetHealScale()) value = int(static_cast<float>(value) * CastToNPC()->GetHealScale() / 100.0f); if (Critical) { entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_HEAL, GetName(), itoa(value)); if (IsClient()) Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value)); } return value; } //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] else { chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); if (spellbonuses.CriticalRegenDecay) chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); if(chance && zone->random.Roll(chance)) value *= 2; } if (IsNPC() && CastToNPC()->GetHealScale()) value = int(static_cast<float>(value) * CastToNPC()->GetHealScale() / 100.0f); return value; }
//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); }
Mob* EntityList::AICheckCloseAggro(Mob* sender, float iAggroRange, float iAssistRange) { if (!sender || !sender->IsNPC()) return(nullptr); #ifdef REVERSE_AGGRO //with reverse aggro, npc->client is checked elsewhere, no need to check again auto it = npc_list.begin(); while (it != npc_list.end()) { #else auto it = mob_list.begin(); while (it != mob_list.end()) { #endif Mob *mob = it->second; if (sender->CheckWillAggro(mob)) return mob; ++it; } //LogFile->write(EQEMuLog::Debug, "Check aggro for %s no target.", sender->GetName()); return nullptr; } int EntityList::GetHatedCount(Mob *attacker, Mob *exclude) { // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker if (!attacker) return 0; int Count = 0; for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { NPC *mob = it->second; if (!mob || (mob == exclude)) continue; if (!mob->IsEngaged()) continue; if (mob->IsFeared() || mob->IsMezzed()) continue; if (attacker->GetLevelCon(mob->GetLevel()) == CON_GREEN) continue; if (!mob->CheckAggro(attacker)) continue; float AggroRange = mob->GetAggroRange(); // Square it because we will be using DistNoRoot AggroRange *= AggroRange; if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange) continue; Count++; } return Count; } void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { if(!sender || !attacker) return; if (sender->GetPrimaryFaction() == 0 ) return; // well, if we dont have a faction set, we're gonna be indiff to everybody if (sender->HasAssistAggro()) return; for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { NPC *mob = it->second; if (!mob) continue; if (mob->CheckAggro(attacker)) continue; if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap)) break; float r = mob->GetAssistRange(); r = r * r; if ( mob != sender && mob != attacker // && !mob->IsCorpse() // && mob->IsAIControlled() && mob->GetPrimaryFaction() != 0 && DistanceSquared(mob->GetPosition(), sender->GetPosition()) <= r && !mob->IsEngaged() && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) // If we're a pet we don't react to any calls for help if our owner is a client ) { //if they are in range, make sure we are not green... //then jump in if they are our friend if(attacker->GetLevelCon(mob->GetLevel()) != CON_GREEN) { bool useprimfaction = false; if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) { const NPCFactionList *cf = database.GetNPCFactionEntry(mob->GetNPCFactionID()); if(cf){ if(cf->assistprimaryfaction != 0) useprimfaction = true; } } if(useprimfaction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE ) { //attacking someone on same faction, or a friend //Father Nitwit: make sure we can see them. if(mob->CheckLosFN(sender)) { #if (EQDEBUG>=5) Log.Out(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", sender->GetName(), attacker->GetName(), mob->GetName(), attacker->GetName(), DistanceSquared(mob->GetPosition(), sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ())); #endif mob->AddToHateList(attacker, 25, 0, false); sender->AddAssistCap(); } } } } } } /* returns false if attack should not be allowed I try to list every type of conflict that's possible here, so it's easy to see how the decision is made. Yea, it could be condensed and made faster, but I'm doing it this way to make it readable and easy to modify */ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) { Mob *mob1, *mob2, *tempmob; Client *c1, *c2, *becomenpc; // NPC *npc1, *npc2; int reverse; if(!zone->CanDoCombat()) return false; // some special cases if(!target) return false; if(this == target) // you can attack yourself return true; if(target->GetSpecialAbility(NO_HARM_FROM_CLIENT)){ return false; } // can't damage own pet (applies to everthing) Mob *target_owner = target->GetOwner(); Mob *our_owner = GetOwner(); if(target_owner && target_owner == this) return false; else if(our_owner && our_owner == target) return false; // invalidate for swarm pets for later on if their owner is a corpse if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner && our_owner->IsCorpse() && !our_owner->IsPlayerCorpse()) our_owner = nullptr; if (target->IsNPC() && target->CastToNPC()->GetSwarmInfo() && target_owner && target_owner->IsCorpse() && !target_owner->IsPlayerCorpse()) target_owner = nullptr; //cannot hurt untargetable mobs bodyType bt = target->GetBodyType(); if(bt == BT_NoTarget || bt == BT_NoTarget2) { if (RuleB(Pets, UnTargetableSwarmPet)) { if (target->IsNPC()) { if (!target->CastToNPC()->GetSwarmOwner()) { return(false); } } else { return(false); } } else { return(false); } } if(!isSpellAttack) { if(GetClass() == LDON_TREASURE) { return false; } } // the format here is a matrix of mob type vs mob type. // redundant ones are omitted and the reverse is tried if it falls through. // first figure out if we're pets. we always look at the master's flags. // no need to compare pets to anything mob1 = our_owner ? our_owner : this; mob2 = target_owner ? target_owner : target; reverse = 0; do { if(_CLIENT(mob1)) { if(_CLIENT(mob2)) // client vs client { c1 = mob1->CastToClient(); c2 = mob2->CastToClient(); if // if both are pvp they can fight ( c1->GetPVP() && c2->GetPVP() ) return true; else if // if they're dueling they can go at it ( c1->IsDueling() && c2->IsDueling() && c1->GetDuelTarget() == c2->GetID() && c2->GetDuelTarget() == c1->GetID() ) return true; else return false; } else if(_NPC(mob2)) // client vs npc { return true; } else if(_BECOMENPC(mob2)) // client vs becomenpc { c1 = mob1->CastToClient(); becomenpc = mob2->CastToClient(); if(c1->GetLevel() > becomenpc->GetBecomeNPCLevel()) return false; else return true; } else if(_CLIENTCORPSE(mob2)) // client vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // client vs npc corpse { return false; } } else if(_NPC(mob1)) { if(_NPC(mob2)) // npc vs npc { /* this says that an NPC can NEVER attack a faction ally... this is stupid... somebody else should check this rule if they want to enforce it, this just says 'can they possibly fight based on their type', in which case, the answer is yes. */ /* npc1 = mob1->CastToNPC(); npc2 = mob2->CastToNPC(); if ( npc1->GetPrimaryFaction() != 0 && npc2->GetPrimaryFaction() != 0 && ( npc1->GetPrimaryFaction() == npc2->GetPrimaryFaction() || npc1->IsFactionListAlly(npc2->GetPrimaryFaction()) ) ) return false; else */ return true; } else if(_BECOMENPC(mob2)) // npc vs becomenpc { return true; } else if(_CLIENTCORPSE(mob2)) // npc vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // npc vs npc corpse { return false; } } else if(_BECOMENPC(mob1)) { if(_BECOMENPC(mob2)) // becomenpc vs becomenpc { return true; } else if(_CLIENTCORPSE(mob2)) // becomenpc vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // becomenpc vs npc corpse { return false; } } else if(_CLIENTCORPSE(mob1)) { if(_CLIENTCORPSE(mob2)) // client corpse vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // client corpse vs npc corpse { return false; } } else if(_NPCCORPSE(mob1)) { if(_NPCCORPSE(mob2)) // npc corpse vs npc corpse { return false; } } #ifdef BOTS bool HasRuleDefined = false; bool IsBotAttackAllowed = false; IsBotAttackAllowed = Bot::IsBotAttackAllowed(mob1, mob2, HasRuleDefined); if(HasRuleDefined) return IsBotAttackAllowed; #endif //BOTS // we fell through, now we swap the 2 mobs and run through again once more tempmob = mob1; mob1 = mob2; mob2 = tempmob; } while( reverse++ == 0 ); Log.Out(Logs::General, Logs::None, "Mob::IsAttackAllowed: don't have a rule for this - %s vs %s\n", this->GetName(), target->GetName()); return false; }
bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) { Mob *mob1 = nullptr, *mob2 = nullptr, *tempmob = nullptr; Client *c1 = nullptr, *c2 = nullptr, *becomenpc = nullptr; // NPC *npc1, *npc2; int reverse; if(!zone->CanDoCombat()) return false; // some special cases if(!target) return false; if(this == target) // you can attack yourself return true; if(target->GetSpecialAbility(NO_HARM_FROM_CLIENT)){ return false; } // can't damage own pet (applies to everthing) Mob *target_owner = target->GetOwner(); Mob *our_owner = GetOwner(); if(target_owner && target_owner == this) return false; else if(our_owner && our_owner == target) return false; // invalidate for swarm pets for later on if their owner is a corpse if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner && our_owner->IsCorpse() && !our_owner->IsPlayerCorpse()) our_owner = nullptr; if (target->IsNPC() && target->CastToNPC()->GetSwarmInfo() && target_owner && target_owner->IsCorpse() && !target_owner->IsPlayerCorpse()) target_owner = nullptr; //cannot hurt untargetable mobs bodyType bt = target->GetBodyType(); if(bt == BT_NoTarget || bt == BT_NoTarget2) { if (RuleB(Pets, UnTargetableSwarmPet)) { if (target->IsNPC()) { if (!target->CastToNPC()->GetSwarmOwner()) { return(false); } } else { return(false); } } else { return(false); } } if(!isSpellAttack) { if(GetClass() == LDON_TREASURE) { return false; } } // the format here is a matrix of mob type vs mob type. // redundant ones are omitted and the reverse is tried if it falls through. // first figure out if we're pets. we always look at the master's flags. // no need to compare pets to anything mob1 = our_owner ? our_owner : this; mob2 = target_owner ? target_owner : target; reverse = 0; do { if(_CLIENT(mob1)) { if(_CLIENT(mob2)) // client vs client { c1 = mob1->CastToClient(); c2 = mob2->CastToClient(); if // if both are pvp they can fight ( c1->GetPVP() && c2->GetPVP() ) return true; else if // if they're dueling they can go at it ( c1->IsDueling() && c2->IsDueling() && c1->GetDuelTarget() == c2->GetID() && c2->GetDuelTarget() == c1->GetID() ) return true; else return false; } else if(_NPC(mob2)) // client vs npc { return true; } else if(_BECOMENPC(mob2)) // client vs becomenpc { c1 = mob1->CastToClient(); becomenpc = mob2->CastToClient(); if(c1->GetLevel() > becomenpc->GetBecomeNPCLevel()) return false; else return true; } else if(_CLIENTCORPSE(mob2)) // client vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // client vs npc corpse { return false; } } else if(_NPC(mob1)) { if(_NPC(mob2)) // npc vs npc { /* this says that an NPC can NEVER attack a faction ally... this is stupid... somebody else should check this rule if they want to enforce it, this just says 'can they possibly fight based on their type', in which case, the answer is yes. */ /* npc1 = mob1->CastToNPC(); npc2 = mob2->CastToNPC(); if ( npc1->GetPrimaryFaction() != 0 && npc2->GetPrimaryFaction() != 0 && ( npc1->GetPrimaryFaction() == npc2->GetPrimaryFaction() || npc1->IsFactionListAlly(npc2->GetPrimaryFaction()) ) ) return false; else */ return true; } else if(_BECOMENPC(mob2)) // npc vs becomenpc { return true; } else if(_CLIENTCORPSE(mob2)) // npc vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // npc vs npc corpse { return false; } } else if(_BECOMENPC(mob1)) { if(_BECOMENPC(mob2)) // becomenpc vs becomenpc { return true; } else if(_CLIENTCORPSE(mob2)) // becomenpc vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // becomenpc vs npc corpse { return false; } } else if(_CLIENTCORPSE(mob1)) { if(_CLIENTCORPSE(mob2)) // client corpse vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // client corpse vs npc corpse { return false; } } else if(_NPCCORPSE(mob1)) { if(_NPCCORPSE(mob2)) // npc corpse vs npc corpse { return false; } } #ifdef BOTS bool HasRuleDefined = false; bool IsBotAttackAllowed = false; IsBotAttackAllowed = Bot::IsBotAttackAllowed(mob1, mob2, HasRuleDefined); if(HasRuleDefined) return IsBotAttackAllowed; #endif //BOTS // we fell through, now we swap the 2 mobs and run through again once more tempmob = mob1; mob1 = mob2; mob2 = tempmob; } while( reverse++ == 0 ); Log(Logs::General, Logs::None, "Mob::IsAttackAllowed: don't have a rule for this - %s vs %s\n", this->GetName(), target->GetName()); return false; }