2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 creature_movement Table
22 alter table creature_movement add `textid1` int(11) NOT NULL default '0';
23 alter table creature_movement add `textid2` int(11) NOT NULL default '0';
24 alter table creature_movement add `textid3` int(11) NOT NULL default '0';
25 alter table creature_movement add `textid4` int(11) NOT NULL default '0';
26 alter table creature_movement add `textid5` int(11) NOT NULL default '0';
27 alter table creature_movement add `emote` int(10) unsigned default '0';
28 alter table creature_movement add `spell` int(5) unsigned default '0';
29 alter table creature_movement add `wpguid` int(11) default '0';
35 #include "WaypointMovementGenerator.h"
36 #include "ObjectMgr.h"
38 #include "DestinationHolderImp.h"
39 #include "CreatureAI.h"
40 #include "WaypointManager.h"
44 //-----------------------------------------------//
45 void WaypointMovementGenerator
<Creature
>::LoadPath(Creature
&c
)
47 sLog
.outDetail("LoadPath: loading waypoint path for creature %d,%d", c
.GetGUIDLow(), c
.GetDBTableGUIDLow());
49 i_path
= WaypointMgr
.GetPath(c
.GetDBTableGUIDLow());
52 sLog
.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %d) doesn't have waypoint path",
53 c
.GetName(), c
.GetEntry(), c
.GetDBTableGUIDLow());
57 uint32 node_count
= i_path
->size();
58 i_hasDone
.resize(node_count
);
59 for(uint32 i
= 0; i
< node_count
-1; ++i
)
62 // to prevent a misbehavior inside "update"
63 // update is always called with the next wp - but the wpSys needs the current
64 // so when the routine is called the first time, wpSys gets the last waypoint
65 // and this prevents the system from performing text/emote, etc
66 i_hasDone
[node_count
- 1] = true;
69 void WaypointMovementGenerator
<Creature
>::ClearWaypoints()
74 void WaypointMovementGenerator
<Creature
>::Initialize()
78 bool WaypointMovementGenerator
<Creature
>::Update(Creature
&creature
, const uint32
&diff
)
83 // Waypoint movement can be switched on/off
84 // This is quite handy for escort quests and other stuff
85 if(creature
.hasUnitState(UNIT_STAT_ROOT
| UNIT_STAT_STUNNED
| UNIT_STAT_DISTRACTED
))
88 // prevent a crash at empty waypoint path.
89 if(!i_path
|| i_path
->empty())
92 // i_path was modified by chat commands for example
93 if(i_path
->size() != i_hasDone
.size())
94 i_hasDone
.resize(i_path
->size());
95 if(i_currentNode
>= i_path
->size())
98 CreatureTraveller
traveller(creature
);
100 i_nextMoveTime
.Update(diff
);
101 i_destinationHolder
.UpdateTraveller(traveller
, diff
, false, true);
103 // creature has been stopped in middle of the waypoint segment
104 if (!i_destinationHolder
.HasArrived() && creature
.IsStopped())
106 if( i_nextMoveTime
.Passed()) // Timer has elapsed, meaning this part controlled it
108 SetStopedByPlayer(false);
109 // Now we re-set destination to same node and start travel
110 creature
.addUnitState(UNIT_STAT_ROAMING
);
111 if (creature
.canFly())
112 creature
.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2
);
113 const WaypointNode
&node
= i_path
->at(i_currentNode
);
114 i_destinationHolder
.SetDestination(traveller
, node
.x
, node
.y
, node
.z
);
115 i_nextMoveTime
.Reset(i_destinationHolder
.GetTotalTravelTime());
117 else // if( !i_nextMoveTime.Passed())
118 { // unexpected end of timer && creature stopped && not at end of segment
119 if (!IsStopedByPlayer())
120 { // Put 30 seconds delay
121 i_destinationHolder
.IncreaseTravelTime(STOP_TIME_FOR_PLAYER
);
122 i_nextMoveTime
.Reset(STOP_TIME_FOR_PLAYER
);
123 SetStopedByPlayer(true); // Mark we did it
126 return true; // Abort here this update
129 if( creature
.IsStopped())
131 uint32 idx
= i_currentNode
> 0 ? i_currentNode
-1 : i_path
->size()-1;
135 if (i_path
->at(idx
).orientation
!=100)
136 creature
.SetOrientation(i_path
->at(idx
).orientation
);
138 if(WaypointBehavior
*behavior
= i_path
->at(idx
).behavior
)
140 if(behavior
->emote
!= 0)
141 creature
.SetUInt32Value(UNIT_NPC_EMOTESTATE
,behavior
->emote
);
142 if(behavior
->spell
!= 0)
143 creature
.CastSpell(&creature
,behavior
->spell
, false);
144 if(behavior
->model1
!= 0)
145 creature
.SetDisplayId(behavior
->model1
);
146 if(behavior
->textid
[0])
148 // Not only one text is set
149 if( behavior
->textid
[1] )
151 // Select one from max 5 texts (0 and 1 laready checked)
153 for( ; i
< MAX_WAYPOINT_TEXT
; ++i
)
154 if( !behavior
->textid
[i
] )
157 creature
.Say(behavior
->textid
[rand() % i
], 0, 0);
160 creature
.Say(behavior
->textid
[0], 0, 0);
163 i_hasDone
[idx
] = true;
164 MovementInform(creature
);
165 } // wpBehaviour found
166 } // HasDone == false
167 } // i_creature.IsStopped()
169 if( i_nextMoveTime
.Passed() ) // This is at the end of waypoint segment or has been stopped by player
171 if( creature
.IsStopped() ) // If stopped then begin a new move segment
173 creature
.addUnitState(UNIT_STAT_ROAMING
);
174 if (creature
.canFly())
175 creature
.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2
);
176 const WaypointNode
&node
= i_path
->at(i_currentNode
);
177 i_destinationHolder
.SetDestination(traveller
, node
.x
, node
.y
, node
.z
);
178 i_nextMoveTime
.Reset(i_destinationHolder
.GetTotalTravelTime());
179 uint32 idx
= i_currentNode
> 0 ? i_currentNode
-1 : i_path
->size()-1;
181 if (i_path
->at(idx
).orientation
!=100)
182 creature
.SetOrientation(i_path
->at(idx
).orientation
);
184 if(WaypointBehavior
*behavior
= i_path
->at(idx
).behavior
)
186 i_hasDone
[idx
] = false;
187 if(behavior
->model2
!= 0)
188 creature
.SetDisplayId(behavior
->model2
);
190 creature
.SetUInt32Value(UNIT_NPC_EMOTESTATE
, 0);
193 else // If not stopped then stop it and set the reset of TimeTracker to waittime
195 creature
.StopMoving();
196 SetStopedByPlayer(false);
197 i_nextMoveTime
.Reset(i_path
->at(i_currentNode
).delay
);
199 if( i_currentNode
>= i_path
->size() )
206 void WaypointMovementGenerator
<Creature
>::MovementInform(Creature
&unit
)
209 unit
.AI()->MovementInform(WAYPOINT_MOTION_TYPE
, i_currentNode
);
212 //----------------------------------------------------//
213 void FlightPathMovementGenerator::LoadPath(Player
&)
215 objmgr
.GetTaxiPathNodes(i_pathId
, i_path
,i_mapIds
);
218 uint32
FlightPathMovementGenerator::GetPathAtMapEnd() const
220 if(i_currentNode
>= i_mapIds
.size())
221 return i_mapIds
.size();
223 uint32 curMapId
= i_mapIds
[i_currentNode
];
224 for(uint32 i
= i_currentNode
; i
< i_mapIds
.size(); ++i
)
226 if(i_mapIds
[i
] != curMapId
)
230 return i_mapIds
.size();
233 void FlightPathMovementGenerator::Initialize(Player
&player
)
235 player
.getHostilRefManager().setOnlineOfflineState(false);
236 player
.addUnitState(UNIT_STAT_IN_FLIGHT
);
237 player
.SetFlag(UNIT_FIELD_FLAGS
,UNIT_FLAG_DISABLE_MOVE
| UNIT_FLAG_TAXI_FLIGHT
);
239 Traveller
<Player
> traveller(player
);
240 // do not send movement, it was sent already
241 i_destinationHolder
.SetDestination(traveller
, i_path
[i_currentNode
].x
, i_path
[i_currentNode
].y
, i_path
[i_currentNode
].z
, false);
243 player
.SendMonsterMoveByPath(GetPath(),GetCurrentNode(),GetPathAtMapEnd(),MOVEMENTFLAG_WALK_MODE
|MOVEMENTFLAG_ONTRANSPORT
);
246 void FlightPathMovementGenerator::Finalize(Player
& player
)
248 // remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack)
249 player
.clearUnitState(UNIT_STAT_IN_FLIGHT
);
252 i_destinationHolder
.GetLocationNow(player
.GetMapId(), x
, y
, z
);
253 player
.SetPosition(x
, y
, z
, player
.GetOrientation());
256 player
.RemoveFlag(UNIT_FIELD_FLAGS
,UNIT_FLAG_DISABLE_MOVE
| UNIT_FLAG_TAXI_FLIGHT
);
258 if(player
.m_taxi
.empty())
260 player
.getHostilRefManager().setOnlineOfflineState(true);
261 if(player
.pvpInfo
.inHostileArea
)
262 player
.CastSpell(&player
, 2479, true);
264 player
.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE
);
269 bool FlightPathMovementGenerator::Update(Player
&player
, const uint32
&diff
)
271 if( MovementInProgress() )
273 Traveller
<Player
> traveller(player
);
274 if( i_destinationHolder
.UpdateTraveller(traveller
, diff
, false) )
276 i_destinationHolder
.ResetUpdate(FLIGHT_TRAVEL_UPDATE
);
277 if( i_destinationHolder
.HasArrived() )
279 uint32 curMap
= i_mapIds
[i_currentNode
];
281 if( MovementInProgress() )
283 DEBUG_LOG("loading node %u for player %s", i_currentNode
, player
.GetName());
284 if(i_mapIds
[i_currentNode
]==curMap
)
286 // do not send movement, it was sent already
287 i_destinationHolder
.SetDestination(traveller
, i_path
[i_currentNode
].x
, i_path
[i_currentNode
].y
, i_path
[i_currentNode
].z
, false);
300 // we have arrived at the end of the path
304 void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
309 uint32 map0
= i_mapIds
[0];
310 for(int i
= 1; i
< i_mapIds
.size(); ++i
)
312 if(i_mapIds
[i
]!=map0
)
321 // Unique1's ASTAR Pathfinding Code... For future use & reference...
324 #ifdef __PATHFINDING__
326 int GetFCost(int to
, int num
, int parentNum
, float *gcost
); // Below...
328 int ShortenASTARRoute(short int *pathlist
, int number
)
329 { // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
330 short int temppathlist
[MAX_PATHLIST_NODES
];
337 for (temp
= number
; temp
>= 0; temp
--)
339 qboolean shortened
= qfalse
;
341 for (temp2
= 0; temp2
< temp
; temp2
++)
343 for (link
= 0; link
< nodes
[pathlist
[temp
]].enodenum
; link
++)
345 if (nodes
[pathlist
[temp
]].links
[link
].flags
& PATH_BLOCKED
)
348 //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
351 //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
354 if (nodes
[pathlist
[temp
]].links
[link
].targetNode
== pathlist
[temp2
])
355 { // Found a shorter route...
356 //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
358 temppathlist
[count
] = pathlist
[temp2
];
369 temppathlist
[count
] = pathlist
[temp
];
376 for (temp
= 0; temp
< count
; temp
++)
378 pathlist
[temp
] = temppathlist
[upto
];
382 G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number
, count
);
387 ===========================================================================
389 This function uses the A* pathfinding algorithm to determine the
390 shortest path between any two nodes.
391 It's fairly complex, so I'm not really going to explain it much.
392 Look up A* and binary heaps for more info.
393 pathlist stores the ideal path between the nodes, in reverse order,
394 and the return value is the number of nodes in that path
395 ===========================================================================
397 int CreatePathAStar(gentity_t
*bot
, int from
, int to
, short int *pathlist
)
399 //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
400 //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
401 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
402 float gcost
[MAX_NODES
];
403 int fcost
[MAX_NODES
];
404 char list
[MAX_NODES
]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
405 short int parent
[MAX_NODES
];
407 short int numOpen
= 0;
408 short int atNode
, temp
, newnode
=-1;
409 qboolean found
= qfalse
;
415 //clear out all the arrays
416 memset(openlist
, 0, sizeof(short int)*(MAX_NODES
+1));
417 memset(fcost
, 0, sizeof(int)*MAX_NODES
);
418 memset(list
, 0, sizeof(char)*MAX_NODES
);
419 memset(parent
, 0, sizeof(short int)*MAX_NODES
);
420 memset(gcost
, -1, sizeof(float)*MAX_NODES
);
422 //make sure we have valid data before calculating everything
423 if ((from
== NODE_INVALID
) || (to
== NODE_INVALID
) || (from
>= MAX_NODES
) || (to
>= MAX_NODES
) || (from
== to
))
426 openlist
[1] = from
; //add the starting node to the open list
428 gcost
[from
] = 0; //its f and g costs are obviously 0
433 if (numOpen
!= 0) //if there are still items in the open list
435 //pop the top item off of the list
436 atNode
= openlist
[1];
437 list
[atNode
] = 2; //put the node on the closed list so we don't check it again
440 openlist
[1] = openlist
[numOpen
+1]; //move the last item in the list to the top position
443 //this while loop reorders the list so that the new lowest fcost is at the top again
447 if ((2*u
+1) < numOpen
) //if both children exist
449 if (fcost
[openlist
[u
]] >= fcost
[openlist
[2*u
]])
451 if (fcost
[openlist
[v
]] >= fcost
[openlist
[2*u
+1]])
456 if ((2*u
) < numOpen
) //if only one child exists
458 if (fcost
[openlist
[u
]] >= fcost
[openlist
[2*u
]])
463 if (u
!= v
) //if they're out of order, swap this item with its parent
466 openlist
[u
] = openlist
[v
];
473 for (i
= 0; i
< nodes
[atNode
].enodenum
; ++i
) //loop through all the links for this node
475 newnode
= nodes
[atNode
].links
[i
].targetNode
;
477 //if this path is blocked, skip it
478 if (nodes
[atNode
].links
[i
].flags
& PATH_BLOCKED
)
480 //if this path is blocked, skip it
481 if (bot
->client
&& (bot
->client
->ps
.eFlags
& EF_TANK
) && nodes
[atNode
].links
[i
].flags
& PATH_NOTANKS
)
483 //skip any unreachable nodes
484 if (bot
->client
&& (nodes
[newnode
].type
& NODE_ALLY_UNREACHABLE
) && (bot
->client
->sess
.sessionTeam
== TEAM_ALLIES
))
486 if (bot
->client
&& (nodes
[newnode
].type
& NODE_AXIS_UNREACHABLE
) && (bot
->client
->sess
.sessionTeam
== TEAM_AXIS
))
489 if (list
[newnode
] == 2) //if this node is on the closed list, skip it
492 if (list
[newnode
] != 1) //if this node is not already on the open list
494 openlist
[++numOpen
] = newnode
; //add the new node to the open list
496 parent
[newnode
] = atNode
; //record the node's parent
498 if (newnode
== to
) //if we've found the goal, don't keep computing paths!
499 break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
501 //store it's f cost value
502 fcost
[newnode
] = GetFCost(to
, newnode
, parent
[newnode
], gcost
);
504 //this loop re-orders the heap so that the lowest fcost is at the top
506 while (m
!= 1) //while this item isn't at the top of the heap already
508 //if it has a lower fcost than its parent
509 if (fcost
[openlist
[m
]] <= fcost
[openlist
[m
/2]])
511 temp
= openlist
[m
/2];
512 openlist
[m
/2] = openlist
[m
];
513 openlist
[m
] = temp
; //swap them
520 else //if this node is already on the open list
523 VectorSubtract(nodes
[newnode
].origin
, nodes
[atNode
].origin
, vec
);
524 gc
+= VectorLength(vec
); //calculate what the gcost would be if we reached this node along the current path
526 if (gc
< gcost
[newnode
]) //if the new gcost is less (ie, this path is shorter than what we had before)
528 parent
[newnode
] = atNode
; //set the new parent for this node
529 gcost
[newnode
] = gc
; //and the new g cost
531 for (i
= 1; i
< numOpen
; ++i
) //loop through all the items on the open list
533 if (openlist
[i
] == newnode
) //find this node in the list
535 //calculate the new fcost and store it
536 fcost
[newnode
] = GetFCost(to
, newnode
, parent
[newnode
], gcost
);
538 //reorder the list again, with the lowest fcost item on top
542 //if the item has a lower fcost than it's parent
543 if (fcost
[openlist
[m
]] < fcost
[openlist
[m
/2]])
545 temp
= openlist
[m
/2];
546 openlist
[m
/2] = openlist
[m
];
547 openlist
[m
] = temp
; //swap them
553 break; //exit the 'for' loop because we already changed this node
556 } //if (gc < gcost[newnode])
557 } //if (list[newnode] != 1) --> else
558 } //for (loop through links)
559 } //if (numOpen != 0)
562 found
= qfalse
; //there is no path between these nodes
566 if (list
[to
] == 1) //if the destination node is on the open list, we're done
573 if (found
== qtrue
) //if we found a path
575 //G_Printf("%s - path found!n", bot->client->pers.netname);
578 temp
= to
; //start at the end point
579 while (temp
!= from
) //travel along the path (backwards) until we reach the starting point
581 pathlist
[count
++] = temp
; //add the node to the pathlist and increment the count
582 temp
= parent
[temp
]; //move to the parent of this node to continue the path
585 pathlist
[count
++] = from
; //add the beginning node to the end of the pathlist
587 #ifdef __BOT_SHORTEN_ROUTING__
588 count
= ShortenASTARRoute(pathlist
, count
); // This isn't working... Dunno why.. Unique1
589 #endif //__BOT_SHORTEN_ROUTING__
593 //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
594 count
= CreateDumbRoute(from
, to
, pathlist
);
598 #ifdef __BOT_SHORTEN_ROUTING__
599 count
= ShortenASTARRoute(pathlist
, count
); // This isn't working... Dunno why.. Unique1
600 #endif //__BOT_SHORTEN_ROUTING__
605 return count
; //return the number of nodes in the path, -1 if not found
609 ===========================================================================
611 Utility function used by A* pathfinding to calculate the
612 cost to move between nodes towards a goal. Using the A*
613 algorithm F = G + H, G here is the distance along the node
614 paths the bot must travel, and H is the straight-line distance
616 Returned as an int because more precision is unnecessary and it
617 will slightly speed up heap access
618 ===========================================================================
620 int GetFCost(int to
, int num
, int parentNum
, float *gcost
)
626 if (gcost
[num
] == -1)
630 gc
= gcost
[parentNum
];
631 VectorSubtract(nodes
[num
].origin
, nodes
[parentNum
].origin
, v
);
632 gc
+= VectorLength(v
);
639 VectorSubtract(nodes
[to
].origin
, nodes
[num
].origin
, v
);
640 hc
= VectorLength(v
);
642 return (int)(gc
+ hc
);
644 #endif //__PATHFINDING__