Crossfire Server, Trunk  R22047
move.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
19 #include "global.h"
20 
21 #include <stdlib.h>
22 
23 #include "sproto.h"
24 
25 static int roll_ob(object *op, int dir, object *pusher);
26 
39 int move_object(object *op, int dir) {
40  return move_ob(op, dir, op);
41 }
42 
58 int move_ob(object *op, int dir, object *originator) {
59  object *part;
60  mapstruct *m;
61 
62  op->direction = dir;
63 
64  if (op->will_apply&(WILL_APPLY_EARTHWALL|WILL_APPLY_DOOR)) {
65  tag_t op_tag;
66 
67  op_tag = op->count;
68  for (part = op; part != NULL; part = part->more) {
69  int16_t x, y;
70 
71  if (get_map_flags(part->map, &m, part->x+freearr_x[dir], part->y+freearr_y[dir], &x, &y)&P_OUT_OF_MAP)
72  continue;
73 
74  if (op->will_apply&WILL_APPLY_EARTHWALL) {
75  monster_check_earthwalls(op, m, x, y);
76  if (object_was_destroyed(op, op_tag))
77  return 1;
78  }
79 
80  if (op->will_apply&WILL_APPLY_DOOR) {
81  monster_check_doors(op, m, x, y);
82  if (object_was_destroyed(op, op_tag))
83  return 1;
84  }
85  }
86  }
87 
88  for (part = op; part != NULL; part = part->more) {
89  int16_t x, y;
90 
91  if (get_map_flags(part->map, &m, part->x+freearr_x[dir], part->y+freearr_y[dir], &x, &y)&P_OUT_OF_MAP)
92  return 0;
93 
94  if (!QUERY_FLAG(op, FLAG_WIZPASS) && blocked_link(op->type == TRANSPORT ? part : op, m, x, y))
95  return 0;
96  }
97 
98  if (QUERY_FLAG(op, FLAG_ALIVE) && op->move_type == MOVE_WALK && op->weight >= 100) {
99  if (!op->hide) {
100  play_sound_map(SOUND_TYPE_GROUND, op, 0, "step");
101  }
102  }
103 
104  object_remove(op);
105  object_insert_in_map_at(op, op->map, originator, 0, op->x+freearr_x[dir], op->y+freearr_y[dir]);
106 
107  /* Hmmm. Should be possible for multispace players now */
108  if (op->type == PLAYER) {
109  esrv_map_scroll(&op->contr->socket, freearr_x[dir], freearr_y[dir]);
110  op->contr->socket.update_look = 1;
111  op->contr->socket.look_position = 0;
112  } else if (op->type == TRANSPORT) {
113  FOR_INV_PREPARE(op, pl)
114  if (pl->type == PLAYER) {
115  pl->contr->do_los = 1;
116  pl->map = op->map;
117  pl->x = op->x;
118  pl->y = op->y;
119  esrv_map_scroll(&pl->contr->socket, freearr_x[dir], freearr_y[dir]);
120  pl->contr->socket.update_look = 1;
121  pl->contr->socket.look_position = 0;
122  }
123  FOR_INV_FINISH();
124  }
125 
126  return 1; /* this shouldn't be reached */
127 }
128 
150 int transfer_ob(object *op, int x, int y, int randomly, object *originator) {
151  int i;
152  object *tmp;
153 
154  if (randomly)
155  i = object_find_free_spot(op, op->map, x, y, 0, SIZEOFFREE);
156  else
157  i = object_find_first_free_spot(op, op->map, x, y);
158 
159  if (i == -1)
160  return 0; /* No free spot */
161 
162  op = HEAD(op);
163  object_remove(op);
164  tmp = object_insert_in_map_at(op, op->map, originator, 0, x+freearr_x[i], y+freearr_y[i]);
165  if (op && op->type == PLAYER) {
166  map_newmap_cmd(&op->contr->socket);
168  }
169  if (tmp)
170  return 0;
171  else
172  return 1;
173 }
174 
194 int teleport(object *teleporter, uint8_t tele_type, object *user) {
195  object *altern[120]; /* Better use c/malloc here in the future */
196  int i, j, k, nrofalt = 0;
197  object *other_teleporter, *tmp;
198  mapstruct *m;
199  int16_t sx, sy;
200 
201  if (user == NULL)
202  return 0;
203  user = HEAD(user);
204 
205  /* Find all other teleporters within range. This range
206  * should really be setable by some object attribute instead of
207  * using hard coded values.
208  */
209  for (i = -5; i < 6; i++)
210  for (j = -5; j < 6; j++) {
211  if (i == 0 && j == 0)
212  continue;
213  /* Perhaps this should be extended to support tiled maps */
214  if (OUT_OF_REAL_MAP(teleporter->map, teleporter->x+i, teleporter->y+j))
215  continue;
216  FOR_MAP_PREPARE(teleporter->map, teleporter->x+i, teleporter->y+j, tmp) {
217  if (tmp->type == tele_type) {
218  altern[nrofalt++] = tmp;
219  break;
220  }
221  } FOR_MAP_FINISH();
222  }
223 
224  if (!nrofalt) {
225  LOG(llevError, "No alternative teleporters around!\n");
226  return 0;
227  }
228 
229  other_teleporter = altern[RANDOM()%nrofalt];
230  k = object_find_free_spot(user, other_teleporter->map, other_teleporter->x, other_teleporter->y, 1, 9);
231 
232  /* if k==-1, unable to find a free spot. If this is shop
233  * mat that the player is using, find someplace to move
234  * the player - otherwise, player can get trapped in the shops
235  * that appear in random dungeons. We basically just make
236  * sure the space isn't no pass (eg wall), and don't care
237  * about is alive.
238  */
239  if (k == -1) {
240  if (tele_type == SHOP_MAT && user->type == PLAYER) {
241  for (k = 1; k < 9; k++) {
242  if (get_map_flags(other_teleporter->map, &m,
243  other_teleporter->x+freearr_x[k],
244  other_teleporter->y+freearr_y[k], &sx, &sy)&P_OUT_OF_MAP)
245  continue;
246 
247  if (!OB_TYPE_MOVE_BLOCK(user, GET_MAP_MOVE_BLOCK(m, sx, sy)))
248  break;
249  }
250  if (k == 9) {
251  LOG(llevError, "Shop mat %s (%d, %d) is in solid rock?\n", other_teleporter->name, other_teleporter->x, other_teleporter->y);
252  /* Teleport player on top of blocked destination: this prevents
253  * players from being trapped inside shops if the destination
254  * is blocked with earth walls.
255  */
256  k = 0;
257  }
258  } else
259  return 0;
260  }
261 
262  object_remove(user);
263 
264  tmp = object_insert_in_map_at(user, other_teleporter->map, NULL, 0, other_teleporter->x+freearr_x[k], other_teleporter->y+freearr_y[k]);
265  if (tmp && tmp->type == PLAYER) {
266  map_newmap_cmd(&tmp->contr->socket);
268  }
269  return (tmp == NULL);
270 }
271 
282 void recursive_roll(object *op, int dir, object *pusher) {
283  char name[MAX_BUF];
284 
285  query_name(op, name, MAX_BUF);
286  if (!roll_ob(op, dir, pusher)) {
288  "You fail to push the %s.",
289  name);
290  return;
291  }
292  (void)move_ob(pusher, dir, pusher);
294  "You move the %s.",
295  name);
296  return;
297 }
298 
320 static int try_fit(object *op, mapstruct *m, int x, int y) {
321  object *more;
322  int16_t tx, ty;
323  int mflags;
324  mapstruct *m2;
325 
326  op = HEAD(op);
327  for (more = op; more; more = more->more) {
328  tx = x+more->x-op->x;
329  ty = y+more->y-op->y;
330 
331  mflags = get_map_flags(m, &m2, tx, ty, &tx, &ty);
332 
333  if (mflags&P_OUT_OF_MAP)
334  return 1;
335 
336  FOR_MAP_PREPARE(m2, tx, ty, tmp) {
337  if (tmp->head == op || tmp == op)
338  continue;
339 
340  if ((QUERY_FLAG(tmp, FLAG_ALIVE) && tmp->type != DOOR))
341  return 1;
342 
343  if (OB_MOVE_BLOCK(op, tmp))
344  return 1;
345  } FOR_MAP_FINISH();
346  }
347  return 0;
348 }
349 
369 static int roll_ob(object *op, int dir, object *pusher) {
370  int16_t x, y;
371  int flags;
372  mapstruct *m;
373  MoveType move_block;
374 
375  op = HEAD(op);
376  x = op->x+freearr_x[dir];
377  y = op->y+freearr_y[dir];
378 
379  if (!QUERY_FLAG(op, FLAG_CAN_ROLL)
380  || (op->weight && random_roll(0, op->weight/50000-1, pusher, PREFER_LOW) > pusher->stats.Str))
381  return 0;
382 
383  m = op->map;
384  flags = get_map_flags(m, &m, x, y, &x, &y);
385 
386  if (flags&(P_OUT_OF_MAP|P_IS_ALIVE))
387  return 0;
388 
389  move_block = GET_MAP_MOVE_BLOCK(m, x, y);
390 
391  /* If the target space is not blocked, no need to look at the objects on it */
392  if ((op->move_type&move_block) == op->move_type) {
393  FOR_MAP_PREPARE(m, x, y, tmp) {
394  if (tmp->head == op)
395  continue;
396  if (OB_MOVE_BLOCK(op, tmp) && !roll_ob(tmp, dir, pusher))
397  return 0;
398  } FOR_MAP_FINISH();
399  }
400  if (try_fit(op, m, x, y))
401  return 0;
402 
403  object_remove(op);
404  object_insert_in_map_at(op, op->map, pusher, 0, op->x+freearr_x[dir], op->y+freearr_y[dir]);
405  return 1;
406 }
407 
423 int push_ob(object *who, int dir, object *pusher) {
424  int str1, str2;
425  object *owner;
426 
427  who = HEAD(who);
428  owner = object_get_owner(who);
429 
430  /* Wake up sleeping monsters that may be pushed */
431  CLEAR_FLAG(who, FLAG_SLEEP);
432 
433  /* player change place with his pets or summoned creature */
434  /* TODO: allow multi arch pushing. Can't be very difficult */
435  if (who->more == NULL
436  && (owner == pusher || (owner != NULL && owner->type == PLAYER && owner->contr->party != NULL && owner->contr->party == pusher->contr->party))) {
437  int temp;
438  mapstruct *m;
439 
440  object_remove(who);
441  object_remove(pusher);
442  temp = pusher->x;
443  pusher->x = who->x;
444  who->x = temp;
445 
446  temp = pusher->y;
447  pusher->y = who->y;
448  who->y = temp;
449 
450  m = pusher->map;
451  pusher->map = who->map;
452  who->map = m;
453 
454  object_insert_in_map_at(who, who->map, pusher, 0, who->x, who->y);
455  object_insert_in_map_at(pusher, pusher->map, pusher, 0, pusher->x, pusher->y);
456 
457  /* we presume that if the player is pushing his put, he moved in
458  * direction 'dir'. I can' think of any case where this would not be
459  * the case. Putting the map_scroll should also improve performance some.
460  */
461  if (pusher->type == PLAYER) {
462  esrv_map_scroll(&pusher->contr->socket, freearr_x[dir], freearr_y[dir]);
463  pusher->contr->socket.update_look = 1;
464  pusher->contr->socket.look_position = 0;
465  }
466  return 0;
467  }
468 
469  /* We want ONLY become enemy of evil, unaggressive monster. We must RUN in them */
470  /* In original we have here a unaggressive check only - that was the reason why */
471  /* we so often become an enemy of friendly monsters... */
472  /* funny: was they set to unaggressive 0 (= not so nice) they don't attack */
473  if (owner != pusher
474  && pusher->type == PLAYER
475  && who->type != PLAYER
476  && !QUERY_FLAG(who, FLAG_FRIENDLY)
477  && !QUERY_FLAG(who, FLAG_NEUTRAL)) {
478  if (pusher->contr->run_on) { /* only when we run */
479  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
481  "You start to attack %s!",
482  who->name);
483  CLEAR_FLAG(who, FLAG_UNAGGRESSIVE); /* the sucker don't like you anymore */
484  object_set_enemy(who, pusher);
485  return 1;
486  } else {
487  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
489  "You avoid attacking %s.",
490  who->name);
491  }
492  }
493 
494  /* now, lets test stand still. we NEVER can push stand_still monsters. */
495  if (QUERY_FLAG(who, FLAG_STAND_STILL)) {
496  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
498  "You can't push %s.",
499  who->name);
500  return 0;
501  }
502 
503  /* This block is basically if you are pushing friendly but
504  * non pet creaturs.
505  * It basically does a random strength comparision to
506  * determine if you can push someone around. Note that
507  * this pushes the other person away - its not a swap.
508  */
509 
510  str1 = (who->stats.Str > 0 ? who->stats.Str : who->level);
511  str2 = (pusher->stats.Str > 0 ? pusher->stats.Str : pusher->level);
512  if (QUERY_FLAG(who, FLAG_WIZ)
513  || random_roll(str1, str1/2+str1*2, who, PREFER_HIGH) >= random_roll(str2, str2/2+str2*2, pusher, PREFER_HIGH)
514  || !move_object(who, dir)) {
515  if (who->type == PLAYER) {
517  "%s tried to push you.",
518  pusher->name);
519  }
520  return 0;
521  }
522 
523  /* If we get here, the push succeeded.
524  * Let everyone know the status.
525  */
526  if (who->type == PLAYER) {
528  "%s pushed you.",
529  pusher->name);
530  }
531  if (pusher->type == PLAYER) {
533  "You pushed %s back.",
534  who->name);
535  }
536 
537  return 1;
538 }
539 
551 int move_to(object *op, int x, int y) {
552  int direction;
553 
554  if (op->x == x && op->y == y)
555  return 0;
556 
557  if (get_map_flags(op->map, NULL, x, y, NULL, NULL)&P_OUT_OF_MAP)
558  return 2;
559 
560  direction = monster_compute_path(op, GET_MAP_OB(op->map, x, y), -1);
561  if (direction == -1)
562  return 2;
563 
564  op->direction = direction;
565  op->facing = direction;
566  if (op->animation)
567  animate_object(op, op->direction);
568 
569  /* can fail, as the direction computing takes into account the blocked state,
570  * except for the final spot... */
571  if (move_ob(op, direction, op) == 0)
572  return 2;
573 
574  return 1;
575 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:313
Definition: player.h:92
#define MOVE_WALK
Definition: define.h:407
#define FLAG_SLEEP
Definition: define.h:308
unsigned char uint8_t
Definition: win32.h:161
#define MSG_TYPE_COMMAND_FAILURE
Definition: newclient.h:511
#define FLAG_STAND_STILL
Definition: define.h:309
uint16_t look_position
Definition: newserver.h:114
#define FLAG_FRIENDLY
Definition: define.h:246
int monster_compute_path(object *source, object *target, int default_dir)
Definition: monster.c:393
#define SOUND_TYPE_GROUND
Definition: newclient.h:311
unsigned char MoveType
Definition: define.h:432
static int try_fit(object *op, mapstruct *m, int x, int y)
Definition: move.c:320
socket_struct socket
Definition: player.h:94
short freearr_x[SIZEOFFREE]
Definition: object.c:65
#define PREFER_LOW
Definition: define.h:601
int push_ob(object *who, int dir, object *pusher)
Definition: move.c:423
#define OUT_OF_REAL_MAP(M, X, Y)
Definition: map.h:218
int blocked_link(object *ob, mapstruct *m, int16_t sx, int16_t sy)
Definition: map.c:355
#define NDI_BLACK
Definition: newclient.h:221
void recursive_roll(object *op, int dir, object *pusher)
Definition: move.c:282
#define FLAG_CAN_ROLL
Definition: define.h:254
short freearr_y[SIZEOFFREE]
Definition: object.c:71
void map_newmap_cmd(socket_struct *ns)
Definition: request.c:618
uint32_t update_look
Definition: newserver.h:104
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:1849
int move_to(object *op, int x, int y)
Definition: move.c:551
#define MSG_TYPE_COMMAND
Definition: newclient.h:379
#define FLAG_ALIVE
Definition: define.h:230
#define MSG_TYPE_VICTIM
Definition: newclient.h:392
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:510
signed short int16_t
Definition: win32.h:160
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
#define FOR_INV_FINISH()
Definition: define.h:714
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:447
#define SIZEOFFREE
Definition: define.h:154
#define P_OUT_OF_MAP
Definition: map.h:252
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:193
int teleport(object *teleporter, uint8_t tele_type, object *user)
Definition: move.c:194
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define CLEAR_FLAG(xyz, p)
Definition: define.h:224
int transfer_ob(object *op, int x, int y, int randomly, object *originator)
Definition: move.c:150
#define FLAG_WIZ
Definition: define.h:231
#define MAX_BUF
Definition: define.h:35
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.c:1537
int object_find_first_free_spot(const object *ob, mapstruct *m, int x, int y)
Definition: object.c:3331
static const flag_definition flags[]
#define FOR_MAP_FINISH()
Definition: define.h:767
void animate_object(object *op, int dir)
Definition: anim.c:43
#define PREFER_HIGH
Definition: define.h:600
#define FLAG_NEUTRAL
Definition: define.h:362
#define RANDOM()
Definition: define.h:681
uint32_t do_los
Definition: player.h:126
void object_set_enemy(object *op, object *enemy)
Definition: object.c:679
void monster_check_doors(object *op, mapstruct *m, int x, int y)
Definition: monster.c:2092
void monster_check_earthwalls(object *op, mapstruct *m, int x, int y)
Definition: monster.c:2076
#define GET_MAP_OB(M, X, Y)
Definition: map.h:173
int move_object(object *op, int dir)
Definition: move.c:39
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.c:311
#define NDI_UNIQUE
Definition: newclient.h:245
#define OB_MOVE_BLOCK(ob1, ob2)
Definition: define.h:438
void player_update_bg_music(object *player)
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
#define MSG_TYPE_VICTIM_WAS_PUSHED
Definition: newclient.h:655
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:760
void play_sound_map(int8_t sound_type, object *emitter, int dir, const char *action)
Definition: sounds.c:107
int object_find_free_spot(const object *ob, mapstruct *m, int x, int y, int start, int stop)
Definition: object.c:3291
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:583
int move_ob(object *op, int dir, object *originator)
Definition: move.c:58
Definition: map.h:326
#define P_IS_ALIVE
Definition: map.h:238
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
#define FLAG_WIZPASS
Definition: define.h:315
object * object_get_owner(object *op)
Definition: object.c:568
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:707
void object_remove(object *op)
Definition: object.c:1588
static int roll_ob(object *op, int dir, object *pusher)
Definition: move.c:369