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

Revision 102, 25.0 kB (checked in by yumileroy, 17 years ago)

[svn] Fixed copyright notices to comply with GPL.

Original author: w12x
Date: 2008-10-23 03:29:52-05:00

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