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

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

Some missing changes. This should fix the bug that loading char causes crash.
Please do not commit to the other tip (I do not know how to delete it).

Original author: megamage
Date: 2008-11-20 17:40:13-06: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 `textid1` int(11) NOT NULL default '0';
25alter table creature_movement add `textid2` int(11) NOT NULL default '0';
26alter table creature_movement add `textid3` int(11) NOT NULL default '0';
27alter table creature_movement add `textid4` int(11) NOT NULL default '0';
28alter table creature_movement add `textid5` int(11) NOT NULL default '0';
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->textid[0])
152                {
153                    // Not only one text is set
154                    if( behavior->textid[1] )
155                    {
156                        // Select one from max 5 texts (0 and 1 already checked)
157                        int i = 2;
158                        for( ; i < MAX_WAYPOINT_TEXT; ++i )
159                            if( !behavior->textid[i] )
160                                break;
161
162                        creature.Say(behavior->textid[rand() % i], 0, 0);
163                    }
164                    else
165                        creature.Say(behavior->textid[0], 0, 0);
166                }
167            }                                               // wpBehaviour found
168            i_hasDone[idx] = true;
169            MovementInform(creature);
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.