2 * Copyright (C) 2005-2010 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"
41 #include "WorldPacket.h"
45 //-----------------------------------------------//
46 void WaypointMovementGenerator
<Creature
>::LoadPath(Creature
&c
)
48 sLog
.outDetail("LoadPath: loading waypoint path for creature %u, %u", c
.GetGUIDLow(), c
.GetDBTableGUIDLow());
50 i_path
= sWaypointMgr
.GetPath(c
.GetDBTableGUIDLow());
54 sLog
.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path",
55 c
.GetName(), c
.GetEntry(), c
.GetDBTableGUIDLow());
59 uint32 node_count
= i_path
->size();
60 i_hasDone
.resize(node_count
);
62 for(uint32 i
= 0; i
< node_count
-1; ++i
)
65 // to prevent a misbehavior inside "update"
66 // update is always called with the next wp - but the wpSys needs the current
67 // so when the routine is called the first time, wpSys gets the last waypoint
68 // and this prevents the system from performing text/emote, etc
69 i_hasDone
[node_count
- 1] = true;
72 void WaypointMovementGenerator
<Creature
>::ClearWaypoints()
77 void WaypointMovementGenerator
<Creature
>::Initialize( Creature
&u
)
79 i_nextMoveTime
.Reset(0); // TODO: check the lower bound (0 is probably too small)
82 u
.addUnitState(UNIT_STAT_ROAMING
|UNIT_STAT_ROAMING_MOVE
);
85 void WaypointMovementGenerator
<Creature
>::Finalize( Creature
&u
)
87 u
.clearUnitState(UNIT_STAT_ROAMING
|UNIT_STAT_ROAMING_MOVE
);
90 void WaypointMovementGenerator
<Creature
>::Interrupt( Creature
&u
)
92 u
.addUnitState(UNIT_STAT_ROAMING
|UNIT_STAT_ROAMING_MOVE
);
95 void WaypointMovementGenerator
<Creature
>::Reset( Creature
&u
)
98 b_StoppedByPlayer
= false;
99 i_nextMoveTime
.Reset(0);
100 u
.addUnitState(UNIT_STAT_ROAMING
|UNIT_STAT_ROAMING_MOVE
);
103 bool WaypointMovementGenerator
<Creature
>::Update(Creature
&creature
, const uint32
&diff
)
108 // Waypoint movement can be switched on/off
109 // This is quite handy for escort quests and other stuff
110 if (creature
.hasUnitState(UNIT_STAT_NOT_MOVE
))
112 creature
.clearUnitState(UNIT_STAT_ROAMING_MOVE
);
116 // prevent a crash at empty waypoint path.
117 if (!i_path
|| i_path
->empty())
119 creature
.clearUnitState(UNIT_STAT_ROAMING_MOVE
);
123 // i_path was modified by chat commands for example
124 if (i_path
->size() != i_hasDone
.size())
125 i_hasDone
.resize(i_path
->size());
127 if (i_currentNode
>= i_path
->size())
130 CreatureTraveller
traveller(creature
);
132 i_nextMoveTime
.Update(diff
);
133 i_destinationHolder
.UpdateTraveller(traveller
, diff
, false, true);
135 // creature has been stopped in middle of the waypoint segment
136 if (!i_destinationHolder
.HasArrived() && creature
.IsStopped())
138 // Timer has elapsed, meaning this part controlled it
139 if (i_nextMoveTime
.Passed())
141 SetStoppedByPlayer(false);
143 creature
.addUnitState(UNIT_STAT_ROAMING_MOVE
);
145 if (creature
.canFly())
146 creature
.AddSplineFlag(SPLINEFLAG_UNKNOWN7
);
148 // Now we re-set destination to same node and start travel
149 const WaypointNode
&node
= i_path
->at(i_currentNode
);
150 i_destinationHolder
.SetDestination(traveller
, node
.x
, node
.y
, node
.z
);
151 i_nextMoveTime
.Reset(i_destinationHolder
.GetTotalTravelTime());
153 else // if( !i_nextMoveTime.Passed())
155 // unexpected end of timer && creature stopped && not at end of segment
156 if (!IsStoppedByPlayer())
158 // Put 30 seconds delay
159 i_destinationHolder
.IncreaseTravelTime(STOP_TIME_FOR_PLAYER
);
160 i_nextMoveTime
.Reset(STOP_TIME_FOR_PLAYER
);
161 SetStoppedByPlayer(true); // Mark we did it
164 return true; // Abort here this update
167 if (creature
.IsStopped())
169 uint32 idx
= i_currentNode
> 0 ? i_currentNode
-1 : i_path
->size()-1;
173 if (i_path
->at(idx
).orientation
!= 100)
174 creature
.SetOrientation(i_path
->at(idx
).orientation
);
176 if (WaypointBehavior
*behavior
= i_path
->at(idx
).behavior
)
178 if (behavior
->emote
!= 0)
179 creature
.SetUInt32Value(UNIT_NPC_EMOTESTATE
, behavior
->emote
);
181 if (behavior
->spell
!= 0)
182 creature
.CastSpell(&creature
, behavior
->spell
, false);
184 if (behavior
->model1
!= 0)
185 creature
.SetDisplayId(behavior
->model1
);
187 if (behavior
->textid
[0])
189 // Not only one text is set
190 if (behavior
->textid
[1])
192 // Select one from max 5 texts (0 and 1 already checked)
194 for(; i
< MAX_WAYPOINT_TEXT
; ++i
)
196 if (!behavior
->textid
[i
])
200 creature
.Say(behavior
->textid
[rand() % i
], 0, 0);
203 creature
.Say(behavior
->textid
[0], 0, 0);
205 } // wpBehaviour found
207 i_hasDone
[idx
] = true;
208 MovementInform(creature
);
209 } // HasDone == false
210 } // i_creature.IsStopped()
212 // This is at the end of waypoint segment or has been stopped by player
213 if (i_nextMoveTime
.Passed())
215 // If stopped then begin a new move segment
216 if (creature
.IsStopped())
218 creature
.addUnitState(UNIT_STAT_ROAMING_MOVE
);
220 if (creature
.canFly())
221 creature
.AddSplineFlag(SPLINEFLAG_UNKNOWN7
);
223 const WaypointNode
&node
= i_path
->at(i_currentNode
);
224 i_destinationHolder
.SetDestination(traveller
, node
.x
, node
.y
, node
.z
);
225 i_nextMoveTime
.Reset(i_destinationHolder
.GetTotalTravelTime());
227 uint32 idx
= i_currentNode
> 0 ? i_currentNode
-1 : i_path
->size()-1;
229 if (i_path
->at(idx
).orientation
!= 100)
230 creature
.SetOrientation(i_path
->at(idx
).orientation
);
232 if (WaypointBehavior
*behavior
= i_path
->at(idx
).behavior
)
234 i_hasDone
[idx
] = false;
236 if (behavior
->model2
!= 0)
237 creature
.SetDisplayId(behavior
->model2
);
239 creature
.SetUInt32Value(UNIT_NPC_EMOTESTATE
, 0);
244 // If not stopped then stop it and set the reset of TimeTracker to waittime
245 creature
.StopMoving();
246 SetStoppedByPlayer(false);
248 i_nextMoveTime
.Reset(i_path
->at(i_currentNode
).delay
);
251 if (i_currentNode
>= i_path
->size())
258 void WaypointMovementGenerator
<Creature
>::MovementInform(Creature
&unit
)
261 unit
.AI()->MovementInform(WAYPOINT_MOTION_TYPE
, i_currentNode
);
264 //----------------------------------------------------//
265 void FlightPathMovementGenerator::LoadPath(Player
&)
267 sObjectMgr
.GetTaxiPathNodes(i_pathId
, i_path
,i_mapIds
);
270 uint32
FlightPathMovementGenerator::GetPathAtMapEnd() const
272 if(i_currentNode
>= i_mapIds
.size())
273 return i_mapIds
.size();
275 uint32 curMapId
= i_mapIds
[i_currentNode
];
276 for(uint32 i
= i_currentNode
; i
< i_mapIds
.size(); ++i
)
278 if(i_mapIds
[i
] != curMapId
)
282 return i_mapIds
.size();
285 void FlightPathMovementGenerator::Initialize(Player
&player
)
287 player
.getHostileRefManager().setOnlineOfflineState(false);
288 player
.addUnitState(UNIT_STAT_IN_FLIGHT
);
289 player
.SetFlag(UNIT_FIELD_FLAGS
,UNIT_FLAG_DISABLE_MOVE
| UNIT_FLAG_TAXI_FLIGHT
);
291 Traveller
<Player
> traveller(player
);
292 // do not send movement, it was sent already
293 i_destinationHolder
.SetDestination(traveller
, i_path
[i_currentNode
].x
, i_path
[i_currentNode
].y
, i_path
[i_currentNode
].z
, false);
295 player
.SendMonsterMoveByPath(GetPath(),GetCurrentNode(),GetPathAtMapEnd(), SplineFlags(SPLINEFLAG_WALKMODE
|SPLINEFLAG_FLYING
));
298 void FlightPathMovementGenerator::Finalize(Player
& player
)
300 // remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack)
301 player
.clearUnitState(UNIT_STAT_IN_FLIGHT
);
304 i_destinationHolder
.GetLocationNow(player
.GetBaseMap(), x
, y
, z
);
305 player
.SetPosition(x
, y
, z
, player
.GetOrientation());
308 player
.RemoveFlag(UNIT_FIELD_FLAGS
,UNIT_FLAG_DISABLE_MOVE
| UNIT_FLAG_TAXI_FLIGHT
);
310 if(player
.m_taxi
.empty())
312 player
.getHostileRefManager().setOnlineOfflineState(true);
313 if(player
.pvpInfo
.inHostileArea
)
314 player
.CastSpell(&player
, 2479, true);
316 // update z position to ground and orientation for landing point
317 // this prevent cheating with landing point at lags
318 // when client side flight end early in comparison server side
323 bool FlightPathMovementGenerator::Update(Player
&player
, const uint32
&diff
)
325 if( MovementInProgress() )
327 Traveller
<Player
> traveller(player
);
328 if( i_destinationHolder
.UpdateTraveller(traveller
, diff
, false) )
330 i_destinationHolder
.ResetUpdate(FLIGHT_TRAVEL_UPDATE
);
331 if( i_destinationHolder
.HasArrived() )
333 uint32 curMap
= i_mapIds
[i_currentNode
];
335 if( MovementInProgress() )
337 DEBUG_LOG("loading node %u for player %s", i_currentNode
, player
.GetName());
338 if(i_mapIds
[i_currentNode
]==curMap
)
340 // do not send movement, it was sent already
341 i_destinationHolder
.SetDestination(traveller
, i_path
[i_currentNode
].x
, i_path
[i_currentNode
].y
, i_path
[i_currentNode
].z
, false);
354 // we have arrived at the end of the path
358 void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
363 uint32 map0
= i_mapIds
[0];
364 for (size_t i
= 1; i
< i_mapIds
.size(); ++i
)
366 if(i_mapIds
[i
]!=map0
)
375 // Unique1's ASTAR Pathfinding Code... For future use & reference...
378 #ifdef __PATHFINDING__
380 int GetFCost(int to
, int num
, int parentNum
, float *gcost
); // Below...
382 int ShortenASTARRoute(short int *pathlist
, int number
)
383 { // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
384 short int temppathlist
[MAX_PATHLIST_NODES
];
391 for (temp
= number
; temp
>= 0; temp
--)
393 qboolean shortened
= qfalse
;
395 for (temp2
= 0; temp2
< temp
; temp2
++)
397 for (link
= 0; link
< nodes
[pathlist
[temp
]].enodenum
; link
++)
399 if (nodes
[pathlist
[temp
]].links
[link
].flags
& PATH_BLOCKED
)
402 //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
405 //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
408 if (nodes
[pathlist
[temp
]].links
[link
].targetNode
== pathlist
[temp2
])
409 { // Found a shorter route...
410 //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
412 temppathlist
[count
] = pathlist
[temp2
];
423 temppathlist
[count
] = pathlist
[temp
];
430 for (temp
= 0; temp
< count
; temp
++)
432 pathlist
[temp
] = temppathlist
[upto
];
436 G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number
, count
);
441 ===========================================================================
443 This function uses the A* pathfinding algorithm to determine the
444 shortest path between any two nodes.
445 It's fairly complex, so I'm not really going to explain it much.
446 Look up A* and binary heaps for more info.
447 pathlist stores the ideal path between the nodes, in reverse order,
448 and the return value is the number of nodes in that path
449 ===========================================================================
451 int CreatePathAStar(gentity_t
*bot
, int from
, int to
, short int *pathlist
)
453 //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
454 //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
455 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
456 float gcost
[MAX_NODES
];
457 int fcost
[MAX_NODES
];
458 char list
[MAX_NODES
]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
459 short int parent
[MAX_NODES
];
461 short int numOpen
= 0;
462 short int atNode
, temp
, newnode
=-1;
463 qboolean found
= qfalse
;
469 //clear out all the arrays
470 memset(openlist
, 0, sizeof(short int)*(MAX_NODES
+1));
471 memset(fcost
, 0, sizeof(int)*MAX_NODES
);
472 memset(list
, 0, sizeof(char)*MAX_NODES
);
473 memset(parent
, 0, sizeof(short int)*MAX_NODES
);
474 memset(gcost
, -1, sizeof(float)*MAX_NODES
);
476 //make sure we have valid data before calculating everything
477 if ((from
== NODE_INVALID
) || (to
== NODE_INVALID
) || (from
>= MAX_NODES
) || (to
>= MAX_NODES
) || (from
== to
))
480 openlist
[1] = from
; //add the starting node to the open list
482 gcost
[from
] = 0; //its f and g costs are obviously 0
487 if (numOpen
!= 0) //if there are still items in the open list
489 //pop the top item off of the list
490 atNode
= openlist
[1];
491 list
[atNode
] = 2; //put the node on the closed list so we don't check it again
494 openlist
[1] = openlist
[numOpen
+1]; //move the last item in the list to the top position
497 //this while loop reorders the list so that the new lowest fcost is at the top again
501 if ((2*u
+1) < numOpen
) //if both children exist
503 if (fcost
[openlist
[u
]] >= fcost
[openlist
[2*u
]])
505 if (fcost
[openlist
[v
]] >= fcost
[openlist
[2*u
+1]])
510 if ((2*u
) < numOpen
) //if only one child exists
512 if (fcost
[openlist
[u
]] >= fcost
[openlist
[2*u
]])
517 if (u
!= v
) //if they're out of order, swap this item with its parent
520 openlist
[u
] = openlist
[v
];
527 for (i
= 0; i
< nodes
[atNode
].enodenum
; ++i
) //loop through all the links for this node
529 newnode
= nodes
[atNode
].links
[i
].targetNode
;
531 //if this path is blocked, skip it
532 if (nodes
[atNode
].links
[i
].flags
& PATH_BLOCKED
)
534 //if this path is blocked, skip it
535 if (bot
->client
&& (bot
->client
->ps
.eFlags
& EF_TANK
) && nodes
[atNode
].links
[i
].flags
& PATH_NOTANKS
)
537 //skip any unreachable nodes
538 if (bot
->client
&& (nodes
[newnode
].type
& NODE_ALLY_UNREACHABLE
) && (bot
->client
->sess
.sessionTeam
== TEAM_ALLIES
))
540 if (bot
->client
&& (nodes
[newnode
].type
& NODE_AXIS_UNREACHABLE
) && (bot
->client
->sess
.sessionTeam
== TEAM_AXIS
))
543 if (list
[newnode
] == 2) //if this node is on the closed list, skip it
546 if (list
[newnode
] != 1) //if this node is not already on the open list
548 openlist
[++numOpen
] = newnode
; //add the new node to the open list
550 parent
[newnode
] = atNode
; //record the node's parent
552 if (newnode
== to
) //if we've found the goal, don't keep computing paths!
553 break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
555 //store it's f cost value
556 fcost
[newnode
] = GetFCost(to
, newnode
, parent
[newnode
], gcost
);
558 //this loop re-orders the heap so that the lowest fcost is at the top
560 while (m
!= 1) //while this item isn't at the top of the heap already
562 //if it has a lower fcost than its parent
563 if (fcost
[openlist
[m
]] <= fcost
[openlist
[m
/2]])
565 temp
= openlist
[m
/2];
566 openlist
[m
/2] = openlist
[m
];
567 openlist
[m
] = temp
; //swap them
574 else //if this node is already on the open list
577 VectorSubtract(nodes
[newnode
].origin
, nodes
[atNode
].origin
, vec
);
578 gc
+= VectorLength(vec
); //calculate what the gcost would be if we reached this node along the current path
580 if (gc
< gcost
[newnode
]) //if the new gcost is less (ie, this path is shorter than what we had before)
582 parent
[newnode
] = atNode
; //set the new parent for this node
583 gcost
[newnode
] = gc
; //and the new g cost
585 for (i
= 1; i
< numOpen
; ++i
) //loop through all the items on the open list
587 if (openlist
[i
] == newnode
) //find this node in the list
589 //calculate the new fcost and store it
590 fcost
[newnode
] = GetFCost(to
, newnode
, parent
[newnode
], gcost
);
592 //reorder the list again, with the lowest fcost item on top
596 //if the item has a lower fcost than it's parent
597 if (fcost
[openlist
[m
]] < fcost
[openlist
[m
/2]])
599 temp
= openlist
[m
/2];
600 openlist
[m
/2] = openlist
[m
];
601 openlist
[m
] = temp
; //swap them
607 break; //exit the 'for' loop because we already changed this node
610 } //if (gc < gcost[newnode])
611 } //if (list[newnode] != 1) --> else
612 } //for (loop through links)
613 } //if (numOpen != 0)
616 found
= qfalse
; //there is no path between these nodes
620 if (list
[to
] == 1) //if the destination node is on the open list, we're done
627 if (found
== qtrue
) //if we found a path
629 //G_Printf("%s - path found!n", bot->client->pers.netname);
632 temp
= to
; //start at the end point
633 while (temp
!= from
) //travel along the path (backwards) until we reach the starting point
635 pathlist
[count
++] = temp
; //add the node to the pathlist and increment the count
636 temp
= parent
[temp
]; //move to the parent of this node to continue the path
639 pathlist
[count
++] = from
; //add the beginning node to the end of the pathlist
641 #ifdef __BOT_SHORTEN_ROUTING__
642 count
= ShortenASTARRoute(pathlist
, count
); // This isn't working... Dunno why.. Unique1
643 #endif //__BOT_SHORTEN_ROUTING__
647 //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
648 count
= CreateDumbRoute(from
, to
, pathlist
);
652 #ifdef __BOT_SHORTEN_ROUTING__
653 count
= ShortenASTARRoute(pathlist
, count
); // This isn't working... Dunno why.. Unique1
654 #endif //__BOT_SHORTEN_ROUTING__
659 return count
; //return the number of nodes in the path, -1 if not found
663 ===========================================================================
665 Utility function used by A* pathfinding to calculate the
666 cost to move between nodes towards a goal. Using the A*
667 algorithm F = G + H, G here is the distance along the node
668 paths the bot must travel, and H is the straight-line distance
670 Returned as an int because more precision is unnecessary and it
671 will slightly speed up heap access
672 ===========================================================================
674 int GetFCost(int to
, int num
, int parentNum
, float *gcost
)
680 if (gcost
[num
] == -1)
684 gc
= gcost
[parentNum
];
685 VectorSubtract(nodes
[num
].origin
, nodes
[parentNum
].origin
, v
);
686 gc
+= VectorLength(v
);
693 VectorSubtract(nodes
[to
].origin
, nodes
[num
].origin
, v
);
694 hc
= VectorLength(v
);
696 return (int)(gc
+ hc
);
698 #endif //__PATHFINDING__