Atrinik Server  4.0
waypoint.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <plugin.h>
32 #include <monster_guard.h>
33 #include <object.h>
34 #include <object_methods.h>
35 #include <waypoint.h>
36 
41 {
42 }
43 
52 object *
53 waypoint_get_active (object *npc)
54 {
55  HARD_ASSERT(npc != NULL);
56 
57  FOR_INV_PREPARE(npc, tmp) {
58  if (tmp->type == WAYPOINT_OBJECT && QUERY_FLAG(tmp, FLAG_CURSED)) {
59  return tmp;
60  }
61  } FOR_INV_FINISH();
62 
63  return NULL;
64 }
65 
74 object *
75 waypoint_get_aggro (object *npc)
76 {
77  HARD_ASSERT(npc != NULL);
78 
79  FOR_INV_PREPARE(npc, tmp) {
80  if (tmp->type == WAYPOINT_OBJECT && QUERY_FLAG(tmp, FLAG_DAMNED)) {
81  return tmp;
82  }
83  } FOR_INV_FINISH();
84 
85  return NULL;
86 }
87 
97 object *
98 waypoint_get_home (object *npc)
99 {
100  HARD_ASSERT(npc != NULL);
101 
102  FOR_INV_PREPARE(npc, tmp) {
103  if (tmp->type == WAYPOINT_OBJECT && QUERY_FLAG(tmp, FLAG_REFLECTING)) {
104  return tmp;
105  }
106  } FOR_INV_FINISH();
107 
108  return NULL;
109 }
110 
121 static object *
122 waypoint_find (object *npc, shstr *name)
123 {
124  HARD_ASSERT(npc != NULL);
125  HARD_ASSERT(name != NULL);
126 
127  FOR_INV_PREPARE(npc, tmp) {
128  if (tmp->type == WAYPOINT_OBJECT && tmp->name == name) {
129  return tmp;
130  }
131  } FOR_INV_FINISH();
132 
133  return NULL;
134 }
135 
147 static mapstruct *
148 waypoint_load_destination (object *op, object *npc)
149 {
150  HARD_ASSERT(op != NULL);
151  HARD_ASSERT(npc != NULL);
152 
153  if (!map_path_isabs(op->slaying)) {
154  char *path = map_get_path(npc->map,
155  op->slaying,
156  MAP_UNIQUE(npc->map),
157  NULL);
158  FREE_AND_COPY_HASH(op->slaying, path);
159  efree(path);
160  }
161 
162  mapstruct *destmap;
163  if (op->slaying == npc->map->path) {
164  destmap = npc->map;
165  } else {
166  destmap = ready_map_name(op->slaying, NULL, MAP_NAME_SHARED);
167  }
168  return destmap;
169 }
170 
179 void
181 {
182  HARD_ASSERT(op != NULL);
183 
184  if (unlikely(op->env == NULL)) {
185  return;
186  }
187 
188  /* Store final path destination (used by aggro wp) */
189  if (QUERY_FLAG(op, FLAG_DAMNED)) {
190  if (OBJECT_VALID(op->enemy, op->enemy_count)) {
192  op->x = op->stats.hp = op->enemy->x;
193  op->y = op->stats.sp = op->enemy->y;
194  } else {
195  LOG(ERROR,
196  "Dynamic waypoint without valid target: %s",
197  object_get_str(op));
198  return;
199  }
200  }
201 
202 
203  mapstruct *destmap;
204  if (op->slaying != NULL) {
205  destmap = waypoint_load_destination(op, op->env);
206  } else {
207  destmap = op->env->map;
208  }
209 
210  if (destmap == NULL) {
211  LOG(ERROR,
212  "Invalid destination map '%s': %s",
213  op->slaying,
214  object_get_str(op));
215  return;
216  }
217 
218  path_node_t *path = path_find(op->env,
219  op->env->map,
220  op->env->x,
221  op->env->y,
222  destmap,
223  op->stats.hp,
224  op->stats.sp,
225  NULL);
226  path = path_compress(path);
227  if (path == NULL) {
228  if (!QUERY_FLAG(op, FLAG_DAMNED)) {
229  LOG(ERROR,
230  "No path to destination ('%s', %s @ %d,%d -> "
231  "'%s', %s @ %d,%d) for waypoint: %s",
232  op->env->name,
233  op->env->map->path,
234  op->env->x,
235  op->env->y,
236  op->name,
237  destmap->path,
238  op->stats.hp,
239  op->stats.sp,
240  object_get_str(op));
241  }
242 
243  return;
244  }
245 
246  /* Skip the first path element (always the starting position), but only
247  * if it's not a node with an exit. */
248  if (!(path->flags & PATH_NODE_EXIT)) {
249  path = path->next;
250  if (path == NULL) {
251  return;
252  }
253  }
254 
255  /* Textually encoded path */
257  /* Map file for last local path step */
259 
260  op->msg = path_encode(path);
261  /* Path offset */
262  op->stats.food = 0;
263  /* Msg boundary */
264  op->attacked_by_distance = strlen(op->msg);
265 
266  /* Number of fails */
267  op->stats.Int = 0;
268  /* Number of fails */
269  op->stats.Str = 0;
270  /* Best distance */
271  op->stats.dam = 30000;
272 }
273 
282 void
283 waypoint_move (object *op, object *npc)
284 {
285  HARD_ASSERT(op != NULL);
286  HARD_ASSERT(npc != NULL);
287  HARD_ASSERT(npc->map != NULL);
288 
289  mapstruct *destmap = npc->map;
290 
291  if (QUERY_FLAG(op, FLAG_DAMNED)) {
292  if (op->enemy == npc->enemy &&
293  op->enemy_count == npc->enemy_count &&
294  OBJECT_VALID(op->enemy, op->enemy_count)) {
295  destmap = op->enemy->map;
296  op->stats.hp = op->enemy->x;
297  op->stats.sp = op->enemy->y;
298  } else {
299  op->enemy = npc->enemy;
300  op->enemy_count = npc->enemy_count;
301  return;
302  }
303  } else if (op->slaying != NULL) {
304  destmap = waypoint_load_destination(op, npc);
305  }
306 
307  if (destmap == NULL) {
308  LOG(ERROR,
309  "Invalid destination map '%s' for NPC %s, waypoint %s",
310  op->slaying,
311  object_get_str(npc),
312  object_get_str(op));
313  return;
314  }
315 
316  rv_vector global_rv;
318  npc->x,
319  npc->y,
320  destmap,
321  op->stats.hp,
322  op->stats.sp,
323  &global_rv,
326  LOG(ERROR,
327  "Could not find rv to: %s, %d, %d, waypoint: %s",
328  destmap->path,
329  op->stats.hp,
330  op->stats.sp,
331  object_get_str(op));
332  /* Disable this waypoint */
333  CLEAR_FLAG(op, FLAG_CURSED);
334  return;
335  }
336 
337  rv_vector *dest_rv = &global_rv;
338 
339  /* Reached the final destination? */
340  if (global_rv.distance_z == 0 &&
341  global_rv.distance <= (unsigned int) op->stats.maxsp) {
342  /* Just arrived? */
343  if (op->stats.ac == 0) {
344 #ifdef DEBUG_PATHFINDING
345  LOG(DEBUG, "'%s' reached destination '%s'", npc->name, op->name);
346 #endif
347 
348  /* Trigger the TRIGGER event */
350  npc,
351  op,
352  NULL,
353  NULL,
354  0,
355  0,
356  0,
357  0) != 0) {
358  return;
359  }
360  }
361 
362  /* Waiting at this waypoint? */
363  if (op->stats.ac < op->stats.wc) {
364  op->stats.ac++;
365  return;
366  }
367 
368  /* Clear timer */
369  op->stats.ac = 0;
370  /* Set as inactive */
371  CLEAR_FLAG(op, FLAG_CURSED);
372  /* Remove precomputed path */
374  /* Remove precomputed path data */
376 
377  /* Is it a return-home waypoint? */
378  if (QUERY_FLAG(op, FLAG_REFLECTING)) {
379  npc->move_type = op->move_type;
381  }
382 
383  /* Start over with the new waypoint, if any */
384  if (!QUERY_FLAG(op, FLAG_DAMNED) && op->title != NULL) {
385  object *wp = waypoint_find(npc, op->title);
386  if (wp != NULL) {
387 #ifdef DEBUG_PATHFINDING
388  LOG(DEBUG, "'%s' next waypoint: '%s'", npc->name, op->title);
389 #endif
390  SET_FLAG(wp, FLAG_CURSED);
391  }
392 #ifdef DEBUG_PATHFINDING
393  else {
394  LOG(DEBUG, "'%s' is missing next waypoint.", npc->name);
395  }
396 #endif
397  }
398 
399  op->enemy = NULL;
400  return;
401  }
402 
403  if (HAS_EVENT(op, EVENT_CLOSE) &&
404  trigger_event(EVENT_CLOSE, npc, op, NULL, NULL, 0, 0, 0, 0) != 0) {
405  return;
406  }
407 
408  /* Handle precomputed paths */
409 
410  /* If we finished our current path, clear it so that we can get a
411  * new one. */
412  if (op->msg != NULL && (op->msg[op->stats.food] == '\0' ||
413  (global_rv.distance_z == 0 &&
414  global_rv.distance <= 0))) {
416  }
417 
418  /* Get new path if target has moved much since the path was created. */
419  if (QUERY_FLAG(op, FLAG_DAMNED) &&
420  op->msg != NULL &&
421  (op->stats.hp != op->x || op->stats.sp != op->y)) {
422  rv_vector rv;
423  ;
424 
425  if (global_rv.distance_z != 0 ||
427  op->stats.hp,
428  op->stats.sp,
429  destmap,
430  op->x,
431  op->y,
432  &rv,
434  (rv.distance > 1 && rv.distance > global_rv.distance)) {
435 #ifdef DEBUG_PATHFINDING
436  LOG(DEBUG,
437  "Path distance = %d for '%s' -> '%s'. Discarding old path.",
438  rv.distance,
439  npc->name,
440  npc->enemy->name);
441 #endif
443  }
444  }
445 
446  int destx;
447  int desty;
448  int16_t new_offset;
449  uint32_t destflags;
450  rv_vector local_rv;
451 
452  /* Are we far enough from the target to require a path? */
453  if (global_rv.distance_z != 0 || global_rv.distance > 1) {
454  if (op->msg == NULL) {
455  /* Request a path if we don't have one */
456  path_request(op);
457  } else {
458  /* If we have precalculated path, take direction to next
459  * subwaypoint. */
460  destx = op->stats.hp;
461  desty = op->stats.sp;
462 
463  new_offset = op->stats.food;
464 
465  if (new_offset < op->attacked_by_distance &&
466  path_get_next(op->msg,
467  &new_offset,
468  &op->race,
469  &destmap,
470  &destx,
471  &desty,
472  &destflags)) {
474  npc->x,
475  npc->y,
476  destmap,
477  destx,
478  desty,
479  &local_rv,
482  dest_rv = &local_rv;
483  } else {
484  /* We seem to have an invalid path string or offset. */
486  path_request(op);
487  }
488  }
489  }
490 
491  /* Did we get closer to our goal last time? */
492  if (dest_rv->distance < (unsigned int) op->stats.dam) {
493  op->stats.dam = dest_rv->distance;
494  /* Number of times we failed getting closer to (sub)goal */
495  op->stats.Str = 0;
496  } else if (dest_rv->distance_z == 0 && op->stats.Str++ > 4) {
497  /* Discard the current path, so that we can get a new one */
499 
500  /* For best-effort waypoints don't try too many times. */
501  if (QUERY_FLAG(op, FLAG_NO_ATTACK) && op->stats.Int++ > 10) {
502 #ifdef DEBUG_PATHFINDING
503  LOG(DEBUG,
504  "Stuck with a best-effort waypoint (%s). Accepting "
505  "current position", op->name);
506 #endif
507  op->stats.hp = npc->x;
508  op->stats.sp = npc->y;
509  return;
510  }
511  }
512 
513  if ((global_rv.distance_z != 0 || global_rv.distance > 1) &&
514  op->msg == NULL &&
516  !QUERY_FLAG(op, FLAG_DAMNED)) {
517 #ifdef DEBUG_PATHFINDING
518  LOG(DEBUG, "No path found. '%s' standing still.", npc->name);
519 #endif
520  return;
521  }
522 
523  /* Perform the actual move */
524  int dir = dest_rv->direction;
525  if (QUERY_FLAG(npc, FLAG_CONFUSED)) {
526  dir = get_randomized_dir(dir);
527  }
528 
529  if (dir != 0 && !QUERY_FLAG(npc, FLAG_STAND_STILL)) {
530  int ret = 0;
531 
533  /* Can the monster move directly toward waypoint? */
534  if (dest_rv->distance != 0 && (ret = move_object(npc, dir)) == 0) {
535  /* Try to move around corners otherwise */
536  for (int diff = 1; diff <= 2; diff++) {
537  /* Try left or right first? */
538  int m = 1 - rndm_chance(2) ? 2 : 0;
539 
540  if ((ret = move_object(npc, absdir(dir + diff * m))) != 0) {
541  break;
542  }
543 
544  if ((ret = move_object(npc, absdir(dir - diff * m))) != 0) {
545  break;
546  }
547  }
548  }
549 
550  if (OBJECTS_DESTROYED(npc)) {
551  return;
552  }
554 
555  /* If we had a local destination and we got close enough to it,
556  * accept it. */
557  if (dest_rv == &local_rv && dest_rv->distance == 1 && ret != -1) {
558  /* Handle entering exits that need to be manually
559  * applied (stairs, ladders, etc). */
560  if (destflags & PATH_NODE_EXIT &&
561  npc->map == destmap &&
562  npc->x == destx &&
563  npc->y == desty) {
564  FOR_MAP_PREPARE(npc->map, npc->x, npc->y, tmp) {
565  if (tmp->type == EXIT) {
566  object_apply(tmp, npc, 0);
567  break;
568  }
569  } FOR_MAP_FINISH();
570  }
571 
572  op->stats.food = new_offset;
573  /* Number of fails */
574  op->stats.Str = 0;
575  /* Best distance */
576  op->stats.dam = 30000;
577  }
578  }
579 }
#define FREE_AND_COPY_HASH(_sv_, _nv_)
Definition: global.h:100
int16_t ac
Definition: living.h:93
#define EXIT
Definition: define.h:312
uint8_t move_type
Definition: object.h:384
#define EVENT_CLOSE
Definition: plugin.h:95
int direction
Definition: map.h:787
void path_request(object *waypoint)
Definition: pathfinder.c:275
const char * race
Definition: object.h:174
#define FLAG_CURSED
Definition: define.h:1154
unsigned int distance
Definition: map.h:775
tag_t enemy_count
Definition: object.h:216
path_node_t * path_find(object *op, mapstruct *map1, int x, int y, mapstruct *map2, int x2, int y2, path_visualizer_t **visualizer)
Definition: pathfinder.c:859
object * waypoint_get_home(object *npc)
Definition: waypoint.c:98
int trigger_event(int event_type, object *const activator, object *const me, object *const other, const char *msg, int parm1, int parm2, int parm3, int flags)
Definition: plugins.c:510
object * waypoint_get_active(object *npc)
Definition: waypoint.c:53
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:1691
int get_randomized_dir(int dir)
Definition: move.c:53
#define FOR_INV_FINISH()
Definition: define.h:1698
shstr * path
Definition: map.h:568
#define FLAG_WP_PATH_REQUESTED
Definition: pathfinder.h:150
const char * slaying
Definition: object.h:180
#define HAS_EVENT(ob, event)
Definition: plugin.h:179
static int absdir(int d)
Definition: define.h:1838
int16_t sp
Definition: living.h:78
#define OBJECTS_DESTROYED_BEGIN(...)
Definition: object.h:765
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
static mapstruct * waypoint_load_destination(object *op, object *npc)
Definition: waypoint.c:148
struct obj * enemy
Definition: object.h:196
int8_t Int
Definition: living.h:112
static object * waypoint_find(object *npc, shstr *name)
Definition: waypoint.c:122
path_node_t * path_compress(path_node_t *path)
Definition: pathfinder.c:759
#define FLAG_STAND_STILL
Definition: define.h:1133
int16_t maxsp
Definition: living.h:81
int32_t hp
Definition: living.h:72
int object_apply(object *op, object *applier, int aflags)
const char * title
Definition: object.h:171
#define FLAG_DAMNED
Definition: define.h:1158
int16_t y
Definition: object.h:276
#define EVENT_TRIGGER
Definition: plugin.h:93
const char * object_get_str(const object *op)
Definition: object.c:3151
#define RV_RECURSIVE_SEARCH
Definition: map.h:825
object * waypoint
Waypoint object.
Definition: pathfinder.c:87
struct mapdef * map
Definition: object.h:139
int16_t dam
Definition: living.h:87
const char * name
Definition: object.h:168
#define SET_FLAG(xyz, p)
Definition: define.h:741
struct obj * env
Definition: object.h:130
#define move_object(__op, __dir)
Definition: global.h:253
#define WAYPOINT_OBJECT
Definition: define.h:485
#define FLAG_NO_ATTACK
Definition: define.h:1225
object * waypoint_get_aggro(object *npc)
Definition: waypoint.c:75
#define OBJECTS_DESTROYED(obj)
Definition: object.h:811
void waypoint_compute_path(object *op)
Definition: waypoint.c:180
#define FOR_MAP_FINISH()
Definition: define.h:1759
int16_t x
Definition: object.h:273
int16_t wc
Definition: living.h:90
#define MAP_NAME_SHARED
Definition: map.h:157
#define OBJECTS_DESTROYED_END()
Definition: object.h:816
#define RV_DIAGONAL_DISTANCE
Definition: map.h:809
int path_get_next(shstr *buf, int16_t *off, shstr **mappath, mapstruct **map, int *x, int *y, uint32_t *flags)
Definition: pathfinder.c:652
int get_rangevector_from_mapcoords(mapstruct *map1, int x, int y, mapstruct *map2, int x2, int y2, rv_vector *retval, int flags)
Definition: map.c:2297
OBJECT_TYPE_INIT_DEFINE(waypoint)
Definition: waypoint.c:40
living stats
Definition: object.h:481
int distance_z
Definition: map.h:784
void monster_guard_activate_gate(object *op, int state)
Definition: monster_guard.c:52
#define CLEAR_FLAG(xyz, p)
Definition: define.h:751
uint8_t flags
A combination of Path node flags.
Definition: pathfinder.h:64
int16_t attacked_by_distance
Definition: object.h:289
#define MAP_UNIQUE(m)
Definition: map.h:96
const char * msg
Definition: object.h:183
#define FREE_AND_CLEAR_HASH(_nv_)
Definition: global.h:130
#define PATH_NODE_EXIT
Definition: pathfinder.h:42
#define OBJECT_VALID(_ob_, _count_)
Definition: object.h:548
int8_t Str
Definition: living.h:103
void waypoint_move(object *op, object *npc)
Definition: waypoint.c:283
mapstruct * ready_map_name(const char *name, mapstruct *originator, int flags)
Definition: map.c:1584
#define FLAG_CONFUSED
Definition: define.h:872
Definition: map.h:536
struct path_node * next
Next node in a linked list.
Definition: pathfinder.h:57
shstr * path_encode(path_node_t *path)
Definition: pathfinder.c:589
#define FLAG_REFLECTING
Definition: define.h:984
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:1752
int16_t food
Definition: living.h:84