Crossfire Server, Trunk
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) {
76  if (object_was_destroyed(op, op_tag))
77  return 1;
78  }
79 
80  if (op->will_apply&WILL_APPLY_DOOR) {
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) {
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 
153  if (randomly)
154  i = object_find_free_spot(op, op->map, x, y, 0, SIZEOFFREE);
155  else
156  i = object_find_first_free_spot(op, op->map, x, y);
157 
158  if (i == -1)
159  return 0; /* No free spot */
160 
161  op = HEAD(op);
162  object_remove(op);
163  op = object_insert_in_map_at(op, op->map, originator, 0, x+freearr_x[i], y+freearr_y[i]);
164  if (op && op->type == PLAYER) {
165  map_newmap_cmd(&op->contr->socket);
168  }
169  return op ? 0 : 1;
170 }
171 
191 int teleport(object *teleporter, uint8_t tele_type, object *user) {
192  object *altern[120]; /* Better use c/malloc here in the future */
193  int i, j, k, nrofalt = 0;
194  object *other_teleporter;
195  mapstruct *m;
196  int16_t sx, sy;
197 
198  if (user == NULL)
199  return 0;
200  user = HEAD(user);
201 
202  /* Find all other teleporters within range. This range
203  * should really be setable by some object attribute instead of
204  * using hard coded values.
205  */
206  for (i = -5; i < 6; i++)
207  for (j = -5; j < 6; j++) {
208  if (i == 0 && j == 0)
209  continue;
210  /* Perhaps this should be extended to support tiled maps */
211  if (OUT_OF_REAL_MAP(teleporter->map, teleporter->x+i, teleporter->y+j))
212  continue;
213  FOR_MAP_PREPARE(teleporter->map, teleporter->x+i, teleporter->y+j, tmp) {
214  if (tmp->type == tele_type) {
215  altern[nrofalt++] = tmp;
216  break;
217  }
218  } FOR_MAP_FINISH();
219  }
220 
221  if (!nrofalt) {
222  LOG(llevError, "No alternative teleporters around!\n");
223  return 0;
224  }
225 
226  other_teleporter = altern[RANDOM()%nrofalt];
227  k = object_find_free_spot(user, other_teleporter->map, other_teleporter->x, other_teleporter->y, 1, 9);
228 
229  /* if k==-1, unable to find a free spot. If this is shop
230  * mat that the player is using, find someplace to move
231  * the player - otherwise, player can get trapped in the shops
232  * that appear in random dungeons. We basically just make
233  * sure the space isn't no pass (eg wall), and don't care
234  * about is alive.
235  */
236  if (k == -1) {
237  if (tele_type == SHOP_MAT && user->type == PLAYER) {
238  for (k = 1; k < 9; k++) {
239  if (get_map_flags(other_teleporter->map, &m,
240  other_teleporter->x+freearr_x[k],
241  other_teleporter->y+freearr_y[k], &sx, &sy)&P_OUT_OF_MAP)
242  continue;
243 
244  if (!OB_TYPE_MOVE_BLOCK(user, GET_MAP_MOVE_BLOCK(m, sx, sy)))
245  break;
246  }
247  if (k == 9) {
248  LOG(llevError, "Shop mat %s (%d, %d) is in solid rock?\n", other_teleporter->name, other_teleporter->x, other_teleporter->y);
249  /* Teleport player on top of blocked destination: this prevents
250  * players from being trapped inside shops if the destination
251  * is blocked with earth walls.
252  */
253  k = 0;
254  }
255  } else
256  return 0;
257  }
258 
259  object_remove(user);
260 
261  user = object_insert_in_map_at(user, other_teleporter->map, NULL, 0, other_teleporter->x+freearr_x[k], other_teleporter->y+freearr_y[k]);
262  if (user && user->type == PLAYER) {
263  map_newmap_cmd(&user->contr->socket);
265  pets_attempt_follow(user, 1);
266  }
267  return (user == NULL);
268 }
269 
280 void recursive_roll(object *op, int dir, object *pusher) {
281  char name[MAX_BUF];
282 
284  if (!roll_ob(op, dir, pusher)) {
286  "You fail to push the %s.",
287  name);
288  return;
289  }
290  (void)move_ob(pusher, dir, pusher);
292  "You move the %s.",
293  name);
294  return;
295 }
296 
318 static int try_fit(object *op, mapstruct *m, int x, int y) {
319  object *more;
320  int16_t tx, ty;
321  int mflags;
322  mapstruct *m2;
323 
324  op = HEAD(op);
325  for (more = op; more; more = more->more) {
326  tx = x+more->x-op->x;
327  ty = y+more->y-op->y;
328 
329  mflags = get_map_flags(m, &m2, tx, ty, &tx, &ty);
330 
331  if (mflags&P_OUT_OF_MAP)
332  return 1;
333 
334  FOR_MAP_PREPARE(m2, tx, ty, tmp) {
335  if (tmp->head == op || tmp == op)
336  continue;
337 
338  if ((QUERY_FLAG(tmp, FLAG_ALIVE) && tmp->type != DOOR))
339  return 1;
340 
341  if (OB_MOVE_BLOCK(op, tmp))
342  return 1;
343  } FOR_MAP_FINISH();
344  }
345  return 0;
346 }
347 
367 static int roll_ob(object *op, int dir, object *pusher) {
368  int16_t x, y;
369  int flags;
370  mapstruct *m;
371  MoveType move_block;
372 
373  op = HEAD(op);
374  x = op->x+freearr_x[dir];
375  y = op->y+freearr_y[dir];
376 
378  || (op->weight && random_roll(0, op->weight/50000-1, pusher, PREFER_LOW) > pusher->stats.Str))
379  return 0;
380 
381  m = op->map;
382  flags = get_map_flags(m, &m, x, y, &x, &y);
383 
385  return 0;
386 
387  move_block = GET_MAP_MOVE_BLOCK(m, x, y);
388 
389  /* If the target space is not blocked, no need to look at the objects on it */
390  if ((op->move_type&move_block) == op->move_type) {
391  FOR_MAP_PREPARE(m, x, y, tmp) {
392  if (tmp->head == op)
393  continue;
394  if (OB_MOVE_BLOCK(op, tmp) && !roll_ob(tmp, dir, pusher))
395  return 0;
396  } FOR_MAP_FINISH();
397  }
398  if (try_fit(op, m, x, y))
399  return 0;
400 
401  object_remove(op);
402  object_insert_in_map_at(op, op->map, pusher, 0, op->x+freearr_x[dir], op->y+freearr_y[dir]);
403  return 1;
404 }
405 
421 int push_ob(object *who, int dir, object *pusher) {
422  int str1, str2;
423  object *owner;
424 
425  who = HEAD(who);
426  owner = object_get_owner(who);
427 
428  /* Wake up sleeping monsters that may be pushed */
430 
431  /* player change place with his pets or summoned creature */
432  /* TODO: allow multi arch pushing. Can't be very difficult */
433  if (who->more == NULL
434  && (owner == pusher || (owner != NULL && owner->type == PLAYER && owner->contr->party != NULL && owner->contr->party == pusher->contr->party))) {
435  int temp;
436  mapstruct *m;
437 
439  object_remove(pusher);
440  temp = pusher->x;
441  pusher->x = who->x;
442  who->x = temp;
443 
444  temp = pusher->y;
445  pusher->y = who->y;
446  who->y = temp;
447 
448  m = pusher->map;
449  pusher->map = who->map;
450  who->map = m;
451 
452  object_insert_in_map_at(who, who->map, pusher, 0, who->x, who->y);
453  object_insert_in_map_at(pusher, pusher->map, pusher, 0, pusher->x, pusher->y);
454 
455  /* we presume that if the player is pushing his put, he moved in
456  * direction 'dir'. I can' think of any case where this would not be
457  * the case. Putting the map_scroll should also improve performance some.
458  */
459  if (pusher->type == PLAYER) {
460  esrv_map_scroll(&pusher->contr->socket, freearr_x[dir], freearr_y[dir]);
461  pusher->contr->socket.update_look = 1;
462  pusher->contr->socket.look_position = 0;
463  }
464  return 0;
465  }
466 
467  /* We want ONLY become enemy of evil, unaggressive monster. We must RUN in them */
468  /* In original we have here a unaggressive check only - that was the reason why */
469  /* we so often become an enemy of friendly monsters... */
470  /* funny: was they set to unaggressive 0 (= not so nice) they don't attack */
471  if (owner != pusher
472  && pusher->type == PLAYER
473  && who->type != PLAYER
475  && !QUERY_FLAG(who, FLAG_NEUTRAL)) {
476  if (pusher->contr->run_on) { /* only when we run */
477  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
479  "You start to attack %s!",
480  who->name);
481  CLEAR_FLAG(who, FLAG_UNAGGRESSIVE); /* the sucker don't like you anymore */
482  object_set_enemy(who, pusher);
483  return 1;
484  } else {
485  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
487  "You avoid attacking %s.",
488  who->name);
489  }
490  }
491 
492  /* now, lets test stand still. we NEVER can push stand_still monsters. */
494  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
496  "You can't push %s.",
497  who->name);
498  return 0;
499  }
500 
501  /* This block is basically if you are pushing friendly but
502  * non pet creaturs.
503  * It basically does a random strength comparision to
504  * determine if you can push someone around. Note that
505  * this pushes the other person away - its not a swap.
506  */
507 
508  str1 = (who->stats.Str > 0 ? who->stats.Str : who->level);
509  str2 = (pusher->stats.Str > 0 ? pusher->stats.Str : pusher->level);
510  if (QUERY_FLAG(who, FLAG_WIZ)
511  || random_roll(str1, str1/2+str1*2, who, PREFER_HIGH) >= random_roll(str2, str2/2+str2*2, pusher, PREFER_HIGH)
512  || !move_object(who, dir)) {
513  if (who->type == PLAYER) {
515  "%s tried to push you.",
516  pusher->name);
517  }
518  return 0;
519  }
520 
521  /* If we get here, the push succeeded.
522  * Let everyone know the status.
523  */
524  if (who->type == PLAYER) {
526  "%s pushed you.",
527  pusher->name);
528  }
529  if (pusher->type == PLAYER) {
531  "You pushed %s back.",
532  who->name);
533  }
534 
535  return 1;
536 }
537 
549 int move_to(object *op, int x, int y) {
550  int direction;
551 
552  if (op->x == x && op->y == y)
553  return 0;
554 
555  if (get_map_flags(op->map, NULL, x, y, NULL, NULL)&P_OUT_OF_MAP)
556  return 2;
557 
558  direction = monster_compute_path(op, GET_MAP_OB(op->map, x, y), -1);
559  if (direction == -1)
560  return 2;
561 
562  op->direction = direction;
563  op->facing = direction;
564  if (op->animation)
565  animate_object(op, op->direction);
566 
567  /* can fail, as the direction computing takes into account the blocked state,
568  * except for the final spot... */
569  if (move_ob(op, direction, op) == 0)
570  return 2;
571 
572  return 1;
573 }
574 
583 int object_teleport(object *op, mapstruct *map, int x, int y) {
584  if (!out_of_map(map, x, y)) {
585  int k;
587  if (k == -1) {
588  return 0;
589  }
590 
591  if (!QUERY_FLAG(op, FLAG_REMOVED)) {
592  object_remove(op);
593  }
594 
595  object_insert_in_map_at(op, map, NULL, 0, x, y);
596  if (op->type == PLAYER) {
597  map_newmap_cmd(&op->contr->socket);
599  }
600  return 1;
601  }
602  return 0;
603 }
object_was_destroyed
#define object_was_destroyed(op, old_tag)
Definition: object.h:68
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:173
object_find_first_free_spot
int object_find_first_free_spot(const object *ob, mapstruct *m, int x, int y)
Definition: object.c:3549
PLAYER
@ PLAYER
Definition: object.h:107
global.h
FLAG_NEUTRAL
#define FLAG_NEUTRAL
Definition: define.h:354
object_remove
void object_remove(object *op)
Definition: object.c:1806
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:728
object_set_enemy
void object_set_enemy(object *op, object *enemy)
Definition: object.c:908
FLAG_STAND_STILL
#define FLAG_STAND_STILL
Definition: define.h:308
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:530
llevError
@ llevError
Definition: logger.h:11
push_ob
int push_ob(object *who, int dir, object *pusher)
Definition: move.c:421
esrv_map_scroll
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.c:1613
socket_struct::look_position
uint16_t look_position
Definition: newserver.h:114
diamondslots.x
x
Definition: diamondslots.py:15
obj::map
struct mapdef * map
Definition: object.h:298
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
move_to
int move_to(object *op, int x, int y)
Definition: move.c:549
out_of_map
int out_of_map(mapstruct *m, int x, int y)
Definition: map.c:2302
liv::Str
int8_t Str
Definition: living.h:36
monster_check_doors
void monster_check_doors(object *op, mapstruct *m, int x, int y)
Definition: monster.c:2223
pl::socket
socket_struct socket
Definition: player.h:94
pl
Definition: player.h:92
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
MoveType
unsigned char MoveType
Definition: define.h:417
monster_check_earthwalls
void monster_check_earthwalls(object *op, mapstruct *m, int x, int y)
Definition: monster.c:2207
blocked_link
int blocked_link(object *ob, mapstruct *m, int16_t sx, int16_t sy)
Definition: map.c:355
play_sound_map
void play_sound_map(int8_t sound_type, object *emitter, int dir, const char *action)
Definition: sounds.c:108
Ice.tmp
int tmp
Definition: Ice.py:207
TRANSPORT
@ TRANSPORT
Definition: object.h:108
flags
static const flag_definition flags[]
Definition: gridarta-types-convert.c:101
P_IS_ALIVE
#define P_IS_ALIVE
Definition: map.h:238
teleport
int teleport(object *teleporter, uint8_t tele_type, object *user)
Definition: move.c:191
MSG_TYPE_VICTIM
#define MSG_TYPE_VICTIM
Definition: newclient.h:415
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:404
freearr_x
short freearr_x[SIZEOFFREE]
Definition: object.c:288
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.c:294
FLAG_ALIVE
#define FLAG_ALIVE
Definition: define.h:230
animate_object
void animate_object(object *op, int dir)
Definition: anim.c:43
socket_struct::update_look
uint32_t update_look
Definition: newserver.h:104
m
static event_registration m
Definition: citylife.cpp:425
autojail.who
who
Definition: autojail.py:3
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
disinfect.map
map
Definition: disinfect.py:4
obj::name
sstring name
Definition: object.h:312
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:584
MOVE_WALK
#define MOVE_WALK
Definition: define.h:392
HEAD
#define HEAD(op)
Definition: object.h:594
obj::x
int16_t x
Definition: object.h:328
move_ob
int move_ob(object *op, int dir, object *originator)
Definition: move.c:58
FLAG_WIZPASS
#define FLAG_WIZPASS
Definition: define.h:314
GET_MAP_MOVE_BLOCK
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:193
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:675
FLAG_CAN_ROLL
#define FLAG_CAN_ROLL
Definition: define.h:254
FLAG_UNAGGRESSIVE
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
pl::run_on
uint32_t run_on
Definition: player.h:130
tag_t
uint32_t tag_t
Definition: object.h:12
sproto.h
give.direction
direction
Definition: give.py:37
mapdef
Definition: map.h:324
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
NDI_BLACK
#define NDI_BLACK
Definition: newclient.h:242
recursive_roll
void recursive_roll(object *op, int dir, object *pusher)
Definition: move.c:280
SIZEOFFREE
#define SIZEOFFREE
Definition: define.h:155
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:250
MAX_BUF
#define MAX_BUF
Definition: define.h:35
SHOP_MAT
@ SHOP_MAT
Definition: object.h:184
OB_MOVE_BLOCK
#define OB_MOVE_BLOCK(ob1, ob2)
Definition: define.h:423
RANDOM
#define RANDOM()
Definition: define.h:642
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
MSG_TYPE_COMMAND_FAILURE
#define MSG_TYPE_COMMAND_FAILURE
Definition: newclient.h:531
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:721
obj::y
int16_t y
Definition: object.h:328
OUT_OF_REAL_MAP
#define OUT_OF_REAL_MAP(M, X, Y)
Definition: map.h:218
pets_attempt_follow
void pets_attempt_follow(object *for_owner, int force)
Definition: pets.c:248
FLAG_REMOVED
#define FLAG_REMOVED
Definition: define.h:232
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
obj::type
uint8_t type
Definition: object.h:341
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:262
FLAG_FRIENDLY
#define FLAG_FRIENDLY
Definition: define.h:246
obj::stats
living stats
Definition: object.h:371
obj::contr
struct pl * contr
Definition: object.h:277
MSG_TYPE_VICTIM_WAS_PUSHED
#define MSG_TYPE_VICTIM_WAS_PUSHED
Definition: newclient.h:657
SOUND_TYPE_GROUND
#define SOUND_TYPE_GROUND
Definition: newclient.h:336
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
give.op
op
Definition: give.py:33
object_find_free_spot
int object_find_free_spot(const object *ob, mapstruct *m, int x, int y, int start, int stop)
Definition: object.c:3509
pl::do_los
uint32_t do_los
Definition: player.h:128
roll_ob
static int roll_ob(object *op, int dir, object *pusher)
Definition: move.c:367
diamondslots.y
y
Definition: diamondslots.py:16
WILL_APPLY_DOOR
#define WILL_APPLY_DOOR
Definition: object.h:55
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
obj::more
struct obj * more
Definition: object.h:296
map_newmap_cmd
void map_newmap_cmd(socket_struct *ns)
Definition: request.c:620
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:2067
DOOR
@ DOOR
Definition: object.h:126
OB_TYPE_MOVE_BLOCK
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:432
transfer_ob
int transfer_ob(object *op, int x, int y, int randomly, object *originator)
Definition: move.c:150
object_teleport
int object_teleport(object *op, mapstruct *map, int x, int y)
Definition: move.c:583
WILL_APPLY_EARTHWALL
#define WILL_APPLY_EARTHWALL
Definition: object.h:54
pl::party
partylist * party
Definition: player.h:188
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.c:311
guildbuy.temp
def temp
Definition: guildbuy.py:26
try_fit
static int try_fit(object *op, mapstruct *m, int x, int y)
Definition: move.c:318
FLAG_SLEEP
#define FLAG_SLEEP
Definition: define.h:307
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:668
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:319
obj::level
int16_t level
Definition: object.h:354
monster_compute_path
int monster_compute_path(object *source, object *target, int default_dir)
Definition: monster.c:438
give.name
name
Definition: give.py:27
player_update_bg_music
void player_update_bg_music(object *player)
object_get_owner
object * object_get_owner(object *op)
Definition: object.c:797
move_object
int move_object(object *op, int dir)
Definition: move.c:39