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

Revision 2, 24.7 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

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