Crossfire Server, Branches 1.12  R18729
move.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_move_c =
3  * "$Id: move.c 11578 2009-02-23 22:02:27Z lalo $";
4  */
5 
6 /*
7  CrossFire, A Multiplayer game for X-windows
8 
9  Copyright (C) 2002-2006 Mark Wedel & Crossfire Development Team
10  Copyright (C) 1992 Frank Tore Johansen
11 
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 
26  The author can be reached via e-mail to crossfire-devel@real-time.com
27 */
28 
34 #include <global.h>
35 #ifndef __CEXTRACT__
36 #include <sproto.h>
37 #endif
38 
39 static int roll_ob(object *op, int dir, object *pusher);
40 
53 int move_object(object *op, int dir) {
54  return move_ob(op, dir, op);
55 }
56 
72 int move_ob(object *op, int dir, object *originator) {
73  sint16 newx = op->x+freearr_x[dir];
74  sint16 newy = op->y+freearr_y[dir];
75  object *tmp;
76  mapstruct *m;
77  int mflags;
78 
79  if (op == NULL) {
80  LOG(llevError, "Trying to move NULL.\n");
81  return 0;
82  }
83 
84  m = op->map;
85  mflags = get_map_flags(m, &m, newx, newy, &newx, &newy);
86 
87  /* If the space the player is trying to is out of the map,
88  * bail now - we know it can't work.
89  */
90  if (mflags&P_OUT_OF_MAP)
91  return 0;
92 
93 
94  /* Is this space blocked? Players with wizpass are immune to
95  * this condition.
96  */
97  if (blocked_link(op, m, newx, newy)
98  && !QUERY_FLAG(op, FLAG_WIZPASS))
99  return 0;
100 
101  /* 0.94.2 - we need to set the direction for the new animation code.
102  * it uses it to figure out face to use - I can't see it
103  * breaking anything, but it might.
104  */
105  if (op->more != NULL && !move_ob(op->more, dir, op->more->head))
106  return 0;
107 
108  op->direction = dir;
109 
111  check_earthwalls(op, m, newx, newy);
112  if (op->will_apply&WILL_APPLY_DOOR)
113  check_doors(op, m, newx, newy);
114 
115  /* 0.94.1 - I got a stack trace that showed it crash with remove_ob trying
116  * to remove a removed object, and this function was the culprit. A possible
117  * guess I have is that check_doors above ran into a trap, killing the
118  * monster.
119  *
120  * Unfortunately, it doesn't appear that the calling functions of move_object
121  * deal very well with op being killed, so all this might do is just
122  * migrate the problem someplace else.
123  */
124 
125  if (QUERY_FLAG(op, FLAG_REMOVED)) {
126  LOG(llevDebug, "move_object: monster has been removed - will not process further\n");
127  /* Was not successful, but don't want to try and move again */
128  return 1;
129  }
130 
131  /* If this is a tail portion, just want to tell caller that move is
132  * ok - the caller will deal with actual object removal/insertion
133  */
134  if (op->head)
135  return 1;
136 
137  remove_ob(op);
138 
139  /* we already have newx, newy, and m, so lets use them.
140  * In addition, this fixes potential crashes, because multipart object was
141  * on edge of map, +=x, +=y doesn't make correct coordinates.
142  */
143  for (tmp = op; tmp != NULL; tmp = tmp->more) {
144  tmp->x += freearr_x[dir];
145  tmp->y += freearr_y[dir];
146  tmp->map = get_map_from_coord(tmp->map, &tmp->x, &tmp->y);
147  }
148 
149  /* insert_ob_in_map will deal with any tiling issues */
150  insert_ob_in_map(op, m, originator, 0);
151 
152  /* Hmmm. Should be possible for multispace players now */
153  if (op->type == PLAYER) {
154  esrv_map_scroll(&op->contr->socket, freearr_x[dir], freearr_y[dir]);
155  op->contr->socket.update_look = 1;
156  op->contr->socket.look_position = 0;
157  } else if (op->type == TRANSPORT) {
158  object *pl;
159 
160  for (pl = op->inv; pl; pl = pl->below) {
161  if (pl->type == PLAYER) {
162  pl->contr->do_los = 1;
163  pl->map = op->map;
164  pl->x = op->x;
165  pl->y = op->y;
166  esrv_map_scroll(&pl->contr->socket, freearr_x[dir], freearr_y[dir]);
167  pl->contr->socket.update_look = 1;
168  pl->contr->socket.look_position = 0;
169  }
170  }
171  }
172 
173  return 1; /* this shouldn't be reached */
174 }
175 
197 int transfer_ob(object *op, int x, int y, int randomly, object *originator) {
198  int i;
199  object *tmp;
200 
201  if (randomly)
202  i = find_free_spot(op, op->map, x, y, 0, SIZEOFFREE);
203  else
204  i = find_first_free_spot(op, op->map, x, y);
205 
206  if (i == -1)
207  return 0; /* No free spot */
208 
209  if (op->head != NULL)
210  op = op->head;
211  remove_ob(op);
212  for (tmp = op; tmp != NULL; tmp = tmp->more)
213  tmp->x = x+freearr_x[i]+(tmp->arch == NULL ? 0 : tmp->arch->clone.x),
214  tmp->y = y+freearr_y[i]+(tmp->arch == NULL ? 0 : tmp->arch->clone.y);
215 
216  tmp = insert_ob_in_map(op, op->map, originator, 0);
217  if (op && op->type == PLAYER)
218  map_newmap_cmd(&op->contr->socket);
219  if (tmp)
220  return 0;
221  else
222  return 1;
223 }
224 
244 int teleport(object *teleporter, uint8 tele_type, object *user) {
245  object *altern[120]; /* Better use c/malloc here in the future */
246  int i, j, k, nrofalt = 0;
247  object *other_teleporter, *tmp;
248  mapstruct *m;
249  sint16 sx, sy;
250 
251  if (user == NULL)
252  return 0;
253  if (user->head != NULL)
254  user = user->head;
255 
256  /* Find all other teleporters within range. This range
257  * should really be setable by some object attribute instead of
258  * using hard coded values.
259  */
260  for (i = -5; i < 6; i++)
261  for (j = -5; j < 6; j++) {
262  if (i == 0 && j == 0)
263  continue;
264  /* Perhaps this should be extended to support tiled maps */
265  if (OUT_OF_REAL_MAP(teleporter->map, teleporter->x+i, teleporter->y+j))
266  continue;
267  other_teleporter = GET_MAP_OB(teleporter->map, teleporter->x+i, teleporter->y+j);
268 
269  while (other_teleporter) {
270  if (other_teleporter->type == tele_type)
271  break;
272  other_teleporter = other_teleporter->above;
273  }
274  if (other_teleporter)
275  altern[nrofalt++] = other_teleporter;
276  }
277 
278  if (!nrofalt) {
279  LOG(llevError, "No alternative teleporters around!\n");
280  return 0;
281  }
282 
283  other_teleporter = altern[RANDOM()%nrofalt];
284  k = find_free_spot(user, other_teleporter->map, other_teleporter->x, other_teleporter->y, 1, 9);
285 
286  /* if k==-1, unable to find a free spot. If this is shop
287  * mat that the player is using, find someplace to move
288  * the player - otherwise, player can get trapped in the shops
289  * that appear in random dungeons. We basically just make
290  * sure the space isn't no pass (eg wall), and don't care
291  * about is alive.
292  */
293  if (k == -1) {
294  if (tele_type == SHOP_MAT && user->type == PLAYER) {
295  for (k = 1; k < 9; k++) {
296  if (get_map_flags(other_teleporter->map, &m,
297  other_teleporter->x+freearr_x[k],
298  other_teleporter->y+freearr_y[k], &sx, &sy)&P_OUT_OF_MAP)
299  continue;
300 
301  if (!OB_TYPE_MOVE_BLOCK(user, GET_MAP_MOVE_BLOCK(m, sx, sy)))
302  break;
303 
304  }
305  if (k == 9) {
306  LOG(llevError, "Shop mat %s (%d, %d) is in solid rock?\n", other_teleporter->name, other_teleporter->x, other_teleporter->y);
307  /* Teleport player on top of blocked destination: this prevents
308  * players from being trapped inside shops if the destination
309  * is blocked with earth walls.
310  */
311  k = 0;
312  }
313  } else
314  return 0;
315  }
316 
317  remove_ob(user);
318 
319  /* Update location for the object */
320  for (tmp = user; tmp != NULL; tmp = tmp->more) {
321  tmp->x = other_teleporter->x+freearr_x[k]+(tmp->arch == NULL ? 0 : tmp->arch->clone.x);
322  tmp->y = other_teleporter->y+freearr_y[k]+(tmp->arch == NULL ? 0 : tmp->arch->clone.y);
323  }
324  tmp = insert_ob_in_map(user, other_teleporter->map, NULL, 0);
325  if (tmp && tmp->type == PLAYER)
326  map_newmap_cmd(&tmp->contr->socket);
327  return (tmp == NULL);
328 }
329 
340 void recursive_roll(object *op, int dir, object *pusher) {
341  char name[MAX_BUF];
342 
343  query_name(op, name, MAX_BUF);
344  if (!roll_ob(op, dir, pusher)) {
346  "You fail to push the %s.",
347  "You fail to push the %s.",
348  name);
349  return;
350  }
351  (void)move_ob(pusher, dir, pusher);
353  "You move the %s.",
354  "You move the %s.",
355  name);
356  return;
357 }
358 
380 static int try_fit(object *op, mapstruct *m, int x, int y) {
381  object *tmp, *more;
382  sint16 tx, ty;
383  int mflags;
384  mapstruct *m2;
385 
386  if (op->head)
387  op = op->head;
388 
389  for (more = op; more; more = more->more) {
390  tx = x+more->x-op->x;
391  ty = y+more->y-op->y;
392 
393  mflags = get_map_flags(m, &m2, tx, ty, &tx, &ty);
394 
395  if (mflags&P_OUT_OF_MAP)
396  return 1;
397 
398  for (tmp = GET_MAP_OB(m2, tx, ty); tmp; tmp = tmp->above) {
399  if (tmp->head == op || tmp == op)
400  continue;
401 
402  if ((QUERY_FLAG(tmp, FLAG_ALIVE) && tmp->type != DOOR))
403  return 1;
404 
405  if (OB_MOVE_BLOCK(op, tmp))
406  return 1;
407 
408  }
409  }
410  return 0;
411 }
412 
432 static int roll_ob(object *op, int dir, object *pusher) {
433  object *tmp;
434  sint16 x, y;
435  int flags;
436  mapstruct *m;
437  MoveType move_block;
438 
439  if (op->head)
440  op = op->head;
441 
442  x = op->x+freearr_x[dir];
443  y = op->y+freearr_y[dir];
444 
445  if (!QUERY_FLAG(op, FLAG_CAN_ROLL)
446  || (op->weight && random_roll(0, op->weight/50000-1, pusher, PREFER_LOW) > pusher->stats.Str))
447  return 0;
448 
449  m = op->map;
450  flags = get_map_flags(m, &m, x, y, &x, &y);
451 
452  if (flags&(P_OUT_OF_MAP|P_IS_ALIVE))
453  return 0;
454 
455  move_block = GET_MAP_MOVE_BLOCK(m, x, y);
456 
457  /* If the target space is not blocked, no need to look at the objects on it */
458  if ((op->move_type&move_block) == op->move_type) {
459  for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
460  if (tmp->head == op)
461  continue;
462  if (OB_MOVE_BLOCK(op, tmp) && !roll_ob(tmp, dir, pusher))
463  return 0;
464  }
465  }
466  if (try_fit(op, m, x, y))
467  return 0;
468 
469  remove_ob(op);
470  for (tmp = op; tmp != NULL; tmp = tmp->more)
471  tmp->x += freearr_x[dir],
472  tmp->y += freearr_y[dir];
473  insert_ob_in_map(op, op->map, pusher, 0);
474  return 1;
475 }
476 
492 int push_ob(object *who, int dir, object *pusher) {
493  int str1, str2;
494  object *owner;
495 
496  if (who->head != NULL)
497  who = who->head;
498  owner = get_owner(who);
499 
500  /* Wake up sleeping monsters that may be pushed */
501  CLEAR_FLAG(who, FLAG_SLEEP);
502 
503  /* player change place with his pets or summoned creature */
504  /* TODO: allow multi arch pushing. Can't be very difficult */
505  if (who->more == NULL
506  && (owner == pusher || (owner != NULL && owner->type == PLAYER && owner->contr->party != NULL && owner->contr->party == pusher->contr->party))) {
507  int temp;
508  mapstruct *m;
509 
510  remove_ob(who);
511  remove_ob(pusher);
512  temp = pusher->x;
513  pusher->x = who->x;
514  who->x = temp;
515 
516  temp = pusher->y;
517  pusher->y = who->y;
518  who->y = temp;
519 
520  m = pusher->map;
521  pusher->map = who->map;
522  who->map = m;
523 
524  insert_ob_in_map(who, who->map, pusher, 0);
525  insert_ob_in_map(pusher, pusher->map, pusher, 0);
526 
527  /* we presume that if the player is pushing his put, he moved in
528  * direction 'dir'. I can' think of any case where this would not be
529  * the case. Putting the map_scroll should also improve performance some.
530  */
531  if (pusher->type == PLAYER) {
532  esrv_map_scroll(&pusher->contr->socket, freearr_x[dir], freearr_y[dir]);
533  pusher->contr->socket.update_look = 1;
534  pusher->contr->socket.look_position = 0;
535  }
536  return 0;
537  }
538 
539  /* We want ONLY become enemy of evil, unaggressive monster. We must RUN in them */
540  /* In original we have here a unaggressive check only - that was the reason why */
541  /* we so often become an enemy of friendly monsters... */
542  /* funny: was they set to unaggressive 0 (= not so nice) they don't attack */
543  if (owner != pusher
544  && pusher->type == PLAYER
545  && who->type != PLAYER
546  && !QUERY_FLAG(who, FLAG_FRIENDLY)&& !QUERY_FLAG(who, FLAG_NEUTRAL)) {
547  if (pusher->contr->run_on) { /* only when we run */
548  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
550  "You start to attack %s !!",
551  "You start to attack %s !!",
552  who->name);
553  CLEAR_FLAG(who, FLAG_UNAGGRESSIVE); /* the sucker don't like you anymore */
554  who->enemy = pusher;
555  return 1;
556  } else {
557  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
559  "You avoid attacking %s.",
560  "You avoid attacking %s.",
561  who->name);
562  }
563  }
564 
565  /* now, lets test stand still. we NEVER can push stand_still monsters. */
566  if (QUERY_FLAG(who, FLAG_STAND_STILL)) {
567  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
569  "You can't push %s.",
570  "You can't push %s.",
571  who->name);
572  return 0;
573  }
574 
575  /* This block is basically if you are pushing friendly but
576  * non pet creaturs.
577  * It basically does a random strength comparision to
578  * determine if you can push someone around. Note that
579  * this pushes the other person away - its not a swap.
580  */
581 
582  str1 = (who->stats.Str > 0 ? who->stats.Str : who->level);
583  str2 = (pusher->stats.Str > 0 ? pusher->stats.Str : pusher->level);
584  if (QUERY_FLAG(who, FLAG_WIZ)
585  || random_roll(str1, str1/2+str1*2, who, PREFER_HIGH) >= random_roll(str2, str2/2+str2*2, pusher, PREFER_HIGH)
586  || !move_object(who, dir)) {
587  if (who->type == PLAYER) {
589  "%s tried to push you.",
590  "%s tried to push you.",
591  pusher->name);
592  }
593  return 0;
594  }
595 
596  /* If we get here, the push succeeded.
597  * Let everyone know the status.
598  */
599  if (who->type == PLAYER) {
601  "%s pushed you.",
602  "%s pushed you.",
603  pusher->name);
604  }
605  if (pusher->type == PLAYER) {
607  "You pushed %s back.",
608  "You pushed %s back.",
609  who->name);
610  }
611 
612  return 1;
613 }
614 
626 int move_to(object *op, int x, int y) {
627  int direction;
628 
629  if (op->x == x && op->y == y)
630  return 0;
631 
632  if (GET_MAP_FLAGS(op->map, x, y)&P_OUT_OF_MAP)
633  return 2;
634 
635  direction = compute_path(op, GET_MAP_OB(op->map, x, y), -1);
636  if (direction == -1)
637  return 2;
638 
639  /* this shouldn't fail, as the direction computing takes into account the blocked state... */
640  move_ob(op, direction, op);
641  return 1;
642 }
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, sint16 x, sint16 y, sint16 *nx, sint16 *ny)
Definition: map.c:330
Definition: player.h:146
MoveType move_type
Definition: object.h:277
signed short sint16
Definition: global.h:72
#define FLAG_SLEEP
Definition: define.h:604
#define WILL_APPLY_EARTHWALL
Definition: object.h:80
#define FLAG_STAND_STILL
Definition: define.h:605
#define WILL_APPLY_DOOR
Definition: object.h:81
#define FLAG_FRIENDLY
Definition: define.h:542
#define DOOR
Definition: define.h:135
unsigned char MoveType
Definition: define.h:725
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:448
static int try_fit(object *op, mapstruct *m, int x, int y)
Definition: move.c:380
object clone
Definition: object.h:326
socket_struct socket
Definition: player.h:148
short freearr_x[SIZEOFFREE]
Definition: object.c:75
#define PREFER_LOW
Definition: define.h:909
int push_ob(object *who, int dir, object *pusher)
Definition: move.c:492
uint32 run_on
Definition: player.h:182
struct obj * above
Definition: object.h:146
#define OUT_OF_REAL_MAP(M, X, Y)
Definition: map.h:238
sint16 x
Definition: object.h:179
#define NDI_BLACK
Definition: newclient.h:195
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format,...)
Definition: standalone.c:175
struct obj * enemy
Definition: object.h:232
#define PLAYER
Definition: define.h:113
#define MSG_TYPE_COMMAND_FAILURE
Definition: newclient.h:449
void recursive_roll(object *op, int dir, object *pusher)
Definition: move.c:340
#define FLAG_REMOVED
Definition: define.h:528
#define FLAG_CAN_ROLL
Definition: define.h:550
short freearr_y[SIZEOFFREE]
Definition: object.c:81
partylist * party
Definition: player.h:237
void map_newmap_cmd(socket_struct *ns)
Definition: request.c:672
int teleport(object *teleporter, uint8 tele_type, object *user)
Definition: move.c:244
#define MSG_TYPE_VICTIM_WAS_PUSHED
Definition: newclient.h:571
int move_to(object *op, int x, int y)
Definition: move.c:626
void remove_ob(object *op)
Definition: object.c:1515
#define FLAG_ALIVE
Definition: define.h:526
#define TRANSPORT
Definition: define.h:114
void check_earthwalls(object *op, mapstruct *m, int x, int y)
Definition: monster.c:1791
int blocked_link(object *ob, mapstruct *m, int sx, int sy)
Definition: map.c:373
#define MSG_TYPE_VICTIM
Definition: newclient.h:336
sint32 weight
Definition: object.h:216
#define FLAG_UNAGGRESSIVE
Definition: define.h:568
struct mapdef * map
Definition: object.h:155
int find_first_free_spot(const object *ob, mapstruct *m, int x, int y)
Definition: object.c:3240
const char * name
Definition: object.h:167
int compute_path(object *source, object *target, int default_dir)
Definition: monster.c:371
object * get_owner(object *op)
Definition: object.c:524
struct obj * below
Definition: object.h:145
unsigned char uint8
Definition: global.h:75
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:740
#define SIZEOFFREE
Definition: define.h:441
#define P_OUT_OF_MAP
Definition: map.h:272
mapstruct * get_map_from_coord(mapstruct *m, sint16 *x, sint16 *y)
Definition: map.c:2366
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:213
sint16 y
Definition: object.h:179
struct pl * contr
Definition: object.h:134
#define SHOP_MAT
Definition: define.h:231
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
#define CLEAR_FLAG(xyz, p)
Definition: define.h:512
int transfer_ob(object *op, int x, int y, int randomly, object *originator)
Definition: move.c:197
#define FLAG_WIZ
Definition: define.h:527
#define MAX_BUF
Definition: define.h:81
object * insert_ob_in_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1992
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.c:1432
uint16 look_position
Definition: newserver.h:142
static const flag_definition flags[]
sint8 Str
Definition: living.h:78
void check_doors(object *op, mapstruct *m, int x, int y)
Definition: monster.c:1802
#define GET_MAP_FLAGS(M, X, Y)
Definition: map.h:182
#define PREFER_HIGH
Definition: define.h:908
sint8 direction
Definition: object.h:185
#define FLAG_NEUTRAL
Definition: define.h:658
uint32 update_look
Definition: newserver.h:131
living stats
Definition: object.h:219
struct archt * arch
Definition: object.h:263
uint8 will_apply
Definition: object.h:243
uint32 do_los
Definition: player.h:180
#define MSG_TYPE_COMMAND
Definition: newclient.h:326
#define GET_MAP_OB(M, X, Y)
Definition: map.h:193
int find_free_spot(const object *ob, mapstruct *m, int x, int y, int start, int stop)
Definition: object.c:3200
int move_object(object *op, int dir)
Definition: move.c:53
struct obj * inv
Definition: object.h:148
#define NDI_UNIQUE
Definition: newclient.h:219
#define OB_MOVE_BLOCK(ob1, ob2)
Definition: define.h:731
struct obj * head
Definition: object.h:154
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:628
int move_ob(object *op, int dir, object *originator)
Definition: move.c:72
Definition: map.h:346
#define P_IS_ALIVE
Definition: map.h:258
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:51
#define FLAG_WIZPASS
Definition: define.h:611
sint16 level
Definition: object.h:202
struct obj * more
Definition: object.h:153
uint8 type
Definition: object.h:189
static int roll_ob(object *op, int dir, object *pusher)
Definition: move.c:432