bool PostgreSQLConnection::Execute(const char *sql) { if (!mPGconn) return false; uint32 _s = WorldTimer::getMSTime(); PGresult *res = PQexec(mPGconn, sql); if (PQresultStatus(res) != PGRES_COMMAND_OK) { sLog.outErrorDb( "SQL: %s", sql ); sLog.outErrorDb( "SQL %s", PQerrorMessage(mPGconn) ); return false; } else { DEBUG_FILTER_LOG(LOG_FILTER_SQL_TEXT, "[%u ms] SQL: %s", WorldTimer::getMSTimeDiff(_s,WorldTimer::getMSTime()), sql ); } PQclear(res); return true; }
void MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode) { if (m_owner->GetTypeId() == TYPEID_PLAYER) { if (path < sTaxiPathNodesByPath.size()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "%s taxi to (Path %u node %u)", m_owner->GetGuidStr().c_str(), path, pathnode); FlightPathMovementGenerator* mgen = new FlightPathMovementGenerator(sTaxiPathNodesByPath[path], pathnode); Mutate(mgen); } else { sLog.outError("%s attempt taxi to (nonexistent Path %u node %u)", m_owner->GetGuidStr().c_str(), path, pathnode); } } else { sLog.outError("%s attempt taxi to (Path %u node %u)", m_owner->GetGuidStr().c_str(), path, pathnode); } }
void MotionMaster::MoveWaypoint(uint32 pathId /*=0*/, uint32 source /*=0==PATH_NO_PATH*/, uint32 initialDelay /*=0*/, uint32 overwriteEntry /*=0*/) { if (m_owner->GetTypeId() == TYPEID_UNIT) { if (GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) { sLog.outError("%s attempt to MoveWaypoint() but is already using waypoint", m_owner->GetGuidStr().c_str()); return; } Creature* creature = (Creature*)m_owner; DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "%s start MoveWaypoint()", m_owner->GetGuidStr().c_str()); WaypointMovementGenerator<Creature>* newWPMMgen = new WaypointMovementGenerator<Creature>(*creature); Mutate(newWPMMgen); newWPMMgen->InitializeWaypointPath(*creature, pathId, (WaypointPathOrigin)source, initialDelay, overwriteEntry); } else { sLog.outError("Non-creature %s attempt to MoveWaypoint()", m_owner->GetGuidStr().c_str()); } }
void GuardAI::EnterEvadeMode() { if (!m_creature->isAlive()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking because he's dead [guid=%u]", m_creature->GetGUIDLow()); m_creature->StopMoving(); m_creature->GetMotionMaster()->MoveIdle(); i_state = STATE_NORMAL; i_victimGuid.Clear(); m_creature->CombatStop(true); m_creature->DeleteThreatList(); return; } Unit* victim = m_creature->GetMap()->GetUnit(i_victimGuid); if (!victim) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, no victim [guid=%u]", m_creature->GetGUIDLow()); } else if (!victim->isAlive()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is dead [guid=%u]", m_creature->GetGUIDLow()); } else if (victim->HasStealthAura()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is in stealth [guid=%u]", m_creature->GetGUIDLow()); } else if (victim->IsTaxiFlying()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is in flight [guid=%u]", m_creature->GetGUIDLow()); } else { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim out run him [guid=%u]", m_creature->GetGUIDLow()); } m_creature->RemoveAllAuras(); m_creature->DeleteThreatList(); i_victimGuid.Clear(); m_creature->CombatStop(true); i_state = STATE_NORMAL; }
bool MySQLConnection::Execute(const char* sql) { if (!mMysql) return false; { uint32 _s = WorldTimer::getMSTime(); if(mysql_query(mMysql, sql)) { sLog.outErrorDb("SQL: %s", sql); sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql)); return false; } else { DEBUG_FILTER_LOG(LOG_FILTER_SQL_TEXT, "[%u ms] SQL: %s", WorldTimer::getMSTimeDiff(_s,WorldTimer::getMSTime()), sql ); } // end guarded block } return true; }
void WorldSession::HandleCalendarComplain(WorldPacket& recv_data) { ObjectGuid guid = _player->GetObjectGuid(); DEBUG_LOG("WORLD: CMSG_CALENDAR_COMPLAIN [%u]", guid.GetCounter()); ObjectGuid badGuyGuid; ObjectGuid eventId; ObjectGuid inviteId; recv_data >> badGuyGuid; recv_data >> eventId >> inviteId; DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "WorldSession::HandleCalendarComplain %u EventId %u, Invitee %u id: %u", guid.GetCounter(), eventId.GetCounter(), badGuyGuid.GetCounter(), inviteId.GetCounter()); // Remove the invite if (sCalendarMgr.RemoveInvite(eventId, inviteId, guid)) { WorldPacket data(SMSG_COMPLAIN_RESULT, 1 + 1); data << uint8(0); data << uint8(0); // show complain saved. We can send 0x0C to show windows with ok button SendPacket(&data); } }
void WorldSession::HandleCalendarComplain(WorldPacket& recv_data) { ObjectGuid guid = _player->GetObjectGuid(); DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_COMPLAIN [%s]", guid.GetString().c_str()); ObjectGuid badGuyGuid; uint64 eventId; uint64 inviteId; recv_data >> badGuyGuid; recv_data >> eventId >> inviteId; DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "], BadGuyGuid ([%s] inviteId: [" UI64FMTD "])", eventId, badGuyGuid.GetString().c_str(), inviteId); // Remove the invite if (sCalendarMgr.RemoveInvite(eventId, inviteId, guid)) { WorldPacket data(SMSG_COMPLAIN_RESULT, 1 + 1); data << uint8(0); data << uint8(0); // show complain saved. We can send 0x0C to show windows with ok button SendPacket(&data); } }
bool MySQLConnection::_Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD** pFields, uint64* pRowCount, uint32* pFieldCount) { if (!mMysql) return 0; uint32 _s = WorldTimer::getMSTime(); if (mysql_query(mMysql, sql)) { unsigned int err = mysql_errno(mMysql); if (err == 2006 || err == 2003 || err == 2013) mysql_ping(mMysql); sLog.outErrorDb("SQL: %s", sql); sLog.outErrorDb("query ERROR: %s", mysql_error(mMysql)); return false; } else { DEBUG_FILTER_LOG(LOG_FILTER_SQL_TEXT, "[%u ms] SQL: %s", WorldTimer::getMSTimeDiff(_s, WorldTimer::getMSTime()), sql); } *pResult = mysql_store_result(mMysql); *pRowCount = mysql_affected_rows(mMysql); *pFieldCount = mysql_field_count(mMysql); if (!*pResult) return false; if (!*pRowCount) { mysql_free_result(*pResult); return false; } *pFields = mysql_fetch_fields(*pResult); return true; }
bool FlightPathMovementGenerator::Update(Player &player, const uint32 &diff) { if (MovementInProgress()) { Traveller<Player> traveller(player); if( i_destinationHolder.UpdateTraveller(traveller, diff, false) ) { if (!IsActive(player)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost i_destinationHolder.ResetUpdate(FLIGHT_TRAVEL_UPDATE); if (i_destinationHolder.HasArrived()) { uint32 curMap = (*i_path)[i_currentNode].mapid; ++i_currentNode; if (MovementInProgress()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "loading node %u for player %s", i_currentNode, player.GetName()); if ((*i_path)[i_currentNode].mapid == curMap) { // do not send movement, it was sent already i_destinationHolder.SetDestination(traveller, (*i_path)[i_currentNode].x, (*i_path)[i_currentNode].y, (*i_path)[i_currentNode].z, false); } return true; } //else HasArrived() } else return true; } else return true; } // we have arrived at the end of the path return false; }
////////////////// PathInfo ////////////////// PathInfo::PathInfo(const Unit* owner, const float destX, const float destY, const float destZ, bool useStraightPath, bool forceDest) : m_polyLength(0), m_type(PATHFIND_BLANK), m_useStraightPath(useStraightPath), m_forceDestination(forceDest), m_sourceUnit(owner), m_navMesh(NULL), m_navMeshQuery(NULL) { PathNode endPoint(destX, destY, destZ); setEndPosition(endPoint); float x,y,z; m_sourceUnit->GetPosition(x, y, z); PathNode startPoint(x, y, z); setStartPosition(startPoint); DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathInfo::PathInfo for %u \n", m_sourceUnit->GetGUID()); uint32 mapId = m_sourceUnit->GetMapId(); if (MMAP::MMapFactory::IsPathfindingEnabled(mapId)) { MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager(); m_navMesh = mmap->GetNavMesh(mapId); m_navMeshQuery = mmap->GetNavMeshQuery(mapId, m_sourceUnit->GetInstanceId()); } createFilter(); if (m_navMesh && m_navMeshQuery && HaveTiles(endPoint) && !m_sourceUnit->hasUnitState(UNIT_STAT_IGNORE_PATHFINDING)) { BuildPolyPath(startPoint, endPoint); } else { BuildShortcut(); m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); } }
void PathInfo::BuildPolyPath(PathNode startPos, PathNode endPos) { // *** getting start/end poly logic *** float distToStartPoly, distToEndPoly; float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x}; float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x}; dtPolyRef startPoly = getPolyByLocation(startPoint, &distToStartPoly); dtPolyRef endPoly = getPolyByLocation(endPoint, &distToEndPoly); // we have a hole in our mesh // make shortcut path and mark it as NOPATH ( with flying exception ) // its up to caller how he will use this info if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n"); BuildShortcut(); m_type = (m_sourceUnit->GetTypeId() == TYPEID_UNIT && ((Creature*)m_sourceUnit)->CanFly()) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; return; } // we may need a better number here bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f); if (farFromPoly) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly); bool buildShotrcut = false; if (m_sourceUnit->GetTypeId() == TYPEID_UNIT) { Creature* owner = (Creature*)m_sourceUnit; PathNode p = (distToStartPoly > 7.0f) ? startPos : endPos; if (m_sourceUnit->GetTerrain()->IsUnderWater(p.x, p.y, p.z)) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: underWater case\n"); if (owner->CanSwim()) buildShotrcut = true; } else { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: flying case\n"); if (owner->CanFly()) buildShotrcut = true; } } if (buildShotrcut) { BuildShortcut(); m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); return; } else { float closestPoint[VERTEX_SIZE]; // we may want to use closestPointOnPolyBoundary instead if (DT_SUCCESS == m_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint)) { dtVcopy(endPoint, closestPoint); setActualEndPosition(PathNode(endPoint[2],endPoint[0],endPoint[1])); } m_type = PATHFIND_INCOMPLETE; } } // *** poly path generating logic *** // start and end are on same polygon // just need to move in straight line if (startPoly == endPoly) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPoly == endPoly)\n"); BuildShortcut(); m_pathPolyRefs[0] = startPoly; m_polyLength = 1; m_type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL; DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: path type %d\n", m_type); return; } // look for startPoly/endPoly in current path // TODO: we can merge it with getPathPolyByPosition() loop bool startPolyFound = false; bool endPolyFound = false; uint32 pathStartIndex, pathEndIndex; if (m_polyLength) { for (pathStartIndex = 0; pathStartIndex < m_polyLength; ++pathStartIndex) { // here to carch few bugs MANGOS_ASSERT(m_pathPolyRefs[pathStartIndex] != INVALID_POLYREF); if (m_pathPolyRefs[pathStartIndex] == startPoly) { startPolyFound = true; break; } } for (pathEndIndex = m_polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex) if (m_pathPolyRefs[pathEndIndex] == endPoly) { endPolyFound = true; break; } } if (startPolyFound && endPolyFound) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n"); // we moved along the path and the target did not move out of our old poly-path // our path is a simple subpath case, we have all the data we need // just "cut" it out m_polyLength = pathEndIndex - pathStartIndex + 1; memmove(m_pathPolyRefs, m_pathPolyRefs+pathStartIndex, m_polyLength*sizeof(dtPolyRef)); } else if (startPolyFound && !endPolyFound) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n"); // we are moving on the old path but target moved out // so we have atleast part of poly-path ready m_polyLength -= pathStartIndex; // try to adjust the suffix of the path instead of recalculating entire length // at given interval the target cannot get too far from its last location // thus we have less poly to cover // sub-path of optimal path is optimal // take ~80% of the original length // TODO : play with the values here uint32 prefixPolyLength = uint32(m_polyLength*0.8f + 0.5f); memmove(m_pathPolyRefs, m_pathPolyRefs+pathStartIndex, prefixPolyLength*sizeof(dtPolyRef)); dtPolyRef suffixStartPoly = m_pathPolyRefs[prefixPolyLength-1]; // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data float suffixEndPoint[VERTEX_SIZE]; if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)) { // suffixStartPoly is invalid somehow, or the navmesh is broken => error state sLog.outError("%u's Path Build failed: invalid polyRef in path", m_sourceUnit->GetGUID()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } // generate suffix uint32 suffixPolyLength = 0; dtStatus dtResult = m_navMeshQuery->findPath( suffixStartPoly, // start polygon endPoly, // end polygon suffixEndPoint, // start position endPoint, // end position &m_filter, // polygon search filter m_pathPolyRefs + prefixPolyLength - 1, // [out] path (int*)&suffixPolyLength, MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path if (!suffixPolyLength || dtResult != DT_SUCCESS) { // this is probably an error state, but we'll leave it // and hopefully recover on the next Update // we still need to copy our preffix sLog.outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUID()); } DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n",m_polyLength, prefixPolyLength, suffixPolyLength); // new path = prefix + suffix - overlap m_polyLength = prefixPolyLength + suffixPolyLength - 1; } else { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n"); // either we have no path at all -> first run // or something went really wrong -> we aren't moving along the path to the target // just generate new path // free and invalidate old path data clear(); dtStatus dtResult = m_navMeshQuery->findPath( startPoly, // start polygon endPoly, // end polygon startPoint, // start position endPoint, // end position &m_filter, // polygon search filter m_pathPolyRefs, // [out] path (int*)&m_polyLength, MAX_PATH_LENGTH); // max number of polygons in output path if (!m_polyLength || dtResult != DT_SUCCESS) { // only happens if we passed bad data to findPath(), or navmesh is messed up sLog.outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUID()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } } // by now we know what type of path we can get if (m_pathPolyRefs[m_polyLength - 1] == endPoly && !(m_type & PATHFIND_INCOMPLETE)) m_type = PATHFIND_NORMAL; else m_type = PATHFIND_INCOMPLETE; // generate the point-path out of our up-to-date poly-path BuildPointPath(startPoint, endPoint); }
/// Calculate the new weather bool Weather::ReGenerate() { if (!m_weatherChances) { m_type = WEATHER_TYPE_FINE; m_grade = 0.0f; return false; } /// Weather statistics: ///- 30% - no change ///- 30% - weather gets better (if not fine) or change weather type ///- 30% - weather worsens (if not fine) ///- 10% - radical change (if not fine) uint32 u = urand(0, 99); if (u < 30) return false; // remember old values WeatherType old_type = m_type; float old_grade = m_grade; //78 days between January 1st and March 20nd; 365/4=91 days by season // season source http://aa.usno.navy.mil/data/docs/EarthSeasons.html time_t gtime = sWorld.GetGameTime(); struct tm * ltime = localtime(>ime); uint32 season = ((ltime->tm_yday - 78 + 365)/91)%4; static char const* seasonName[WEATHER_SEASONS] = { "spring", "summer", "fall", "winter" }; DEBUG_FILTER_LOG(LOG_FILTER_WEATHER, "Generating a change in %s weather for zone %u.", seasonName[season], m_zone); if ((u < 60) && (m_grade < 0.33333334f)) // Get fair { m_type = WEATHER_TYPE_FINE; m_grade = 0.0f; } if ((u < 60) && (m_type != WEATHER_TYPE_FINE)) // Get better { m_grade -= 0.33333334f; return true; } if ((u < 90) && (m_type != WEATHER_TYPE_FINE)) // Get worse { m_grade += 0.33333334f; return true; } if (m_type != WEATHER_TYPE_FINE) { /// Radical change: ///- if light -> heavy ///- if medium -> change weather type ///- if heavy -> 50% light, 50% change weather type if (m_grade < 0.33333334f) { m_grade = 0.9999f; // go nuts return true; } else { if (m_grade > 0.6666667f) { // Severe change, but how severe? uint32 rnd = urand(0,99); if (rnd < 50) { m_grade -= 0.6666667f; return true; } } m_type = WEATHER_TYPE_FINE; // clear up m_grade = 0; } } // At this point, only weather that isn't doing anything remains but that have weather data uint32 chance1 = m_weatherChances->data[season].rainChance; uint32 chance2 = chance1+ m_weatherChances->data[season].snowChance; uint32 chance3 = chance2+ m_weatherChances->data[season].stormChance; uint32 rnd = urand(0, 99); if (rnd <= chance1) m_type = WEATHER_TYPE_RAIN; else if (rnd <= chance2) m_type = WEATHER_TYPE_SNOW; else if (rnd <= chance3) m_type = WEATHER_TYPE_STORM; else m_type = WEATHER_TYPE_FINE; /// New weather statistics (if not fine): ///- 85% light ///- 7% medium ///- 7% heavy /// If fine 100% sun (no fog) if (m_type == WEATHER_TYPE_FINE) { m_grade = 0.0f; } else if (u < 90) { m_grade = rand_norm_f() * 0.3333f; } else { // Severe change, but how severe? rnd = urand(0, 99); if (rnd < 50) m_grade = rand_norm_f() * 0.3333f + 0.3334f; else m_grade = rand_norm_f() * 0.3333f + 0.6667f; } // return true only in case weather changes return m_type != old_type || m_grade != old_grade; }
void WaypointMovementGenerator<Creature>::OnArrived(Creature& creature) { if (!i_path || i_path->empty()) { return; } m_lastReachedWaypoint = i_currentNode; if (m_isArrivalDone) { return; } creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); m_isArrivalDone = true; WaypointPath::const_iterator currPoint = i_path->find(i_currentNode); MANGOS_ASSERT(currPoint != i_path->end()); WaypointNode const& node = currPoint->second; if (node.script_id) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature movement start script %u at point %u for %s.", node.script_id, i_currentNode, creature.GetGuidStr().c_str()); creature.GetMap()->ScriptsStart(DBS_ON_CREATURE_MOVEMENT, node.script_id, &creature, &creature); } // We have reached the destination and can process behavior if (WaypointBehavior* behavior = node.behavior) { if (behavior->emote != 0) { creature.HandleEmote(behavior->emote); } if (behavior->spell != 0) { creature.CastSpell(&creature, behavior->spell, false); } if (behavior->model1 != 0) { creature.SetDisplayId(behavior->model1); } if (behavior->textid[0]) { int32 textId = behavior->textid[0]; // Not only one text is set if (behavior->textid[1]) { // Select one from max 5 texts (0 and 1 already checked) int i = 2; for (; i < MAX_WAYPOINT_TEXT; ++i) { if (!behavior->textid[i]) { break; } } textId = behavior->textid[urand(0, i - 1)]; } if (MangosStringLocale const* textData = sObjectMgr.GetMangosStringLocale(textId)) { creature.MonsterText(textData, NULL); } else { sLog.outErrorDb("%s reached waypoint %u, attempted to do text %i, but required text-data could not be found", creature.GetGuidStr().c_str(), i_currentNode, textId); } } } // Inform script if (creature.AI()) { uint32 type = WAYPOINT_MOTION_TYPE; if (m_PathOrigin == PATH_FROM_EXTERNAL && m_pathId > 0) type = EXTERNAL_WAYPOINT_MOVE + m_pathId; creature.AI()->MovementInform(type, i_currentNode); } // Wait delay ms Stop(node.delay); }
void WorldSession::HandleCalendarEventInvite(WorldPacket& recv_data) { ObjectGuid playerGuid = _player->GetObjectGuid(); DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_EVENT_INVITE [%s]", playerGuid.GetString().c_str()); uint64 eventId; // TODO it seem its not inviteID but event->CreatorGuid uint64 inviteId; std::string name; bool isPreInvite; bool isGuildEvent; ObjectGuid inviteeGuid; uint32 inviteeTeam = 0; uint32 inviteeGuildId = 0; bool isIgnored = false; recv_data >> eventId >> inviteId >> name >> isPreInvite >> isGuildEvent; if (Player* player = sObjectAccessor.FindPlayerByName(name.c_str())) { // Invitee is online inviteeGuid = player->GetObjectGuid(); inviteeTeam = player->GetTeam(); inviteeGuildId = player->GetGuildId(); if (player->GetSocial()->HasIgnore(playerGuid)) isIgnored = true; } else { // Invitee offline, get data from database CharacterDatabase.escape_string(name); QueryResult* result = CharacterDatabase.PQuery("SELECT guid,race FROM characters WHERE name ='%s'", name.c_str()); if (result) { Field* fields = result->Fetch(); inviteeGuid = ObjectGuid(HIGHGUID_PLAYER, fields[0].GetUInt32()); inviteeTeam = Player::TeamForRace(fields[1].GetUInt8()); inviteeGuildId = Player::GetGuildIdFromDB(inviteeGuid); delete result; result = CharacterDatabase.PQuery("SELECT flags FROM character_social WHERE guid = %u AND friend = %u", inviteeGuid.GetCounter(), playerGuid.GetCounter()); if (result) { fields = result->Fetch(); if (fields[0].GetUInt8() & SOCIAL_FLAG_IGNORED) isIgnored = true; delete result; } } } if (inviteeGuid.IsEmpty()) { sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_PLAYER_NOT_FOUND); return; } if (isIgnored) { sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_IGNORING_YOU_S, name.c_str()); return; } if (_player->GetTeam() != inviteeTeam && !sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_CALENDAR)) { sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NOT_ALLIED); return; } if (!isPreInvite) { if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) { if (event->IsGuildEvent() && event->GuildId == inviteeGuildId) { // we can't invite guild members to guild events sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NO_GUILD_INVITES); return; } sCalendarMgr.AddInvite(event, playerGuid, inviteeGuid, CALENDAR_STATUS_INVITED, CALENDAR_RANK_PLAYER, "", time(nullptr)); } else sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); } else { if (isGuildEvent && inviteeGuildId == _player->GetGuildId()) { sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NO_GUILD_INVITES); return; } // create a temp invite to send it back to client CalendarInvite invite; invite.SenderGuid = playerGuid; invite.InviteeGuid = inviteeGuid; invite.Status = CALENDAR_STATUS_INVITED; invite.Rank = CALENDAR_RANK_PLAYER; invite.LastUpdateTime = time(nullptr); sCalendarMgr.SendCalendarEventInvite(&invite); DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "PREINVITE> sender[%s], Invitee[%s]", playerGuid.GetString().c_str(), inviteeGuid.GetString().c_str()); } }
void WorldSession::HandleCalendarUpdateEvent(WorldPacket& recv_data) { ObjectGuid guid = _player->GetObjectGuid(); DEBUG_LOG("WORLD: CMSG_CALENDAR_UPDATE_EVENT [%u]", guid.GetCounter()); time_t oldEventTime; ObjectGuid eventId; ObjectGuid inviteId; std::string title; std::string description; uint8 type; uint8 repetitionType; uint32 maxInvites; int32 dungeonId; uint32 eventPackedTime; uint32 UnknownPackedTime; uint32 flags; recv_data >> eventId >> inviteId >> title >> description >> type >> repetitionType >> maxInvites >> dungeonId; eventPackedTime = recv_data.ReadPackedTime(); UnknownPackedTime = recv_data.ReadPackedTime(); recv_data >> flags; DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "WorldSession::HandleCalendarUpdateEvent [%u] EventId [%u], InviteId [%u] Title %s, Description %s, type %u " "Repeatable %u, MaxInvites %u, Dungeon ID %d, Flags %u", guid.GetCounter(), uint32(eventId), uint32(inviteId), title.c_str(), description.c_str(), uint32(type), uint32(repetitionType), maxInvites, dungeonId, flags); if (CalendarEvent* calendarEvent = sCalendarMgr.GetEventById(eventId)) { if (guid != calendarEvent->CreatorGuid) { CalendarInvite* updaterInvite = calendarEvent->GetInviteByGuid(guid); if (updaterInvite == NULL) { sCalendarMgr.SendCalendarCommandResult(guid, CALENDAR_ERROR_NOT_INVITED); return ; } if (updaterInvite->Rank != CALENDAR_RANK_MODERATOR) { // remover have not enough right to change invite status sCalendarMgr.SendCalendarCommandResult(guid, CALENDAR_ERROR_PERMISSIONS); return; } } oldEventTime = calendarEvent->EventTime; calendarEvent->Type = CalendarEventType(type); calendarEvent->Flags = flags; calendarEvent->EventTime = eventPackedTime; calendarEvent->UnknownTime = UnknownPackedTime; calendarEvent->DungeonId = dungeonId; calendarEvent->Title = title; calendarEvent->Description = description; calendarEvent->RemoveFlag(CALENDAR_STATE_FLAG_SAVED); calendarEvent->AddFlag(CALENDAR_STATE_FLAG_UPDATED); sCalendarMgr.SendCalendarEventUpdateAlert(calendarEvent, oldEventTime); } else sCalendarMgr.SendCalendarCommandResult(guid, CALENDAR_ERROR_EVENT_INVALID); }
void MotionMaster::MoveDistract(uint32 timer) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "%s distracted (timer: %u)", m_owner->GetGuidStr().c_str(), timer); DistractMovementGenerator* mgen = new DistractMovementGenerator(timer); Mutate(mgen); }
void PathFinder::BuildPointPath(const float* startPoint, const float* endPoint) { float pathPoints[MAX_POINT_PATH_LENGTH * VERTEX_SIZE]; uint32 pointCount = 0; dtStatus dtResult = DT_FAILURE; if (m_useStraightPath) { dtResult = m_navMeshQuery->findStraightPath( startPoint, // start position endPoint, // end position m_pathPolyRefs, // current path m_polyLength, // lenth of current path pathPoints, // [out] path corner points NULL, // [out] flags NULL, // [out] shortened path (int*)&pointCount, m_pointPathLimit); // maximum number of points/polygons to use } else { dtResult = findSmoothPath( startPoint, // start position endPoint, // end position m_pathPolyRefs, // current path m_polyLength, // length of current path pathPoints, // [out] path corner points (int*)&pointCount, m_pointPathLimit); // maximum number of points } if (pointCount < 2 || dtStatusFailed(dtResult)) { // only happens if pass bad data to findStraightPath or navmesh is broken // single point paths can be generated here // TODO : check the exact cases DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::BuildPointPath FAILED! path sized %d returned\n", pointCount); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } m_pathPoints.resize(pointCount); for (uint32 i = 0; i < pointCount; ++i) { m_pathPoints[i] = Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]); } // first point is always our current location - we need the next one setActualEndPosition(m_pathPoints[pointCount - 1]); // force the given destination, if needed if (m_forceDestination && (!(m_type & PATHFIND_NORMAL) || !inRange(getEndPosition(), getActualEndPosition(), 1.0f, 1.0f))) { // we may want to keep partial subpath if (dist3DSqr(getActualEndPosition(), getEndPosition()) < 0.3f * dist3DSqr(getStartPosition(), getEndPosition())) { setActualEndPosition(getEndPosition()); m_pathPoints[m_pathPoints.size() - 1] = getEndPosition(); } else { setActualEndPosition(getEndPosition()); BuildShortcut(); } m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); } DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::BuildPointPath path type %d size %d poly-size %d\n", m_type, pointCount, m_polyLength); }
void PetAI::Reset() { m_primaryTargetGuid.Clear(); m_savedTargetGuid.Clear(); m_attackDistanceRecheckTimer.SetInterval(TIME_INTERVAL_LOOK); m_attackDistanceRecheckTimer.Reset(); m_updateAlliesTimer.SetInterval(ALLIES_UPDATE_TIME); m_updateAlliesTimer.Reset(); UpdateAllies(); for (uint8 i = PET_SPELL_PASSIVE; i < PET_SPELL_MAX; ++i) m_spellType[i].clear(); m_AIType = PET_AI_PASSIVE; m_attackDistance = 0.0f; float f_range = 0.0f; if (!m_creature->GetCharmInfo()) return; uint32 spellsSize = m_creature->IsPet() ? ((Pet*)m_creature)->GetPetAutoSpellSize() : m_creature->GetPetAutoSpellSize(); uint8 rangedDamageSpells = 0; uint8 meleeDamageSpells = 0; // classification for pet spells for (uint32 i = 0; i < spellsSize; ++i) { uint32 spellID = m_creature->IsPet() ? ((Pet*)m_creature)->GetPetAutoSpellOnPos(i) : m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID); if (!spellInfo) continue; if (IsPassiveSpell(spellInfo)) { m_spellType[PET_SPELL_PASSIVE].insert(spellID); continue; } if (IsNonCombatSpell(spellInfo)) { // Voidwalker Consume Shadows if (IsChanneledSpell(spellInfo)) m_spellType[PET_SPELL_HEAL].insert(spellID); else m_spellType[PET_SPELL_NONCOMBAT].insert(spellID); continue; } // need more correct define this type if (IsSpellReduceThreat(spellInfo) || IsChanneledSpell(spellInfo)) { m_spellType[PET_SPELL_DEFENCE].insert(spellID); continue; } // Voracious Appetite && Cannibalize && Carrion Feeder if (spellInfo->HasAttribute(SPELL_ATTR_ABILITY) && spellInfo->HasAttribute(SPELL_ATTR_EX2_ALLOW_DEAD_TARGET)) { m_spellType[PET_SPELL_HEAL].insert(spellID); continue; } if (IsPositiveSpell(spellInfo) && IsSpellAppliesAura(spellInfo)) { m_spellType[PET_SPELL_BUFF].insert(spellID); continue; } if (spellInfo->HasAttribute(SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)) { m_spellType[PET_SPELL_FREEACTION].insert(spellID); continue; } // don't have SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY ! if (spellInfo->HasAttribute(SPELL_ATTR_EX_CANT_REFLECTED) || spellInfo->HasAttribute(SPELL_ATTR_EX7_HAS_CHARGE_EFFECT)) { m_spellType[PET_SPELL_ATTACKSTART].insert(spellID); continue; } if (IsSpellIncreaseThreat(spellInfo)) { m_spellType[PET_SPELL_THREAT].insert(spellID); continue; } // all non-combat spells classified. switch (spellInfo->rangeIndex) { case SPELL_RANGE_IDX_COMBAT: { if (IsSpellCauseDamage(spellInfo)) { m_spellType[PET_SPELL_MELEE].insert(spellID); ++meleeDamageSpells; } else { m_spellType[PET_SPELL_SPECIAL].insert(spellID); } break; } // possible debuffs or auras? case SPELL_RANGE_IDX_SELF_ONLY: case SPELL_RANGE_IDX_ANYWHERE: { m_spellType[PET_SPELL_SPECIAL].insert(spellID); break; } default: { float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(spellInfo->rangeIndex), false); if (f_range < M_NULL_F || (range > M_NULL_F && range < f_range)) f_range = range; if (IsSpellCauseDamage(spellInfo)) { m_spellType[PET_SPELL_RANGED].insert(spellID); ++rangedDamageSpells; } else { m_spellType[PET_SPELL_SPECIAL].insert(spellID); } break; } } } // define initial AI type if (m_creature->IsVehicle()) m_AIType = PET_AI_PASSIVE; if (m_spellType[PET_SPELL_RANGED].size() > 0 && (m_spellType[PET_SPELL_MELEE].size() < m_spellType[PET_SPELL_RANGED].size())) { m_AIType = PET_AI_RANGED; m_attackDistance = f_range - m_creature->GetObjectBoundingRadius() - 2.0f; if (m_attackDistance < 20.0f) m_attackDistance = 18.0f; } else { m_AIType = PET_AI_MELEE; m_attackDistance = 0.0f; } m_savedAIType = m_AIType; m_creature->GetMotionMaster()->MoveTargetedHome(); DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS,"PetAI::Reset %s, AI %u dist %f, spells: "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD , m_creature->GetObjectGuid().GetString().c_str(), m_AIType, m_attackDistance, m_spellType[PET_SPELL_PASSIVE].size(), m_spellType[PET_SPELL_NONCOMBAT].size(), m_spellType[PET_SPELL_BUFF].size(), m_spellType[PET_SPELL_DEBUFF].size(), m_spellType[PET_SPELL_FREEACTION].size(), m_spellType[PET_SPELL_ATTACKSTART].size(), m_spellType[PET_SPELL_THREAT].size(), m_spellType[PET_SPELL_MELEE].size(), m_spellType[PET_SPELL_RANGED].size(), m_spellType[PET_SPELL_DEFENCE].size(), m_spellType[PET_SPELL_SPECIAL].size(), m_spellType[PET_SPELL_HEAL].size() ); }
void PetAI::UpdateAI(const uint32 diff) { if (!m_creature->isAlive()) return; Unit* owner = m_creature->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (inCombat && (!m_creature->getVictim() || (m_creature->IsPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS))) _stopAttack(); // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. if (m_creature->getVictim()) { if (_needToStop()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_creature->GetGUIDLow()); _stopAttack(); return; } bool meleeReach = m_creature->CanReachWithMeleeAttack(m_creature->getVictim()); if (m_creature->IsStopped() || meleeReach) { // required to be stopped cases if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false)) { if (m_creature->hasUnitState(UNIT_STAT_FOLLOW_MOVE)) m_creature->InterruptNonMeleeSpells(false); else return; } // not required to be stopped case else if (DoMeleeAttackIfReady()) { if (!m_creature->getVictim()) return; // if pet misses its target, it will also be the first in threat list m_creature->getVictim()->AddThreat(m_creature); if (_needToStop()) _stopAttack(); } } } else if (owner && m_creature->GetCharmInfo()) { if (owner->isInCombat() && !(m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY))) { AttackStart(owner->getAttackerForHelper()); } else if (m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) { if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW)) { m_creature->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); } } } // Autocast (casted only in combat or persistent spells in any state) if (!m_creature->IsNonMeleeSpellCasted(false)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i) { uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID); if (!spellInfo) continue; if (m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; // ignore some combinations of combat state and combat/noncombat spells if (!inCombat) { // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this reqs: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (!IsNonCombatSpell(spellInfo)) { // allow only spell without spell cost or with spell cost but not duration limit int32 duration = GetSpellDuration(spellInfo); SpellPowerEntry const* spellPower = spellInfo->GetSpellPower(); if (spellPower && (spellPower->manaCost || spellPower->ManaCostPercentage || spellPower->manaPerSecond) && duration > 0) continue; // allow only spell without cooldown > duration int32 cooldown = GetSpellRecoveryTime(spellInfo); if (cooldown >= 0 && duration >= 0 && cooldown > duration) continue; } } else { // just ignore non-combat spells if (IsNonCombatSpell(spellInfo)) continue; } Spell* spell = new Spell(m_creature, spellInfo, false); if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim())) { targetSpellStore.push_back(TargetSpellList::value_type(m_creature->getVictim(), spell)); continue; } else { bool spellUsed = false; for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = m_creature->GetMap()->GetUnit(*tar); // only buff targets that are in combat, unless the spell can only be cast while out of combat if (!Target) continue; if (spell->CanAutoCast(Target)) { targetSpellStore.push_back(TargetSpellList::value_type(Target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } } // found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget(target); if (!m_creature->HasInArc(M_PI_F, target)) { m_creature->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)owner); } m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } }
void PetAI::UpdateAI(const uint32 diff) { if (!m_creature->isAlive()) return; Unit* owner = m_creature->GetCharmerOrOwner(); m_updateAlliesTimer.Update(diff); if (m_updateAlliesTimer.Passed()) { UpdateAllies(); m_updateAlliesTimer.Reset(); } if (!inCombat && !m_savedTargetGuid.IsEmpty()) { if (Unit* saved_target = m_creature->GetMap()->GetUnit(m_savedTargetGuid)) { if (!saved_target->isAlive()) m_savedTargetGuid.Clear(); else if (!saved_target->IsCrowdControlled()) AttackStart(saved_target); } else m_savedTargetGuid.Clear(); } if (inCombat && (!m_creature->getVictim() || !m_creature->getVictim()->isAlive() || (m_creature->IsPet() && m_creature->GetCharmInfo()->HasState(CHARM_STATE_ACTION, ACTIONS_DISABLE)))) _stopAttack(); if (m_creature->hasUnitState(UNIT_STAT_CAN_NOT_REACT) || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) { UpdateAIType(); return; } // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. if (m_creature->getVictim()) { bool meleeReach = m_creature->CanReachWithMeleeAttack(m_creature->getVictim()); if (_needToStop()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_creature->GetGUIDLow()); _stopAttack(); return; } else if (!m_creature->getVictim()->isAlive()) // Stop attack if target dead { m_creature->InterruptNonMeleeSpells(false); _stopAttack(); return; } else if (sWorld.getConfig(CONFIG_BOOL_PET_ADVANCED_AI) && IsInCombat() && m_creature->getVictim() && m_creature->getVictim()->IsCrowdControlled()) // Stop attack if target under CC effect { m_savedTargetGuid = m_creature->getVictim()->GetObjectGuid(); m_creature->InterruptSpell(CURRENT_GENERIC_SPELL, true); if (!m_creature->IsNonMeleeSpellCasted(false, false, true)) _stopAttack(); return; } else if (m_creature->IsStopped() || meleeReach) { // required to be stopped cases if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false)) { if (m_creature->hasUnitState(UNIT_STAT_FOLLOW_MOVE)) m_creature->InterruptNonMeleeSpells(false); else return; } // not required to be stopped case else if (DoMeleeAttackIfReady()) { if (!m_creature->getVictim()) return; //if pet misses its target, it will also be the first in threat list m_creature->getVictim()->AddThreat(m_creature); if (_needToStop()) _stopAttack(); } } if (!m_creature->IsNonMeleeSpellCasted(true)) { m_attackDistanceRecheckTimer.Update(diff); if (m_attackDistanceRecheckTimer.Passed()) { m_attackDistanceRecheckTimer.Reset(); if (sWorld.getConfig(CONFIG_BOOL_PET_ADVANCED_AI) && m_AIType == PET_AI_RANGED) { float dist = m_creature->GetDistance(m_creature->getVictim()); if ((m_creature->CanReachWithMeleeAttack(m_creature->getVictim()) && m_creature->IsWithinDist(m_creature->GetOwner(), m_creature->GetMap()->GetVisibilityDistance() / 2.0f)) || dist > (m_attackDistance + 2.0f)) { MoveToVictim(m_creature->getVictim()); return; } } if (sWorld.getConfig(CONFIG_BOOL_PET_ADVANCED_AI)) { // AOE check } } } } else if (Unit* target = GetPrimaryTarget()) { AttackStart(target); } else if (owner && owner->IsInCombat()) { switch (m_creature->GetCharmState(CHARM_STATE_REACT)) { case REACT_DEFENSIVE: { if (!m_creature->getVictim() || !m_creature->getVictim()->isAlive() || (m_primaryTargetGuid.IsEmpty() && owner->getVictim() != m_creature->getVictim() && owner->getVictim()->isAlive())) AttackStart(owner->getAttackerForHelper()); break; } case REACT_AGGRESSIVE: { if (!m_creature->getVictim() || !m_creature->getVictim()->isAlive()) AttackStart(owner->getAttackerForHelper()); break; } case REACT_PASSIVE: default: break; } } UpdateAIType(); if (m_creature->IsNonMeleeSpellCasted(true)) return; // Autocast (casted only in combat or persistent spells in any state) if (!sWorld.getConfig(CONFIG_BOOL_PET_ADVANCED_AI) && m_AIType != PET_AI_PASSIVE) { typedef std::vector<std::pair<ObjectGuid, uint32> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i) { uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID); if (!spellInfo) continue; if (m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; if (m_creature->HasSpellCooldown(spellInfo)) continue; // ignore some combinations of combat state and combat/noncombat spells if (!inCombat) { // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this reqs: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (!IsNonCombatSpell(spellInfo)) { // allow only spell without spell cost or with spell cost but not duration limit int32 duration = GetSpellDuration(spellInfo); if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0) continue; // allow only spell without cooldown > duration int32 cooldown = GetSpellRecoveryTime(spellInfo); if (cooldown >= 0 && duration >= 0 && cooldown > duration) continue; } } else { // just ignore non-combat spells if (IsNonCombatSpell(spellInfo)) continue; } Unit* autoCastTarget = NULL; if (inCombat && m_creature->getVictim() && !m_creature->hasUnitState(UNIT_STAT_FOLLOW)) { SpellCastResult result = CanAutoCast(m_creature->getVictim(), spellInfo); if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT) autoCastTarget = m_creature->getVictim(); } if (!autoCastTarget) { for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* target = m_creature->GetMap()->GetUnit(*tar); // Only buff targets that are in combat, unless the spell can only be cast while out of combat if (!target) continue; SpellCastResult result = CanAutoCast(target, spellInfo); if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT) { autoCastTarget = target; break; } } } if (autoCastTarget) targetSpellStore.push_back(TargetSpellList::value_type(autoCastTarget->GetObjectGuid(), spellInfo->Id)); } // found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); if (Unit* target = m_creature->GetMap()->GetUnit(targetSpellStore[index].first)) m_creature->DoPetCastSpell(target, targetSpellStore[index].second); } } else { AutoSpellList currentSpells; switch (m_AIType) { case PET_AI_PASSIVE: { currentSpells.push_back(GetSpellType(PET_SPELL_BUFF)); break; } case PET_AI_SLACKER: { if (!IsInCombat()) break; if (m_creature->IsCrowdControlled() || (owner && owner->IsCrowdControlled())) currentSpells.push_back(GetSpellType(PET_SPELL_FREEACTION)); currentSpells.push_back(GetSpellType(PET_SPELL_DEFENCE)); currentSpells.push_back(GetSpellType(PET_SPELL_BUFF)); currentSpells.push_back(GetSpellType(PET_SPELL_DEBUFF)); currentSpells.push_back(GetSpellType(PET_SPELL_RANGED)); break; } case PET_AI_HEALER: { if (!IsInCombat()) break; if (m_creature->IsCrowdControlled() || (owner && owner->IsCrowdControlled())) currentSpells.push_back(GetSpellType(PET_SPELL_FREEACTION)); if (m_creature->GetHealth() < m_creature->GetMaxHealth() || (owner && (owner->GetHealth() < owner->GetMaxHealth()))) currentSpells.push_back(GetSpellType(PET_SPELL_HEAL)); currentSpells.push_back(GetSpellType(PET_SPELL_BUFF)); currentSpells.push_back(GetSpellType(PET_SPELL_RANGED)); break; } case PET_AI_RANGED: { if (!IsInCombat()) break; if (m_creature->IsCrowdControlled() || (owner && owner->IsCrowdControlled())) currentSpells.push_back(GetSpellType(PET_SPELL_FREEACTION)); currentSpells.push_back(GetSpellType(PET_SPELL_RANGED)); currentSpells.push_back(GetSpellType(PET_SPELL_DEBUFF)); currentSpells.push_back(GetSpellType(PET_SPELL_BUFF)); break; } case PET_AI_MELEE: case PET_AI_RANGED_NOAMMO: { if (!IsInCombat()) break; if (Unit* victim = m_creature->getVictim()) { if (!victim->getVictim() || (victim->getVictim()->GetObjectGuid() != m_creature->GetObjectGuid())) { currentSpells.push_back(GetSpellType(PET_SPELL_ATTACKSTART)); currentSpells.push_back(GetSpellType(PET_SPELL_THREAT)); } } if (m_creature->IsCrowdControlled() || (owner && owner->IsCrowdControlled())) currentSpells.push_back(GetSpellType(PET_SPELL_FREEACTION)); } /* no break here!*/ default: { if (!IsInCombat()) break; currentSpells.push_back(GetSpellType(PET_SPELL_MELEE)); currentSpells.push_back(GetSpellType(PET_SPELL_DEBUFF)); currentSpells.push_back(GetSpellType(PET_SPELL_RANGED)); currentSpells.push_back(GetSpellType(PET_SPELL_BUFF)); break; } } if (!IsInCombat()) { currentSpells.push_back(GetSpellType(PET_SPELL_NONCOMBAT)); if (m_creature->GetHealthPercent() < 95.0f) currentSpells.push_back(GetSpellType(PET_SPELL_HEAL)); } else currentSpells.push_back(GetSpellType(PET_SPELL_SPECIAL)); for (AutoSpellList::const_iterator itr = currentSpells.begin(); itr != currentSpells.end(); ++itr) { uint32 spellID = *itr; if (!spellID) continue; SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID); if (!spellInfo) continue; if (m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; Unit* pTarget = m_creature->IsPet() ? ((Pet*)m_creature)->SelectPreferredTargetForSpell(spellInfo) : ((Creature*)m_creature)->SelectPreferredTargetForSpell(spellInfo); bool b_castOk = false; if (pTarget) { SpellCastResult result = CanAutoCast(pTarget, spellInfo); DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS,"PetAI::Update %s, AI %u try cast %u Target %s", m_creature->GetGuidStr().c_str(), m_AIType, spellID, pTarget ? pTarget->GetGuidStr().c_str() : "<none>"); switch (result) { case SPELL_FAILED_UNIT_NOT_INFRONT: { if (DoCastSpellIfCan(pTarget, spellID) == CAST_OK) { b_castOk = true; m_creature->SetInFront(pTarget); if (pTarget->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)pTarget); } break; } case SPELL_CAST_OK: { if (DoCastSpellIfCan(pTarget, spellID) == CAST_OK) b_castOk = true; break; } default: { Player* owner = (Player*)m_creature->GetOwner(); if (owner) Spell::SendCastResult(owner,spellInfo,0,result, true); DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS,"PetAI::Update cast %s, AI %u Target %s spell %u result %u", m_creature->GetGuidStr().c_str(), m_AIType, pTarget ? pTarget->GetGuidStr().c_str() : "<none>", spellID, result); break; } } } else continue; if (b_castOk) { m_creature->AddSpellAndCategoryCooldowns(spellInfo); if (m_creature->IsPet()) { if(((Pet*)m_creature)->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) m_creature->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else m_creature->SendPetAIReaction(); } break; } } } }
void PetAI::UpdateAI(const uint32 diff) { if (!m_unit->isAlive()) return; Creature* creature = (m_unit->GetTypeId() == TYPEID_UNIT) ? static_cast<Creature*>(m_unit) : nullptr; Pet* pet = (creature && creature->IsPet()) ? static_cast<Pet*>(m_unit) : nullptr; Unit* owner = m_unit->GetMaster(); if (!owner) return; Unit* victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (inCombat && !victim) { m_unit->AttackStop(true, true); inCombat = false; } CharmInfo* charminfo = m_unit->GetCharmInfo(); MANGOS_ASSERT(charminfo); if (charminfo->GetIsRetreating()) { if (!owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2))) { if (!m_unit->hasUnitState(UNIT_STAT_FOLLOW)) m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); return; } else charminfo->SetIsRetreating(); } else if (charminfo->GetSpellOpener() != 0) // have opener stored { uint32 minRange = charminfo->GetSpellOpenerMinRange(); if (!(victim = m_unit->getVictim()) || (minRange != 0 && m_unit->IsWithinDistInMap(victim, minRange))) charminfo->SetSpellOpener(); else if (m_unit->IsWithinDistInMap(victim, charminfo->GetSpellOpenerMaxRange()) && m_unit->IsWithinLOSInMap(victim)) { // stop moving m_unit->clearUnitState(UNIT_STAT_MOVING); // auto turn to target m_unit->SetInFront(victim); if (victim->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)victim); if (owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); uint32 spell_id = charminfo->GetSpellOpener(); SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spell_id); Spell* spell = new Spell(m_unit, spellInfo, false); SpellCastResult result = spell->CheckPetCast(victim); if (result == SPELL_CAST_OK) spell->SpellStart(&(spell->m_targets)); else delete spell; charminfo->SetSpellOpener(); } else return; } // Auto cast (casted only in combat or persistent spells in any state) else if (!m_unit->IsNonMeleeSpellCasted(false)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; if (pet) { for (uint8 i = 0; i < pet->GetPetAutoSpellSize(); ++i) { uint32 spellID = pet->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellID); if (!spellInfo) continue; if (!m_unit->IsSpellReady(*spellInfo)) continue; // ignore some combinations of combat state and combat/non combat spells if (!inCombat) { // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this requirements: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (!IsNonCombatSpell(spellInfo)) { int32 duration = GetSpellDuration(spellInfo); int32 cooldown = GetSpellRecoveryTime(spellInfo); // allow only spell not on cooldown if (cooldown != 0 && duration < cooldown) continue; // not allow instant kill auto casts as full health cost if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_INSTAKILL)) continue; } } // just ignore non-combat spells else if (IsNonCombatSpell(spellInfo)) continue; Spell* spell = new Spell(m_unit, spellInfo, false); if (inCombat && !m_unit->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(victim)) { targetSpellStore.push_back(TargetSpellList::value_type(victim, spell)); continue; } else { bool spellUsed = false; for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = m_unit->GetMap()->GetUnit(*tar); // only buff targets that are in combat, unless the spell can only be cast while out of combat if (!Target) continue; if (spell->CanAutoCast(Target)) { targetSpellStore.push_back(TargetSpellList::value_type(Target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } } } // found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget(target); if (!m_unit->HasInArc(M_PI_F, target)) { m_unit->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); } spell->SpellStart(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } // Stop here if casting spell (No melee and no movement) if (m_unit->IsNonMeleeSpellCasted(false)) return; // we may get our actions disabled during spell casting, so do entire recheck for victim victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim(); if (victim) { // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. // This is needed for charmed creatures, as once their target was reset other effects can trigger threat if (!victim->isTargetableForAttack()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_unit->GetGUIDLow()); m_unit->CombatStop(); inCombat = false; return; } // if pet misses its target, it will also be the first in threat list if ((!creature || !(creature->GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_NO_MELEE)) && m_unit->CanReachWithMeleeAttack(victim)) { if (!m_unit->HasInArc(2 * M_PI_F / 3, victim)) { m_unit->SetInFront(victim); if (victim->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)victim); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); } DoMeleeAttackIfReady(); } else if (!m_unit->hasUnitState(UNIT_STAT_MOVING)) AttackStart(victim); } else if (owner) { CharmInfo* charmInfo = m_unit->GetCharmInfo(); if (owner->isInCombat() && !(charmInfo && charmInfo->HasReactState(REACT_PASSIVE))) AttackStart(owner->getAttackerForHelper()); else { if (charmInfo && charmInfo->HasCommandState(COMMAND_STAY)) { //if stay command is set but we don't have stay pos set then we need to establish current pos as stay position if (!charminfo->IsStayPosSet()) charminfo->SetStayPosition(true); float stayPosX = charminfo->GetStayPosX(); float stayPosY = charminfo->GetStayPosY(); float stayPosZ = charminfo->GetStayPosZ(); if (m_unit->GetPositionX() == stayPosX && m_unit->GetPositionY() == stayPosY && m_unit->GetPositionZ() == stayPosZ) { float StayPosO = charminfo->GetStayPosO(); if (m_unit->hasUnitState(UNIT_STAT_MOVING)) { m_unit->GetMotionMaster()->Clear(false); m_unit->GetMotionMaster()->MoveIdle(); } else if (m_unit->GetOrientation() != StayPosO) m_unit->SetOrientation(StayPosO); } else m_unit->GetMotionMaster()->MovePoint(0, stayPosX, stayPosY, stayPosZ, false); } else if (m_unit->hasUnitState(UNIT_STAT_FOLLOW)) { if (owner->IsWithinDistInMap(m_unit, PET_FOLLOW_DIST)) { m_unit->GetMotionMaster()->Clear(false); m_unit->GetMotionMaster()->MoveIdle(); } } else if (charmInfo && charmInfo->HasCommandState(COMMAND_FOLLOW) && !owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2))) m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); } } }
bool WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff) { if (!&creature) return true; // Waypoint movement can be switched on/off // This is quite handy for escort quests and other stuff if (creature.hasUnitState(UNIT_STAT_NOT_MOVE)) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } // prevent a crash at empty waypoint path. if (!i_path || i_path->empty()) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } if (i_currentNode >= i_path->size()) { sLog.outError("WaypointMovement currentNode (%u) is equal or bigger than path size (creature entry %u)", i_currentNode, creature.GetEntry()); i_currentNode = 0; } CreatureTraveller traveller(creature); i_nextMoveTime.Update(diff); if (i_destinationHolder.UpdateTraveller(traveller, diff, false, true)) { if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost } // creature has been stopped in middle of the waypoint segment if (!i_destinationHolder.HasArrived() && creature.IsStopped()) { // Timer has elapsed, meaning this part controlled it if (i_nextMoveTime.Passed()) { SetStoppedByPlayer(false); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.canFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); // Now we re-set destination to same node and start travel const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); } else // if( !i_nextMoveTime.Passed()) { // unexpected end of timer && creature stopped && not at end of segment if (!IsStoppedByPlayer()) { // Put 30 seconds delay i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER); i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER); SetStoppedByPlayer(true); // Mark we did it } } return true; // Abort here this update } if (creature.IsStopped()) { if (!m_isArrivalDone) { if (i_path->at(i_currentNode).orientation != 100) creature.SetOrientation(i_path->at(i_currentNode).orientation); if (i_path->at(i_currentNode).script_id) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature movement start script %u at point %u for creature %u (entry %u).", i_path->at(i_currentNode).script_id, i_currentNode, creature.GetDBTableGUIDLow(), creature.GetEntry()); creature.GetMap()->ScriptsStart(sCreatureMovementScripts, i_path->at(i_currentNode).script_id, &creature, &creature); } // We have reached the destination and can process behavior if (WaypointBehavior *behavior = i_path->at(i_currentNode).behavior) { if (behavior->emote != 0) creature.HandleEmote(behavior->emote); if (behavior->spell != 0) { creature.CastSpell(&creature, behavior->spell, false); if (!IsActive(creature)) // force stop processing (cast can change movegens list) return true; // not expire now, but already lost } if (behavior->model1 != 0) creature.SetDisplayId(behavior->model1); if (behavior->textid[0]) { // Not only one text is set if (behavior->textid[1]) { // Select one from max 5 texts (0 and 1 already checked) int i = 2; for(; i < MAX_WAYPOINT_TEXT; ++i) { if (!behavior->textid[i]) break; } creature.Say(behavior->textid[rand() % i], 0, 0); } else creature.Say(behavior->textid[0], 0, 0); } } // wpBehaviour found // Can only do this once for the node m_isArrivalDone = true; // Inform script MovementInform(creature); if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost // prevent a crash at empty waypoint path. if (!i_path || i_path->empty() || i_currentNode >= i_path->size()) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } } } // i_creature.IsStopped() // This is at the end of waypoint segment (incl. was previously stopped by player, extending the time) if (i_nextMoveTime.Passed()) { // If stopped then begin a new move segment if (creature.IsStopped()) { creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.canFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); if (WaypointBehavior *behavior = i_path->at(i_currentNode).behavior) { if (behavior->model2 != 0) creature.SetDisplayId(behavior->model2); creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); } // behavior for "departure" of the current node is done m_isArrivalDone = false; // Proceed with increment current node and then send to the next destination ++i_currentNode; // Oops, end of the line so need to start from the beginning if (i_currentNode >= i_path->size()) i_currentNode = 0; if (i_path->at(i_currentNode).orientation != 100) creature.SetOrientation(i_path->at(i_currentNode).orientation); const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); } else { // If not stopped then stop it creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); SetStoppedByPlayer(false); // Set TimeTracker to waittime for the current node i_nextMoveTime.Reset(i_path->at(i_currentNode).delay); } } return true; }
void PathInfo::BuildPointPath(float *startPoint, float *endPoint) { float pathPoints[MAX_POINT_PATH_LENGTH*VERTEX_SIZE]; uint32 pointCount = 0; dtStatus dtResult = DT_FAILURE; bool usedOffmesh = false; if (m_useStraightPath) { dtResult = m_navMeshQuery->findStraightPath( startPoint, // start position endPoint, // end position m_pathPolyRefs, // current path m_polyLength, // lenth of current path pathPoints, // [out] path corner points NULL, // [out] flags NULL, // [out] shortened path (int*)&pointCount, MAX_POINT_PATH_LENGTH); // maximum number of points/polygons to use } else { dtResult = findSmoothPath( startPoint, // start position endPoint, // end position m_pathPolyRefs, // current path m_polyLength, // length of current path pathPoints, // [out] path corner points (int*)&pointCount, usedOffmesh, MAX_POINT_PATH_LENGTH); // maximum number of points } if (pointCount < 2 || dtResult != DT_SUCCESS) { // only happens if pass bad data to findStraightPath or navmesh is broken // single point paths can be generated here // TODO : check the exact cases DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathInfo::BuildPointPath FAILED! path sized %d returned\n", pointCount); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } // we need to flash our poly path to prevent it being used as subpath next cycle // in case of off mesh connection was used if(usedOffmesh) m_polyLength = 0; m_pathPoints.resize(pointCount); for (uint32 i = 0; i < pointCount; ++i) m_pathPoints.set(i, PathNode(pathPoints[i*VERTEX_SIZE+2], pathPoints[i*VERTEX_SIZE], pathPoints[i*VERTEX_SIZE+1])); // first point is always our current location - we need the next one setNextPosition(m_pathPoints[1]); setActualEndPosition(m_pathPoints[pointCount-1]); // force the given destination, if needed if(m_forceDestination && (!(m_type & PATHFIND_NORMAL) || !inRange(getEndPosition(), getActualEndPosition(), 1.0f, 1.0f))) { setActualEndPosition(getEndPosition()); BuildShortcut(); m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); return; } DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathInfo::BuildPointPath path type %d size %d poly-size %d\n", m_type, pointCount, m_polyLength); }
void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T& owner, bool updateDestination) { if (!i_target.isValid() || !i_target->IsInWorld()) return; if (owner.hasUnitState(UNIT_STAT_NOT_MOVE)) return; if (owner.IsNonMeleeSpellCasted(false)) { // some spells should be able to be cast while moving // maybe some attribute? here, check the entry of creatures useing these spells switch(owner.GetEntry()) { case 36633: // Ice Sphere (Lich King) case 37562: // Volatile Ooze and Gas Cloud (Putricide) case 37697: break; default: return; } } if (!i_target->isInAccessablePlaceFor(&owner)) return; float x, y, z; bool targetIsVictim = owner.getVictim() && owner.getVictim()->GetObjectGuid() == i_target->GetObjectGuid(); // i_path can be NULL in case this is the first call for this MMGen (via Update) // Can happen for example if no path was created on MMGen-Initialize because of the owner being stunned if (updateDestination || !i_path) { // prevent redundant micro-movement for pets, other followers. if ((fabs(i_offset) > M_NULL_F) && i_target->IsWithinDistInMap(&owner, i_offset + PET_FOLLOW_DIST)) { if (!owner.movespline->Finalized()) return; owner.GetPosition(x, y, z); } else if (fabs(i_offset) < M_NULL_F) { // to nearest contact position float dist = 0.0f; if (targetIsVictim) dist = owner.GetFloatValue(UNIT_FIELD_COMBATREACH) + i_target->GetFloatValue(UNIT_FIELD_COMBATREACH) - i_target->GetObjectBoundingRadius() - owner.GetObjectBoundingRadius() - 1.0f; if (dist < 0.5f) dist = 0.5f; i_target->GetContactPoint(&owner, x, y, z, dist); } else { // to at i_offset distance from target and i_angle from target facing i_target->GetClosePoint(x, y, z, owner.GetObjectBoundingRadius(), i_offset, i_angle, &owner); } } else { // the destination has not changed, we just need to refresh the path (usually speed change) G3D::Vector3 end = i_path->getEndPosition(); x = end.x; y = end.y; z = end.z; } if (!i_path) i_path = new PathFinder(&owner); // allow pets following their master to cheat while generating paths bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->IsPet() && owner.hasUnitState(UNIT_STAT_FOLLOW)); i_path->calculate(x, y, z, forceDest); if (i_path->getPathType() & PATHFIND_NOPATH) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS,"TargetedMovementGeneratorMedium:: unit %s cannot find path to %s (%f, %f, %f), gained PATHFIND_NOPATH! Owerride used.", owner.GetObjectGuid().GetString().c_str(), i_target.isValid() ? i_target->GetObjectGuid().GetString().c_str() : "<none>", x,y,z); //return; } D::_addUnitStateMove(owner); i_targetReached = false; m_speedChanged = false; Movement::MoveSplineInit init(owner); init.MovebyPath(i_path->getPath()); init.SetWalk(((D*)this)->EnableWalking()); init.Launch(); }
PathInfo::~PathInfo() { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathInfo::~PathInfo() for %u \n", m_sourceUnit->GetGUID()); }
void WorldSession::HandleCalendarEventInvite(WorldPacket& recv_data) { ObjectGuid playerGuid = GetPlayer()->GetObjectGuid(); DEBUG_LOG("WORLD: CMSG_CALENDAR_EVENT_INVITE [%u]", playerGuid.GetCounter()); ObjectGuid eventId; // TODO it seem its not inviteID but event->CreatorGuid ObjectGuid inviteId; std::string name; bool isPreInvite; bool isGuildEvent; ObjectGuid inviteeGuid = ObjectGuid(); uint32 inviteeTeam = 0; uint32 inviteeGuildId = 0; recv_data >> eventId >> inviteId >> name >> isPreInvite >> isGuildEvent; if (Player* player = sObjectAccessor.FindPlayerByName(name.c_str())) { // Invitee is online inviteeGuid = player->GetObjectGuid(); inviteeTeam = player->GetTeam(); inviteeGuildId = player->GetGuildId(); } else { // Invitee offline, get data from database PlayerDataCache const* data = sAccountMgr.GetPlayerDataCache(name); if (data) { inviteeGuid = ObjectGuid(HIGHGUID_PLAYER, data->lowguid); inviteeTeam = Player::TeamForRace(data->race); inviteeGuildId = Player::GetGuildIdFromDB(inviteeGuid); } } if (inviteeGuid.IsEmpty() || !inviteeGuid.IsPlayer()) { sCalendarMgr.SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_PLAYER_NOT_FOUND); return; } if (_player->GetTeam() != inviteeTeam && !sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_CALENDAR)) { sCalendarMgr.SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_NOT_ALLIED); return; } /* wrong query (must be checked in another place), also memleak here. FIXME! if (QueryResult* result = CharacterDatabase.PQuery("SELECT flags FROM character_social WHERE guid = " UI64FMTD " AND friend = " UI64FMTD, inviteeGuid, playerGuid)) { Field* fields = result->Fetch(); if (fields[0].GetUInt8() & SOCIAL_FLAG_IGNORED) { sCalendarMgr.SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_IGNORING_YOU_S, name.c_str()); return; } } */ if (!isPreInvite) { if (CalendarEvent* calendarEvent = sCalendarMgr.GetEventById(eventId)) { if (calendarEvent->IsGuildEvent() && calendarEvent->GuildId == inviteeGuildId) { // we can't invite guild members to guild events sCalendarMgr.SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_NO_GUILD_INVITES); return; } sCalendarMgr.AddInvite(calendarEvent, playerGuid, inviteeGuid, CALENDAR_STATUS_INVITED, CALENDAR_RANK_PLAYER, "", time(NULL)); } else sCalendarMgr.SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_EVENT_INVALID); } else { if (isGuildEvent && inviteeGuildId == _player->GetGuildId()) { sCalendarMgr.SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_NO_GUILD_INVITES); return; } // create a temp invite to send it back to client CalendarInvite calendarInvite; calendarInvite.SenderGuid = playerGuid; calendarInvite.InviteeGuid = inviteeGuid; calendarInvite.Status = CALENDAR_STATUS_INVITED; calendarInvite.Rank = CALENDAR_RANK_PLAYER; calendarInvite.LastUpdateTime = time(NULL); sCalendarMgr.SendCalendarEventInvite(&calendarInvite); DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "WorldSession::HandleCalendarEventInvite PREINVITE sender[%u], Invitee[%u]", playerGuid.GetCounter(), inviteeGuid.GetCounter()); } }
void UnitStateMgr::DropAllStates() { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "UnitStateMgr:DropAllStates %s drop all active states", GetOwnerStr().c_str()); DropActionHigherThen(UNIT_ACTION_PRIORITY_IDLE); PushAction(UNIT_ACTION_IDLE); }
void WorldSession::HandleCalendarUpdateEvent(WorldPacket& recv_data) { ObjectGuid guid = _player->GetObjectGuid(); DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_UPDATE_EVENT [%s]", guid.GetString().c_str()); time_t oldEventTime; uint64 eventId; uint64 inviteId; std::string title; std::string description; uint8 type; uint8 repetitionType; uint32 maxInvites; int32 dungeonId; uint32 eventPackedTime; uint32 UnknownPackedTime; uint32 flags; recv_data >> eventId >> inviteId >> title >> description >> type >> repetitionType >> maxInvites >> dungeonId; recv_data >> eventPackedTime; recv_data >> UnknownPackedTime; recv_data >> flags; DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "], InviteId [" UI64FMTD "] Title %s, Description %s, type %u " "Repeatable %u, MaxInvites %u, Dungeon ID %d, Flags %u", eventId, inviteId, title.c_str(), description.c_str(), uint32(type), uint32(repetitionType), maxInvites, dungeonId, flags); if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) { if (guid != event->CreatorGuid) { CalendarInvite* updaterInvite = event->GetInviteByGuid(guid); if (updaterInvite == nullptr) { sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NOT_INVITED); return ; } if (updaterInvite->Rank != CALENDAR_RANK_MODERATOR) { // remover have not enough right to change invite status sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_PERMISSIONS); return; } } oldEventTime = event->EventTime; event->Type = CalendarEventType(type); event->Flags = flags; event->EventTime = timeBitFieldsToSecs(eventPackedTime); event->UnknownTime = timeBitFieldsToSecs(UnknownPackedTime); event->DungeonId = dungeonId; event->Title = title; event->Description = description; sCalendarMgr.SendCalendarEventUpdateAlert(event, oldEventTime); // query construction CharacterDatabase.escape_string(title); CharacterDatabase.escape_string(description); CharacterDatabase.PExecute("UPDATE calendar_events SET " "type=%u, flags=%u, dungeonId=%d, eventTime=%u, title='%s', description='%s'" "WHERE eventid=" UI64FMTD, uint32(type), flags, dungeonId, uint32(event->EventTime), title.c_str(), description.c_str(), eventId); } else sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); }
void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T& owner, bool updateDestination) { if (!m_target.isValid() || !m_target->IsInWorld()) return; if (owner.hasUnitState(UNIT_STAT_NOT_MOVE)) return; if (!IsAbleMoveWhenCast(owner.GetEntry()) && owner.IsNonMeleeSpellCasted(false)) return; if (!m_target->isInAccessablePlaceFor(&owner)) return; float x, y, z; // m_path can be nullptr in case this is the first call for this MMGen (via Update) // Can happen for example if no path was created on MMGen-Initialize because of the owner being stunned if (updateDestination || !m_path) { owner.GetPosition(x, y, z); // Prevent redundant micro-movement for pets, other followers. if (!RequiresNewPosition(owner, x, y, z)) { if (!owner.movespline->Finalized()) return; } else { float absAngle; // Chase Movement and angle == 0 case: Chase to current angle if (this->GetMovementGeneratorType() == CHASE_MOTION_TYPE && m_angle == 0.0f) absAngle = m_target->GetAngle(&owner); // Targeted movement to at m_offset distance from target and m_angle from target facing else absAngle = m_target->GetOrientation() + m_angle; m_target->GetNearPoint(&owner, x, y, z, owner.GetObjectBoundingRadius(), static_cast<D*>(this)->GetDynamicTargetDistance(owner, false), absAngle); // Add hover height if (owner.IsLevitating()) z += owner.GetFloatValue(UNIT_FIELD_HOVERHEIGHT); } } else { // the destination has not changed, we just need to refresh the path (usually speed change) G3D::Vector3 endPos = m_path->getEndPosition(); x = endPos.x; y = endPos.y; z = endPos.z; } if (!m_path) m_path = new PathFinder(&owner); // allow pets following their master to cheat while generating paths bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->IsPet() && owner.hasUnitState(UNIT_STAT_FOLLOW)); m_path->calculate(x, y, z, forceDest); m_speedChanged = false; // don't move if path points equivalent if (m_path->getStartPosition() == m_path->getEndPosition()) { owner.StopMoving(true); return; } if (m_path->getPathType() & PATHFIND_NOPATH) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS,"TargetedMovementGeneratorMedium:: unit %s cannot find path to %s (%f, %f, %f), gained PATHFIND_NOPATH! Owerride used.", owner.GetGuidStr().c_str(), m_target.isValid() ? m_target->GetObjectGuid().GetString().c_str() : "<none>", x, y, z); //return; } D::_addUnitStateMove(owner); m_targetReached = false; Movement::MoveSplineInit<Unit*> init(owner); init.MovebyPath(m_path->getPath()); init.SetSmooth(); // fix broken fly movement for old creatures init.SetWalk( ((D*)this)->EnableWalking() || // hack for old creatures with bugged fly animation (owner.GetTypeId() == TYPEID_UNIT && owner.IsLevitating() && owner.GetFloatValue(UNIT_FIELD_HOVERHEIGHT) == 0.0f)); init.Launch(); }
void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recv_data*/) { ObjectGuid guid = _player->GetObjectGuid(); DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_GET_CALENDAR [%s]", guid.GetString().c_str()); time_t currTime = time(nullptr); WorldPacket data(SMSG_CALENDAR_SEND_CALENDAR); CalendarInvitesList invites; sCalendarMgr.GetPlayerInvitesList(guid, invites); data << uint32(invites.size()); DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Sending > %u invites", uint32(invites.size())); for (CalendarInvitesList::const_iterator itr = invites.begin(); itr != invites.end(); ++itr) { CalendarEvent const* event = (*itr)->GetCalendarEvent(); MANGOS_ASSERT(event); // TODO: be sure no way to have a null event data << uint64(event->EventId); data << uint64((*itr)->InviteId); data << uint8((*itr)->Status); data << uint8((*itr)->Rank); data << uint8(event->IsGuildEvent()); data << event->CreatorGuid.WriteAsPacked(); DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "invite> EventId[" UI64FMTD "], InviteId[" UI64FMTD "], status[%u], rank[%u]", event->EventId, (*itr)->InviteId, uint32((*itr)->Status), uint32((*itr)->Rank)); } CalendarEventsList events; sCalendarMgr.GetPlayerEventsList(guid, events); data << uint32(events.size()); DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Sending > %u events", uint32(events.size())); for (CalendarEventsList::const_iterator itr = events.begin(); itr != events.end(); ++itr) { CalendarEvent const* event = *itr; data << uint64(event->EventId); data << event->Title; data << uint32(event->Type); data << secsToTimeBitFields(event->EventTime); data << uint32(event->Flags); data << int32(event->DungeonId); data << event->CreatorGuid.WriteAsPacked(); std::string timeStr = TimeToTimestampStr(event->EventTime); DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Events> EventId[" UI64FMTD "], Title[%s], Time[%s], Type[%u], Flag[%u], DungeonId[%d], CreatorGuid[%s]", event->EventId, event->Title.c_str(), timeStr.c_str(), uint32(event->Type), uint32(event->Flags), event->DungeonId, event->CreatorGuid.GetString().c_str()); } data << uint32(currTime); // server time data << secsToTimeBitFields(currTime); // zone time ?? ByteBuffer dataBuffer; uint32 boundCounter = 0; for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) { Player::BoundInstancesMap boundInstances = _player->GetBoundInstances(Difficulty(i)); for (Player::BoundInstancesMap::const_iterator itr = boundInstances.begin(); itr != boundInstances.end(); ++itr) { if (itr->second.perm) { DungeonPersistentState const* state = itr->second.state; dataBuffer << uint32(state->GetMapId()); dataBuffer << uint32(state->GetDifficulty()); dataBuffer << uint32(state->GetResetTime() - currTime); dataBuffer << uint64(state->GetInstanceId()); // instance save id as unique instance copy id ++boundCounter; } } } data << uint32(boundCounter); data.append(dataBuffer); data << uint32(1135753200); // Constant date, unk (28.12.2005 07:00) // Reuse variables boundCounter = 0; std::set<uint32> sentMaps; dataBuffer.clear(); for (MapDifficultyMap::const_iterator itr = sMapDifficultyMap.begin(); itr != sMapDifficultyMap.end(); ++itr) { uint32 map_diff_pair = itr->first; uint32 mapId = PAIR32_LOPART(map_diff_pair); MapDifficultyEntry const* mapDiff = itr->second; // skip mapDiff without global reset time if (!mapDiff->resetTime) continue; // skip non raid map MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); if (!mapEntry || !mapEntry->IsRaid()) continue; // skip already sent map (not same difficulty?) if (sentMaps.find(mapId) != sentMaps.end()) continue; uint32 resetTime = sMapPersistentStateMgr.GetScheduler().GetMaxResetTimeFor(mapDiff); sentMaps.insert(mapId); dataBuffer << mapId; dataBuffer << resetTime; DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "MapId [%u] -> Reset Time: %u", mapId, resetTime); dataBuffer << int32(0); // showed 68400 on map 509 must investigate more ++boundCounter; } DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Map sent [%u]", boundCounter); data << uint32(boundCounter); data.append(dataBuffer); // TODO: Fix this, how we do know how many and what holidays to send? uint32 holidayCount = 0; data << uint32(holidayCount); /*for (uint32 i = 0; i < holidayCount; ++i) { HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(666); data << uint32(holiday->Id); // m_ID data << uint32(holiday->Region); // m_region, might be looping data << uint32(holiday->Looping); // m_looping, might be region data << uint32(holiday->Priority); // m_priority data << uint32(holiday->CalendarFilterType); // m_calendarFilterType for (uint8 j = 0; j < MAX_HOLIDAY_DATES; ++j) data << uint32(holiday->Date[j]); // 26 * m_date -- WritePackedTime ? for (uint8 j = 0; j < MAX_HOLIDAY_DURATIONS; ++j) data << uint32(holiday->Duration[j]); // 10 * m_duration for (uint8 j = 0; j < MAX_HOLIDAY_FLAGS; ++j) data << uint32(holiday->CalendarFlags[j]); // 10 * m_calendarFlags data << holiday->TextureFilename; // m_textureFilename (holiday name) }*/ SendPacket(&data); }