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

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

[svn] * Merge Temp dev SVN with Assembla.
* Changes include:

  • Implementation of w12x's Outdoor PvP and Game Event Systems.
  • Temporary removal of IRC Chat Bot (until infinite loop when disabled is fixed).
  • All mangos -> trinity (to convert your mangos_string table, please run mangos_string_to_trinity_string.sql).
  • Improved Config cleanup.
  • And many more changes.

Original author: Seline
Date: 2008-10-14 11:57:03-05:00

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