root/trunk/src/game/WaypointMovementGenerator.cpp @ 37

Revision 37, 24.9 kB (checked in by yumileroy, 17 years ago)

[svn] * svn:eol-style native set on all files that need it

Original author: Neo2003
Date: 2008-10-11 14:16:25-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
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.
8 *
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.
13 *
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
17 */
18
19/*
20creature_movement Table
21
22alter table creature_movement add `text1` varchar(255) default NULL;
23alter table creature_movement add `text2` varchar(255) default NULL;
24alter table creature_movement add `text3` varchar(255) default NULL;
25alter table creature_movement add `text4` varchar(255) default NULL;
26alter table creature_movement add `text5` varchar(255) default NULL;
27alter table creature_movement add `emote` int(10) unsigned default '0';
28alter table creature_movement add `spell` int(5) unsigned default '0';
29alter table creature_movement add `wpguid` int(11) default '0';
30
31*/
32
33#include <ctime>
34
35#include "WaypointMovementGenerator.h"
36#include "ObjectMgr.h"
37#include "Creature.h"
38#include "DestinationHolderImp.h"
39#include "CreatureAI.h"
40#include "WaypointManager.h"
41
42#include <cassert>
43
44//-----------------------------------------------//
45void
46WaypointMovementGenerator<Creature>::LoadPath(Creature &c)
47{
48    sLog.outDetail("LoadPath: loading waypoint path for creature %d,%d", c.GetGUIDLow(), c.GetDBTableGUIDLow());
49
50    i_path = WaypointMgr.GetPath(c.GetDBTableGUIDLow());
51    if(!i_path)
52    {
53        sLog.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s(%d) doesn't have waypoint path", c.GetName(), c.GetDBTableGUIDLow());
54        return;
55    }
56
57    uint32 node_count = i_path->size();
58    i_hasDone.resize(node_count);
59    for(uint32 i = 0; i < node_count-1; i++)
60        i_hasDone[i] = false;
61
62    // to prevent a misbehaviour 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;
67}
68
69void
70WaypointMovementGenerator<Creature>::ClearWaypoints()
71{
72    i_path = NULL;
73}
74
75void
76WaypointMovementGenerator<Creature>::Initialize()
77{
78}
79
80bool
81WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
82{
83    if(!&creature)
84        return true;
85
86    // Waypoint movement can be switched on/off
87    // This is quite handy for escort quests and other stuff
88    if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
89        return true;
90
91    // prevent a crash at empty waypoint path.
92    if(!i_path || i_path->empty())
93        return true;
94
95    // i_path was modified by chat commands for example
96    if(i_path->size() != i_hasDone.size())
97        i_hasDone.resize(i_path->size());
98    if(i_currentNode >= i_path->size())
99        i_currentNode = 0;
100
101    CreatureTraveller traveller(creature);
102
103    i_nextMoveTime.Update(diff);
104    i_destinationHolder.UpdateTraveller(traveller, diff, false, true);
105
106    // creature has been stoped in middle of the waypoint segment
107    if (!i_destinationHolder.HasArrived() && creature.IsStopped())
108    {
109        if( i_nextMoveTime.Passed()) // Timer has elapsed, meaning this part controlled it
110        {
111            SetStopedByPlayer(false);
112            // Now we re-set destination to same node and start travel
113            creature.addUnitState(UNIT_STAT_ROAMING);
114            if (creature.canFly())
115                creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
116            const WaypointNode &node = i_path->at(i_currentNode);
117            i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
118            i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
119        }
120        else // if( !i_nextMoveTime.Passed())
121        { // unexpected end of timer && creature stopped && not at end of segment
122            if (!IsStopedByPlayer())
123            {                                                   // Put 30 seconds delay
124                i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER);
125                i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER);
126                SetStopedByPlayer(true);                        // Mark we did it
127            }
128        }
129        return true;    // Abort here this update
130    }
131
132    if( creature.IsStopped())
133    {
134        uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1;
135
136        if (!i_hasDone[idx])
137        {
138            if (i_path->at(idx).orientation !=100)
139                creature.SetOrientation(i_path->at(idx).orientation);
140
141            if(WaypointBehavior *behavior = i_path->at(idx).behavior)
142            {
143                if(behavior->emote != 0)
144                    creature.SetUInt32Value(UNIT_NPC_EMOTESTATE,behavior->emote);
145                if(behavior->spell != 0)
146                    creature.CastSpell(&creature,behavior->spell, false);
147                if(behavior->model1 != 0)
148                    creature.SetDisplayId(behavior->model1);
149                if(!behavior->text[0].empty())
150                {
151                    // Only one text is set
152                    if( !behavior->text[1].empty() )
153                    {
154                        // Select one from max 5 texts (0 and 1 laready checked)
155                        int i = 2;
156                        for( ; i < 5; ++i )
157                            if( behavior->text[i].empty() )
158                                break;
159
160                        creature.Say(behavior->text[rand() % i].c_str(), 0, 0);
161
162                    }
163                    else
164                        creature.Say(behavior->text[0].c_str(), 0, 0);
165                }
166
167                i_hasDone[idx] = true;
168                MovementInform(creature);
169            }                                               // wpBehaviour found
170        }                                                   // HasDone == false
171    }                                                       // i_creature.IsStopped()
172
173    if( i_nextMoveTime.Passed() ) // This is at the end of waypoint segment or has been stopped by player
174    {
175        if( creature.IsStopped() ) // If stopped then begin a new move segment
176        {
177            creature.addUnitState(UNIT_STAT_ROAMING);
178            if (creature.canFly())
179                creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
180            const WaypointNode &node = i_path->at(i_currentNode);
181            i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
182            i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
183            uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1;
184
185            if (i_path->at(idx).orientation !=100)
186                creature.SetOrientation(i_path->at(idx).orientation);
187
188            if(WaypointBehavior *behavior = i_path->at(idx).behavior )
189            {
190                i_hasDone[idx] = false;
191                if(behavior->model2 != 0)
192                    creature.SetDisplayId(behavior->model2);
193
194                creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
195            }
196        }
197        else // If not stopped then stop it and set the reset of TimeTracker to waittime
198        {
199            creature.StopMoving();
200            SetStopedByPlayer(false);
201            i_nextMoveTime.Reset(i_path->at(i_currentNode).delay);
202            ++i_currentNode;
203            if( i_currentNode >= i_path->size() )
204                i_currentNode = 0;
205        }
206    }
207    return true;
208}
209
210void WaypointMovementGenerator<Creature>::MovementInform(Creature &unit)
211{
212    if(unit.AI())
213        unit.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
214}
215
216//----------------------------------------------------//
217void
218FlightPathMovementGenerator::LoadPath(Player &)
219{
220    objmgr.GetTaxiPathNodes(i_pathId, i_path,i_mapIds);
221}
222
223uint32
224FlightPathMovementGenerator::GetPathAtMapEnd() const
225{
226    if(i_currentNode >= i_mapIds.size())
227        return i_mapIds.size();
228
229    uint32 curMapId = i_mapIds[i_currentNode];
230    for(uint32 i = i_currentNode; i < i_mapIds.size(); ++i)
231    {
232        if(i_mapIds[i] != curMapId)
233            return i;
234    }
235
236    return i_mapIds.size();
237}
238
239void
240FlightPathMovementGenerator::Initialize(Player &player)
241{
242    player.getHostilRefManager().setOnlineOfflineState(false);
243    player.addUnitState(UNIT_STAT_IN_FLIGHT);
244    player.SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
245    LoadPath(player);
246    Traveller<Player> traveller(player);
247    // do not send movement, it was sent already
248    i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
249
250    player.SendMonsterMoveByPath(GetPath(),GetCurrentNode(),GetPathAtMapEnd(),MOVEMENTFLAG_WALK_MODE|MOVEMENTFLAG_ONTRANSPORT);
251}
252
253void FlightPathMovementGenerator::Finalize(Player & player)
254{
255
256    float x, y, z;
257    i_destinationHolder.GetLocationNow(player.GetMapId(), x, y, z);
258    player.SetPosition(x, y, z, player.GetOrientation());
259
260    player.clearUnitState(UNIT_STAT_IN_FLIGHT);
261    player.Unmount();
262    player.RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
263
264    if(player.m_taxi.empty())
265    {
266        player.getHostilRefManager().setOnlineOfflineState(true);
267        if(player.pvpInfo.inHostileArea)
268            player.CastSpell(&player, 2479, true);
269
270        player.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
271        player.StopMoving();
272    }
273}
274
275bool
276FlightPathMovementGenerator::Update(Player &player, const uint32 &diff)
277{
278    if( MovementInProgress() )
279    {
280        Traveller<Player> traveller(player);
281        if( i_destinationHolder.UpdateTraveller(traveller, diff, false) )
282        {
283            i_destinationHolder.ResetUpdate(FLIGHT_TRAVEL_UPDATE);
284            if( i_destinationHolder.HasArrived() )
285            {
286                uint32 curMap = i_mapIds[i_currentNode];
287                ++i_currentNode;
288                if( MovementInProgress() )
289                {
290                    DEBUG_LOG("loading node %u for player %s", i_currentNode, player.GetName());
291                    if(i_mapIds[i_currentNode]==curMap)
292                    {
293                        // do not send movement, it was sent already
294                        i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
295                    }
296                    return true;
297                }
298                //else HasArrived()
299            }
300            else
301                return true;
302        }
303        else
304            return true;
305    }
306
307    // we have arrived at the end of the path
308    return false;
309}
310
311void
312FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
313{
314    if(i_mapIds.empty())
315        return;
316
317    uint32 map0 = i_mapIds[0];
318    for(int i = 1; i < i_mapIds.size(); ++i)
319    {
320        if(i_mapIds[i]!=map0)
321        {
322            i_currentNode = i;
323            return;
324        }
325    }
326}
327
328//
329// Unique1's ASTAR Pathfinding Code... For future use & reference...
330//
331
332#ifdef __PATHFINDING__
333
334int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
335
336int ShortenASTARRoute(short int *pathlist, int number)
337{                                                           // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
338    short int temppathlist[MAX_PATHLIST_NODES];
339    int count = 0;
340    //    int count2 = 0;
341    int temp, temp2;
342    int link;
343    int upto = 0;
344
345    for (temp = number; temp >= 0; temp--)
346    {
347        qboolean shortened = qfalse;
348
349        for (temp2 = 0; temp2 < temp; temp2++)
350        {
351            for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
352            {
353                if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
354                    continue;
355
356                //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS)    //if this path is blocked, skip it
357                //    continue;
358
359                //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
360                //    continue;
361
362                if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
363                {                                           // Found a shorter route...
364                    //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
365                    {
366                        temppathlist[count] = pathlist[temp2];
367                        temp = temp2;
368                        ++count;
369                        shortened = qtrue;
370                    }
371                }
372            }
373        }
374
375        if (!shortened)
376        {
377            temppathlist[count] = pathlist[temp];
378            ++count;
379        }
380    }
381
382    upto = count;
383
384    for (temp = 0; temp < count; temp++)
385    {
386        pathlist[temp] = temppathlist[upto];
387        --upto;
388    }
389
390    G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
391    return count;
392}
393
394/*
395===========================================================================
396CreatePathAStar
397This function uses the A* pathfinding algorithm to determine the
398shortest path between any two nodes.
399It's fairly complex, so I'm not really going to explain it much.
400Look up A* and binary heaps for more info.
401pathlist stores the ideal path between the nodes, in reverse order,
402and the return value is the number of nodes in that path
403===========================================================================
404*/
405int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
406{
407    //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
408    //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
409    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
410    float gcost[MAX_NODES];
411    int fcost[MAX_NODES];
412    char list[MAX_NODES];                                   //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
413    short int parent[MAX_NODES];
414
415    short int numOpen = 0;
416    short int atNode, temp, newnode=-1;
417    qboolean found = qfalse;
418    int count = -1;
419    float gc;
420    int i, u, v, m;
421    vec3_t vec;
422
423    //clear out all the arrays
424    memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
425    memset(fcost, 0, sizeof(int)*MAX_NODES);
426    memset(list, 0, sizeof(char)*MAX_NODES);
427    memset(parent, 0, sizeof(short int)*MAX_NODES);
428    memset(gcost, -1, sizeof(float)*MAX_NODES);
429
430    //make sure we have valid data before calculating everything
431    if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
432        return -1;
433
434    openlist[1] = from;                                     //add the starting node to the open list
435    ++numOpen;
436    gcost[from] = 0;                                        //its f and g costs are obviously 0
437    fcost[from] = 0;
438
439    while (1)
440    {
441        if (numOpen != 0)                                   //if there are still items in the open list
442        {
443            //pop the top item off of the list
444            atNode = openlist[1];
445            list[atNode] = 2;                               //put the node on the closed list so we don't check it again
446            --numOpen;
447
448            openlist[1] = openlist[numOpen+1];              //move the last item in the list to the top position
449            v = 1;
450
451            //this while loop reorders the list so that the new lowest fcost is at the top again
452            while (1)
453            {
454                u = v;
455                if ((2*u+1) < numOpen)                      //if both children exist
456                {
457                    if (fcost[openlist[u]] >= fcost[openlist[2*u]])
458                        v = 2*u;
459                    if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
460                        v = 2*u+1;
461                }
462                else
463                {
464                    if ((2*u) < numOpen)                    //if only one child exists
465                    {
466                        if (fcost[openlist[u]] >= fcost[openlist[2*u]])
467                            v = 2*u;
468                    }
469                }
470
471                if (u != v)                                 //if they're out of order, swap this item with its parent
472                {
473                    temp = openlist[u];
474                    openlist[u] = openlist[v];
475                    openlist[v] = temp;
476                }
477                else
478                    break;
479            }
480
481            for (i = 0; i < nodes[atNode].enodenum; i++)    //loop through all the links for this node
482            {
483                newnode = nodes[atNode].links[i].targetNode;
484
485                //if this path is blocked, skip it
486                if (nodes[atNode].links[i].flags & PATH_BLOCKED)
487                    continue;
488                //if this path is blocked, skip it
489                if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
490                    continue;
491                //skip any unreachable nodes
492                if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
493                    continue;
494                if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
495                    continue;
496
497                if (list[newnode] == 2)                     //if this node is on the closed list, skip it
498                    continue;
499
500                if (list[newnode] != 1)                     //if this node is not already on the open list
501                {
502                    openlist[++numOpen] = newnode;          //add the new node to the open list
503                    list[newnode] = 1;
504                    parent[newnode] = atNode;               //record the node's parent
505
506                    if (newnode == to)                      //if we've found the goal, don't keep computing paths!
507                        break;                              //this will break the 'for' and go all the way to 'if (list[to] == 1)'
508
509                    //store it's f cost value
510                    fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
511
512                    //this loop re-orders the heap so that the lowest fcost is at the top
513                    m = numOpen;
514                    while (m != 1)                          //while this item isn't at the top of the heap already
515                    {
516                        //if it has a lower fcost than its parent
517                        if (fcost[openlist[m]] <= fcost[openlist[m/2]])
518                        {
519                            temp = openlist[m/2];
520                            openlist[m/2] = openlist[m];
521                            openlist[m] = temp;             //swap them
522                            m /= 2;
523                        }
524                        else
525                            break;
526                    }
527                }
528                else                                        //if this node is already on the open list
529                {
530                    gc = gcost[atNode];
531                    VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
532                    gc += VectorLength(vec);                //calculate what the gcost would be if we reached this node along the current path
533
534                    if (gc < gcost[newnode])                //if the new gcost is less (ie, this path is shorter than what we had before)
535                    {
536                        parent[newnode] = atNode;           //set the new parent for this node
537                        gcost[newnode] = gc;                //and the new g cost
538
539                        for (i = 1; i < numOpen; i++)       //loop through all the items on the open list
540                        {
541                            if (openlist[i] == newnode)     //find this node in the list
542                            {
543                                //calculate the new fcost and store it
544                                fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
545
546                                //reorder the list again, with the lowest fcost item on top
547                                m = i;
548                                while (m != 1)
549                                {
550                                    //if the item has a lower fcost than it's parent
551                                    if (fcost[openlist[m]] < fcost[openlist[m/2]])
552                                    {
553                                        temp = openlist[m/2];
554                                        openlist[m/2] = openlist[m];
555                                        openlist[m] = temp; //swap them
556                                        m /= 2;
557                                    }
558                                    else
559                                        break;
560                                }
561                                break;                      //exit the 'for' loop because we already changed this node
562                            }                               //if
563                        }                                   //for
564                    }                                       //if (gc < gcost[newnode])
565                }                                           //if (list[newnode] != 1) --> else
566            }                                               //for (loop through links)
567        }                                                   //if (numOpen != 0)
568        else
569        {
570            found = qfalse;                                 //there is no path between these nodes
571            break;
572        }
573
574        if (list[to] == 1)                                  //if the destination node is on the open list, we're done
575        {
576            found = qtrue;
577            break;
578        }
579    }                                                       //while (1)
580
581    if (found == qtrue)                                     //if we found a path
582    {
583        //G_Printf("%s - path found!n", bot->client->pers.netname);
584        count = 0;
585
586        temp = to;                                          //start at the end point
587        while (temp != from)                                //travel along the path (backwards) until we reach the starting point
588        {
589            pathlist[count++] = temp;                       //add the node to the pathlist and increment the count
590            temp = parent[temp];                            //move to the parent of this node to continue the path
591        }
592
593        pathlist[count++] = from;                           //add the beginning node to the end of the pathlist
594
595        #ifdef __BOT_SHORTEN_ROUTING__
596        count = ShortenASTARRoute(pathlist, count);         // This isn't working... Dunno why.. Unique1
597        #endif                                              //__BOT_SHORTEN_ROUTING__
598    }
599    else
600    {
601        //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
602        count = CreateDumbRoute(from, to, pathlist);
603
604        if (count > 0)
605        {
606            #ifdef __BOT_SHORTEN_ROUTING__
607            count = ShortenASTARRoute(pathlist, count);     // This isn't working... Dunno why.. Unique1
608            #endif                                          //__BOT_SHORTEN_ROUTING__
609            return count;
610        }
611    }
612
613    return count;                                           //return the number of nodes in the path, -1 if not found
614}
615
616/*
617===========================================================================
618GetFCost
619Utility function used by A* pathfinding to calculate the
620cost to move between nodes towards a goal.  Using the A*
621algorithm F = G + H, G here is the distance along the node
622paths the bot must travel, and H is the straight-line distance
623to the goal node.
624Returned as an int because more precision is unnecessary and it
625will slightly speed up heap access
626===========================================================================
627*/
628int GetFCost(int to, int num, int parentNum, float *gcost)
629{
630    float gc = 0;
631    float hc = 0;
632    vec3_t v;
633
634    if (gcost[num] == -1)
635    {
636        if (parentNum != -1)
637        {
638            gc = gcost[parentNum];
639            VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
640            gc += VectorLength(v);
641        }
642        gcost[num] = gc;
643    }
644    else
645        gc = gcost[num];
646
647    VectorSubtract(nodes[to].origin, nodes[num].origin, v);
648    hc = VectorLength(v);
649
650    return (int)(gc + hc);
651}
652#endif                                                      //__PATHFINDING__
Note: See TracBrowser for help on using the browser.