diff options
Diffstat (limited to 'src/server/game/WaypointMovementGenerator.cpp')
-rw-r--r-- | src/server/game/WaypointMovementGenerator.cpp | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/src/server/game/WaypointMovementGenerator.cpp b/src/server/game/WaypointMovementGenerator.cpp new file mode 100644 index 00000000000..2a0c7c0253d --- /dev/null +++ b/src/server/game/WaypointMovementGenerator.cpp @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +//Basic headers +#include "WaypointMovementGenerator.h" +#include "DestinationHolderImp.h" +//Extended headers +#include "ObjectMgr.h" +#include "World.h" +#include "MapManager.h" // for flightmaster grid preloading +//Creature-specific headers +#include "Creature.h" +#include "CreatureAI.h" +#include "CreatureGroups.h" +//Player-specific +#include "Player.h" + +template<class T> +void +WaypointMovementGenerator<T>::Initialize(T & /*u*/){} + +template<> +void +WaypointMovementGenerator<Creature>::Finalize(Creature & /*u*/){} + +template<> +void +WaypointMovementGenerator<Player>::Finalize(Player & /*u*/){} + +template<class T> +void +WaypointMovementGenerator<T>::MovementInform(T & /*unit*/){} + +template<> +void WaypointMovementGenerator<Creature>::MovementInform(Creature &unit) +{ + unit.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode); +} + +template<> +bool WaypointMovementGenerator<Creature>::GetDestination(float &x, float &y, float &z) const +{ + if (i_destinationHolder.HasArrived()) + return false; + + i_destinationHolder.GetDestination(x, y, z); + return true; +} + +template<> +bool WaypointMovementGenerator<Player>::GetDestination(float & /*x*/, float & /*y*/, float & /*z*/) const +{ + return false; +} + +template<> +void WaypointMovementGenerator<Creature>::Reset(Creature & /*unit*/) +{ + StopedByPlayer = true; + i_nextMoveTime.Reset(0); +} + +template<> +void WaypointMovementGenerator<Player>::Reset(Player & /*unit*/){} + +template<> +void WaypointMovementGenerator<Creature>::InitTraveller(Creature &unit, const WaypointData &node) +{ + node.run ? unit.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE): + unit.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + + unit.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); + unit.SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + + // TODO: make this part of waypoint node, so that creature can walk when desired? + if (unit.canFly()) + unit.SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0x02); + + unit.addUnitState(UNIT_STAT_ROAMING); +} + +template<> +void +WaypointMovementGenerator<Creature>::Initialize(Creature &u) +{ + u.StopMoving(); + //i_currentNode = -1; // uint32, become 0 in the first update + //i_nextMoveTime.Reset(0); + StopedByPlayer = false; + if (!path_id) + path_id = u.GetWaypointPath(); + waypoints = sWaypointMgr->GetPath(path_id); + i_currentNode = 0; + if (waypoints && waypoints->size()) + { + node = waypoints->front(); + Traveller<Creature> traveller(u); + InitTraveller(u, *node); + i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z); + i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); + + //Call for creature group update + if (u.GetFormation() && u.GetFormation()->getLeader() == &u) + u.GetFormation()->LeaderMoveTo(node->x, node->y, node->z); + } + else + node = NULL; +} + +template<> +void WaypointMovementGenerator<Player>::InitTraveller(Player & /*unit*/, const WaypointData & /*node*/){} + +template<class T> +bool +WaypointMovementGenerator<T>::Update(T & /*unit*/, const uint32 & /*diff*/) +{ + return false; +} + +template<> +bool +WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff) +{ + if (!&unit) + return true; + + if (!path_id) + return false; + + // Waypoint movement can be switched on/off + // This is quite handy for escort quests and other stuff + if (unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED)) + return true; + + // Clear the generator if the path doesn't exist + if (!waypoints || !waypoints->size()) + return false; + + Traveller<Creature> traveller(unit); + + i_nextMoveTime.Update(diff); + i_destinationHolder.UpdateTraveller(traveller, diff, true); + + if (i_nextMoveTime.GetExpiry() < TIMEDIFF_NEXT_WP) + { + if (unit.IsStopped()) + { + if (StopedByPlayer) + { + assert(node); + InitTraveller(unit, *node); + i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z); + i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); + StopedByPlayer = false; + return true; + } + + if (i_currentNode == waypoints->size() - 1) // If that's our last waypoint + { + if (repeating) // If the movement is repeating + i_currentNode = 0; // Start moving all over again + else + { + unit.SetHomePosition(node->x, node->y, node->z, unit.GetOrientation()); + unit.GetMotionMaster()->Initialize(); + return false; // Clear the waypoint movement + } + } + else + ++i_currentNode; + + node = waypoints->at(i_currentNode); + InitTraveller(unit, *node); + i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z); + i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); + + //Call for creature group update + if (unit.GetFormation() && unit.GetFormation()->getLeader() == &unit) + unit.GetFormation()->LeaderMoveTo(node->x, node->y, node->z); + } + else + { + //Determine waittime + if (node->delay) + i_nextMoveTime.Reset(node->delay); + + //note: disable "start" for mtmap + if (node->event_id && urand(0,99) < node->event_chance) + unit.GetMap()->ScriptsStart(sWaypointScripts, node->event_id, &unit, NULL/*, false*/); + + i_destinationHolder.ResetTravelTime(); + MovementInform(unit); + unit.UpdateWaypointID(i_currentNode); + unit.clearUnitState(UNIT_STAT_ROAMING); + unit.Relocate(node->x, node->y, node->z); + } + } + else + { + if (unit.IsStopped() && !i_destinationHolder.HasArrived()) + { + if (!StopedByPlayer) + { + i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER); + i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER); + StopedByPlayer = true; + } + } + } + return true; +} + +template void WaypointMovementGenerator<Player>::Initialize(Player &); +template bool WaypointMovementGenerator<Player>::Update(Player &, const uint32 &); +template void WaypointMovementGenerator<Player>::MovementInform(Player &); + +//----------------------------------------------------// +void FlightPathMovementGenerator::LoadPath(Player &) +{ + objmgr.GetTaxiPathNodes(i_pathId, i_path,i_mapIds); +} + +uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const +{ + if (i_currentNode >= i_mapIds.size()) + return i_mapIds.size(); + + uint32 curMapId = i_mapIds[i_currentNode]; + for (uint32 i = i_currentNode; i < i_mapIds.size(); ++i) + { + if (i_mapIds[i] != curMapId) + return i; + } + + return i_mapIds.size(); +} + +void FlightPathMovementGenerator::Initialize(Player &player) +{ + player.getHostileRefManager().setOnlineOfflineState(false); + player.addUnitState(UNIT_STAT_IN_FLIGHT); + player.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + LoadPath(player); + Traveller<Player> traveller(player); + // 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); + + player.SendMonsterMoveByPath(GetPath(), GetCurrentNode(), GetPathAtMapEnd()); + + // Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will + // be reinitialized for each flightmaster at the end of each spline (or stop) in the flight. + + uint32 nodeCount = i_mapIds.size(); // Get the number of nodes in the path. i_path and i_mapIds are the + // same size when loaded in ObjectMgr::GetTaxiPathNodes, called from LoadPath() + + m_endMapId = i_mapIds[nodeCount -1]; // Get the map ID from the last node + m_preloadTargetNode = nodeCount / 2; // Split the number of nodes in half to preload the flightmaster half-way through the flight + m_endGridX = i_path[nodeCount -1].x; // Get the X position from the last node + m_endGridY = i_path[nodeCount -1].y; // Get tye Y position from the last node +} + +void FlightPathMovementGenerator::Finalize(Player & player) +{ + player.clearUnitState(UNIT_STAT_IN_FLIGHT); + + float x, y, z; + i_destinationHolder.GetLocationNow(player.GetBaseMap(), x, y, z); + player.SetPosition(x, y, z, player.GetOrientation()); +} + +bool FlightPathMovementGenerator::Update(Player &player, const uint32 &diff) +{ + if (MovementInProgress()) + { + Traveller<Player> traveller(player); + if (i_destinationHolder.UpdateTraveller(traveller, diff)) + { + i_destinationHolder.ResetUpdate(FLIGHT_TRAVEL_UPDATE); + if (i_destinationHolder.HasArrived()) + { + uint32 curMap = i_mapIds[i_currentNode]; + ++i_currentNode; + if (MovementInProgress()) + { + DEBUG_LOG("loading node %u for player %s", i_currentNode, player.GetName()); + if (i_mapIds[i_currentNode] == 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); + } + + // check if it's time to preload the flightmaster grid at path end + if (i_currentNode == m_preloadTargetNode) + PreloadEndGrid(); + + return true; + } + //else HasArrived() + } + else + return true; + } + else + return true; + } + + // we have arrived at the end of the path + return false; +} + +void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport() +{ + if (i_mapIds.empty()) + return; + + uint32 map0 = i_mapIds[0]; + for (size_t i = 1; i < i_mapIds.size(); ++i) + { + if (i_mapIds[i] != map0) + { + i_currentNode = i; + return; + } + } +} + +void FlightPathMovementGenerator::PreloadEndGrid() +{ + // used to preload the final grid where the flightmaster is + Map *endMap = MapManager::Instance().FindMap(m_endMapId); + + // Load the grid + if (endMap) + { + sLog.outDetail("Preloading flightmaster at grid (%f, %f) for map %u", m_endGridX, m_endGridY, m_endMapId); + endMap->LoadGrid(m_endGridX, m_endGridY); + } + else + sLog.outDetail("Unable to determine map to preload flightmaster grid"); +} + +// +// Unique1's ASTAR Pathfinding Code... For future use & reference... +// + +#ifdef __PATHFINDING__ + +int GetFCost(int to, int num, int parentNum, float *gcost); // Below... + +int ShortenASTARRoute(short int *pathlist, int number) +{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1 + short int temppathlist[MAX_PATHLIST_NODES]; + int count = 0; + // int count2 = 0; + int temp, temp2; + int link; + int upto = 0; + + for (temp = number; temp >= 0; temp--) + { + qboolean shortened = qfalse; + + for (temp2 = 0; temp2 < temp; temp2++) + { + for (link = 0; link < nodes[pathlist[temp]].enodenum; link++) + { + if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED) + continue; + + //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it + // continue; + + //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32) + // continue; + + if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2]) + { // Found a shorter route... + //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1)) + { + temppathlist[count] = pathlist[temp2]; + temp = temp2; + ++count; + shortened = qtrue; + } + } + } + } + + if (!shortened) + { + temppathlist[count] = pathlist[temp]; + ++count; + } + } + + upto = count; + + for (temp = 0; temp < count; temp++) + { + pathlist[temp] = temppathlist[upto]; + --upto; + } + + G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count); + return count; +} + +/* +=========================================================================== +CreatePathAStar +This function uses the A* pathfinding algorithm to determine the +shortest path between any two nodes. +It's fairly complex, so I'm not really going to explain it much. +Look up A* and binary heaps for more info. +pathlist stores the ideal path between the nodes, in reverse order, +and the return value is the number of nodes in that path +=========================================================================== +*/ +int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist) +{ + //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES + //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it + short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index + float gcost[MAX_NODES]; + int fcost[MAX_NODES]; + char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type + short int parent[MAX_NODES]; + + short int numOpen = 0; + short int atNode, temp, newnode=-1; + qboolean found = qfalse; + int count = -1; + float gc; + int i, u, v, m; + vec3_t vec; + + //clear out all the arrays + memset(openlist, 0, sizeof(short int)*(MAX_NODES+1)); + memset(fcost, 0, sizeof(int)*MAX_NODES); + memset(list, 0, sizeof(char)*MAX_NODES); + memset(parent, 0, sizeof(short int)*MAX_NODES); + memset(gcost, -1, sizeof(float)*MAX_NODES); + + //make sure we have valid data before calculating everything + if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to)) + return -1; + + openlist[1] = from; //add the starting node to the open list + ++numOpen; + gcost[from] = 0; //its f and g costs are obviously 0 + fcost[from] = 0; + + while (1) + { + if (numOpen != 0) //if there are still items in the open list + { + //pop the top item off of the list + atNode = openlist[1]; + list[atNode] = 2; //put the node on the closed list so we don't check it again + --numOpen; + + openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position + v = 1; + + //this while loop reorders the list so that the new lowest fcost is at the top again + while (1) + { + u = v; + if ((2*u+1) < numOpen) //if both children exist + { + if (fcost[openlist[u]] >= fcost[openlist[2*u]]) + v = 2*u; + if (fcost[openlist[v]] >= fcost[openlist[2*u+1]]) + v = 2*u+1; + } + else + { + if ((2*u) < numOpen) //if only one child exists + { + if (fcost[openlist[u]] >= fcost[openlist[2*u]]) + v = 2*u; + } + } + + if (u != v) //if they're out of order, swap this item with its parent + { + temp = openlist[u]; + openlist[u] = openlist[v]; + openlist[v] = temp; + } + else + break; + } + + for (i = 0; i < nodes[atNode].enodenum; ++i) //loop through all the links for this node + { + newnode = nodes[atNode].links[i].targetNode; + + //if this path is blocked, skip it + if (nodes[atNode].links[i].flags & PATH_BLOCKED) + continue; + //if this path is blocked, skip it + if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS) + continue; + //skip any unreachable nodes + if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES)) + continue; + if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS)) + continue; + + if (list[newnode] == 2) //if this node is on the closed list, skip it + continue; + + if (list[newnode] != 1) //if this node is not already on the open list + { + openlist[++numOpen] = newnode; //add the new node to the open list + list[newnode] = 1; + parent[newnode] = atNode; //record the node's parent + + if (newnode == to) //if we've found the goal, don't keep computing paths! + break; //this will break the 'for' and go all the way to 'if (list[to] == 1)' + + //store it's f cost value + fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost); + + //this loop re-orders the heap so that the lowest fcost is at the top + m = numOpen; + while (m != 1) //while this item isn't at the top of the heap already + { + //if it has a lower fcost than its parent + if (fcost[openlist[m]] <= fcost[openlist[m/2]]) + { + temp = openlist[m/2]; + openlist[m/2] = openlist[m]; + openlist[m] = temp; //swap them + m /= 2; + } + else + break; + } + } + else //if this node is already on the open list + { + gc = gcost[atNode]; + VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec); + gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path + + if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before) + { + parent[newnode] = atNode; //set the new parent for this node + gcost[newnode] = gc; //and the new g cost + + for (i = 1; i < numOpen; ++i) //loop through all the items on the open list + { + if (openlist[i] == newnode) //find this node in the list + { + //calculate the new fcost and store it + fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost); + + //reorder the list again, with the lowest fcost item on top + m = i; + while (m != 1) + { + //if the item has a lower fcost than it's parent + if (fcost[openlist[m]] < fcost[openlist[m/2]]) + { + temp = openlist[m/2]; + openlist[m/2] = openlist[m]; + openlist[m] = temp; //swap them + m /= 2; + } + else + break; + } + break; //exit the 'for' loop because we already changed this node + } //if + } //for + } //if (gc < gcost[newnode]) + } //if (list[newnode] != 1) --> else + } //for (loop through links) + } //if (numOpen != 0) + else + { + found = qfalse; //there is no path between these nodes + break; + } + + if (list[to] == 1) //if the destination node is on the open list, we're done + { + found = qtrue; + break; + } + } //while (1) + + if (found == qtrue) //if we found a path + { + //G_Printf("%s - path found!n", bot->client->pers.netname); + count = 0; + + temp = to; //start at the end point + while (temp != from) //travel along the path (backwards) until we reach the starting point + { + pathlist[count++] = temp; //add the node to the pathlist and increment the count + temp = parent[temp]; //move to the parent of this node to continue the path + } + + pathlist[count++] = from; //add the beginning node to the end of the pathlist + + #ifdef __BOT_SHORTEN_ROUTING__ + count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1 + #endif //__BOT_SHORTEN_ROUTING__ + } + else + { + //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to); + count = CreateDumbRoute(from, to, pathlist); + + if (count > 0) + { + #ifdef __BOT_SHORTEN_ROUTING__ + count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1 + #endif //__BOT_SHORTEN_ROUTING__ + return count; + } + } + + return count; //return the number of nodes in the path, -1 if not found +} + +/* +=========================================================================== +GetFCost +Utility function used by A* pathfinding to calculate the +cost to move between nodes towards a goal. Using the A* +algorithm F = G + H, G here is the distance along the node +paths the bot must travel, and H is the straight-line distance +to the goal node. +Returned as an int because more precision is unnecessary and it +will slightly speed up heap access +=========================================================================== +*/ +int GetFCost(int to, int num, int parentNum, float *gcost) +{ + float gc = 0; + float hc = 0; + vec3_t v; + + if (gcost[num] == -1) + { + if (parentNum != -1) + { + gc = gcost[parentNum]; + VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v); + gc += VectorLength(v); + } + gcost[num] = gc; + } + else + gc = gcost[num]; + + VectorSubtract(nodes[to].origin, nodes[num].origin, v); + hc = VectorLength(v); + + return (int)(gc + hc); +} +#endif //__PATHFINDING__ + + |