Crossfire Server, Trunk  R20513
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 
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 
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, m, x, y))
95  return 0;
96  }
97 
98  object_remove(op);
99  object_insert_in_map_at(op, op->map, originator, 0, op->x+freearr_x[dir], op->y+freearr_y[dir]);
100 
101  /* Hmmm. Should be possible for multispace players now */
102  if (op->type == PLAYER) {
103  esrv_map_scroll(&op->contr->socket, freearr_x[dir], freearr_y[dir]);
104  op->contr->socket.update_look = 1;
105  op->contr->socket.look_position = 0;
106  } else if (op->type == TRANSPORT) {
107  FOR_INV_PREPARE(op, pl)
108  if (pl->type == PLAYER) {
109  pl->contr->do_los = 1;
110  pl->map = op->map;
111  pl->x = op->x;
112  pl->y = op->y;
113  esrv_map_scroll(&pl->contr->socket, freearr_x[dir], freearr_y[dir]);
114  pl->contr->socket.update_look = 1;
115  pl->contr->socket.look_position = 0;
116  }
117  FOR_INV_FINISH();
118  }
119 
120  return 1; /* this shouldn't be reached */
121 }
122 
144 int transfer_ob(object *op, int x, int y, int randomly, object *originator) {
145  int i;
146  object *tmp;
147 
148  if (randomly)
149  i = object_find_free_spot(op, op->map, x, y, 0, SIZEOFFREE);
150  else
151  i = object_find_first_free_spot(op, op->map, x, y);
152 
153  if (i == -1)
154  return 0; /* No free spot */
155 
156  op = HEAD(op);
157  object_remove(op);
158  tmp = object_insert_in_map_at(op, op->map, originator, 0, x+freearr_x[i], y+freearr_y[i]);
159  if (op && op->type == PLAYER) {
160  map_newmap_cmd(&op->contr->socket);
162  }
163  if (tmp)
164  return 0;
165  else
166  return 1;
167 }
168 
188 int teleport(object *teleporter, uint8_t tele_type, object *user) {
189  object *altern[120]; /* Better use c/malloc here in the future */
190  int i, j, k, nrofalt = 0;
191  object *other_teleporter, *tmp;
192  mapstruct *m;
193  int16_t sx, sy;
194 
195  if (user == NULL)
196  return 0;
197  user = HEAD(user);
198 
199  /* Find all other teleporters within range. This range
200  * should really be setable by some object attribute instead of
201  * using hard coded values.
202  */
203  for (i = -5; i < 6; i++)
204  for (j = -5; j < 6; j++) {
205  if (i == 0 && j == 0)
206  continue;
207  /* Perhaps this should be extended to support tiled maps */
208  if (OUT_OF_REAL_MAP(teleporter->map, teleporter->x+i, teleporter->y+j))
209  continue;
210  FOR_MAP_PREPARE(teleporter->map, teleporter->x+i, teleporter->y+j, tmp) {
211  if (tmp->type == tele_type) {
212  altern[nrofalt++] = tmp;
213  break;
214  }
215  } FOR_MAP_FINISH();
216  }
217 
218  if (!nrofalt) {
219  LOG(llevError, "No alternative teleporters around!\n");
220  return 0;
221  }
222 
223  other_teleporter = altern[RANDOM()%nrofalt];
224  k = object_find_free_spot(user, other_teleporter->map, other_teleporter->x, other_teleporter->y, 1, 9);
225 
226  /* if k==-1, unable to find a free spot. If this is shop
227  * mat that the player is using, find someplace to move
228  * the player - otherwise, player can get trapped in the shops
229  * that appear in random dungeons. We basically just make
230  * sure the space isn't no pass (eg wall), and don't care
231  * about is alive.
232  */
233  if (k == -1) {
234  if (tele_type == SHOP_MAT && user->type == PLAYER) {
235  for (k = 1; k < 9; k++) {
236  if (get_map_flags(other_teleporter->map, &m,
237  other_teleporter->x+freearr_x[k],
238  other_teleporter->y+freearr_y[k], &sx, &sy)&P_OUT_OF_MAP)
239  continue;
240 
241  if (!OB_TYPE_MOVE_BLOCK(user, GET_MAP_MOVE_BLOCK(m, sx, sy)))
242  break;
243  }
244  if (k == 9) {
245  LOG(llevError, "Shop mat %s (%d, %d) is in solid rock?\n", other_teleporter->name, other_teleporter->x, other_teleporter->y);
246  /* Teleport player on top of blocked destination: this prevents
247  * players from being trapped inside shops if the destination
248  * is blocked with earth walls.
249  */
250  k = 0;
251  }
252  } else
253  return 0;
254  }
255 
256  object_remove(user);
257 
258  tmp = object_insert_in_map_at(user, other_teleporter->map, NULL, 0, other_teleporter->x+freearr_x[k], other_teleporter->y+freearr_y[k]);
259  if (tmp && tmp->type == PLAYER) {
260  map_newmap_cmd(&tmp->contr->socket);
262  }
263  return (tmp == NULL);
264 }
265 
276 void recursive_roll(object *op, int dir, object *pusher) {
277  char name[MAX_BUF];
278 
279  query_name(op, name, MAX_BUF);
280  if (!roll_ob(op, dir, pusher)) {
282  "You fail to push the %s.",
283  name);
284  return;
285  }
286  (void)move_ob(pusher, dir, pusher);
288  "You move the %s.",
289  name);
290  return;
291 }
292 
314 static int try_fit(object *op, mapstruct *m, int x, int y) {
315  object *more;
316  int16_t tx, ty;
317  int mflags;
318  mapstruct *m2;
319 
320  op = HEAD(op);
321  for (more = op; more; more = more->more) {
322  tx = x+more->x-op->x;
323  ty = y+more->y-op->y;
324 
325  mflags = get_map_flags(m, &m2, tx, ty, &tx, &ty);
326 
327  if (mflags&P_OUT_OF_MAP)
328  return 1;
329 
330  FOR_MAP_PREPARE(m2, tx, ty, tmp) {
331  if (tmp->head == op || tmp == op)
332  continue;
333 
334  if ((QUERY_FLAG(tmp, FLAG_ALIVE) && tmp->type != DOOR))
335  return 1;
336 
337  if (OB_MOVE_BLOCK(op, tmp))
338  return 1;
339  } FOR_MAP_FINISH();
340  }
341  return 0;
342 }
343 
363 static int roll_ob(object *op, int dir, object *pusher) {
364  int16_t x, y;
365  int flags;
366  mapstruct *m;
367  MoveType move_block;
368 
369  op = HEAD(op);
370  x = op->x+freearr_x[dir];
371  y = op->y+freearr_y[dir];
372 
373  if (!QUERY_FLAG(op, FLAG_CAN_ROLL)
374  || (op->weight && random_roll(0, op->weight/50000-1, pusher, PREFER_LOW) > pusher->stats.Str))
375  return 0;
376 
377  m = op->map;
378  flags = get_map_flags(m, &m, x, y, &x, &y);
379 
380  if (flags&(P_OUT_OF_MAP|P_IS_ALIVE))
381  return 0;
382 
383  move_block = GET_MAP_MOVE_BLOCK(m, x, y);
384 
385  /* If the target space is not blocked, no need to look at the objects on it */
386  if ((op->move_type&move_block) == op->move_type) {
387  FOR_MAP_PREPARE(m, x, y, tmp) {
388  if (tmp->head == op)
389  continue;
390  if (OB_MOVE_BLOCK(op, tmp) && !roll_ob(tmp, dir, pusher))
391  return 0;
392  } FOR_MAP_FINISH();
393  }
394  if (try_fit(op, m, x, y))
395  return 0;
396 
397  object_remove(op);
398  object_insert_in_map_at(op, op->map, pusher, 0, op->x+freearr_x[dir], op->y+freearr_y[dir]);
399  return 1;
400 }
401 
417 int push_ob(object *who, int dir, object *pusher) {
418  int str1, str2;
419  object *owner;
420 
421  who = HEAD(who);
422  owner = object_get_owner(who);
423 
424  /* Wake up sleeping monsters that may be pushed */
425  CLEAR_FLAG(who, FLAG_SLEEP);
426 
427  /* player change place with his pets or summoned creature */
428  /* TODO: allow multi arch pushing. Can't be very difficult */
429  if (who->more == NULL
430  && (owner == pusher || (owner != NULL && owner->type == PLAYER && owner->contr->party != NULL && owner->contr->party == pusher->contr->party))) {
431  int temp;
432  mapstruct *m;
433 
434  object_remove(who);
435  object_remove(pusher);
436  temp = pusher->x;
437  pusher->x = who->x;
438  who->x = temp;
439 
440  temp = pusher->y;
441  pusher->y = who->y;
442  who->y = temp;
443 
444  m = pusher->map;
445  pusher->map = who->map;
446  who->map = m;
447 
448  object_insert_in_map_at(who, who->map, pusher, 0, who->x, who->y);
449  object_insert_in_map_at(pusher, pusher->map, pusher, 0, pusher->x, pusher->y);
450 
451  /* we presume that if the player is pushing his put, he moved in
452  * direction 'dir'. I can' think of any case where this would not be
453  * the case. Putting the map_scroll should also improve performance some.
454  */
455  if (pusher->type == PLAYER) {
456  esrv_map_scroll(&pusher->contr->socket, freearr_x[dir], freearr_y[dir]);
457  pusher->contr->socket.update_look = 1;
458  pusher->contr->socket.look_position = 0;
459  }
460  return 0;
461  }
462 
463  /* We want ONLY become enemy of evil, unaggressive monster. We must RUN in them */
464  /* In original we have here a unaggressive check only - that was the reason why */
465  /* we so often become an enemy of friendly monsters... */
466  /* funny: was they set to unaggressive 0 (= not so nice) they don't attack */
467  if (owner != pusher
468  && pusher->type == PLAYER
469  && who->type != PLAYER
470  && !QUERY_FLAG(who, FLAG_FRIENDLY)
471  && !QUERY_FLAG(who, FLAG_NEUTRAL)) {
472  if (pusher->contr->run_on) { /* only when we run */
473  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
475  "You start to attack %s!",
476  who->name);
477  CLEAR_FLAG(who, FLAG_UNAGGRESSIVE); /* the sucker don't like you anymore */
478  object_set_enemy(who, pusher);
479  return 1;
480  } else {
481  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
483  "You avoid attacking %s.",
484  who->name);
485  }
486  }
487 
488  /* now, lets test stand still. we NEVER can push stand_still monsters. */
489  if (QUERY_FLAG(who, FLAG_STAND_STILL)) {
490  draw_ext_info_format(NDI_UNIQUE, 0, pusher,
492  "You can't push %s.",
493  who->name);
494  return 0;
495  }
496 
497  /* This block is basically if you are pushing friendly but
498  * non pet creaturs.
499  * It basically does a random strength comparision to
500  * determine if you can push someone around. Note that
501  * this pushes the other person away - its not a swap.
502  */
503 
504  str1 = (who->stats.Str > 0 ? who->stats.Str : who->level);
505  str2 = (pusher->stats.Str > 0 ? pusher->stats.Str : pusher->level);
506  if (QUERY_FLAG(who, FLAG_WIZ)
507  || random_roll(str1, str1/2+str1*2, who, PREFER_HIGH) >= random_roll(str2, str2/2+str2*2, pusher, PREFER_HIGH)
508  || !move_object(who, dir)) {
509  if (who->type == PLAYER) {
511  "%s tried to push you.",
512  pusher->name);
513  }
514  return 0;
515  }
516 
517  /* If we get here, the push succeeded.
518  * Let everyone know the status.
519  */
520  if (who->type == PLAYER) {
522  "%s pushed you.",
523  pusher->name);
524  }
525  if (pusher->type == PLAYER) {
527  "You pushed %s back.",
528  who->name);
529  }
530 
531  return 1;
532 }
533 
545 int move_to(object *op, int x, int y) {
546  int direction;
547 
548  if (op->x == x && op->y == y)
549  return 0;
550 
551  if (get_map_flags(op->map, NULL, x, y, NULL, NULL)&P_OUT_OF_MAP)
552  return 2;
553 
554  direction = monster_compute_path(op, GET_MAP_OB(op->map, x, y), -1);
555  if (direction == -1)
556  return 2;
557 
558  op->direction = direction;
559  op->facing = direction;
560  if (op->animation_id)
561  animate_object(op, op->direction);
562 
563  /* can fail, as the direction computing takes into account the blocked state,
564  * except for the final spot... */
565  if (move_ob(op, direction, op) == 0)
566  return 2;
567 
568  return 1;
569 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Sends message to player(s).
Definition: main.c:315
Error, serious thing.
Definition: logger.h:11
One player.
Definition: player.h:92
MoveType move_type
Type of movement this object uses.
Definition: object.h:424
see doc/Developers/objects
Definition: object.h:108
#define FLAG_SLEEP
NPC is sleeping.
Definition: define.h:308
uint32_t run_on
Player should keep moving in dir until run is off.
Definition: player.h:128
unsigned char uint8_t
Definition: win32.h:161
#define WILL_APPLY_EARTHWALL
Destroy earthwalls.
Definition: object.h:54
uint16_t animation_id
An index into the animation array.
Definition: object.h:416
#define MSG_TYPE_COMMAND_FAILURE
Failed result from command.
Definition: newclient.h:511
#define FLAG_STAND_STILL
NPC will not (ever) move.
Definition: define.h:309
uint16_t look_position
Start of drawing of look window.
Definition: newserver.h:126
#define WILL_APPLY_DOOR
Open non-locked doors.
Definition: object.h:55
#define FLAG_FRIENDLY
Will help players.
Definition: define.h:246
void player_update_bg_music(object player[static 1])
Definition: sounds.c:152
int monster_compute_path(object *source, object *target, int default_dir)
Computes a path from source to target.
Definition: monster.c:391
unsigned char MoveType
Typdef here to define type large enough to hold bitmask of all movement types.
Definition: define.h:432
static int try_fit(object *op, mapstruct *m, int x, int y)
Checks if an objects fits on a specified spot.
Definition: move.c:314
socket_struct socket
Socket information for this player.
Definition: player.h:94
short freearr_x[SIZEOFFREE]
X offset when searching around a spot.
Definition: object.c:65
#define PREFER_LOW
Definition: define.h:600
int push_ob(object *who, int dir, object *pusher)
Something is pushing some other object.
Definition: move.c:417
#define OUT_OF_REAL_MAP(M, X, Y)
Checks if a square is out of the map.
Definition: map.h:217
#define object_was_destroyed(op, old_tag)
Checks if an object still exists.
Definition: object.h:68
#define NDI_BLACK
Definition: newclient.h:221
Global type definitions and header inclusions.
void recursive_roll(object *op, int dir, object *pusher)
An object is pushed by another which is trying to take its place.
Definition: move.c:276
#define FLAG_CAN_ROLL
Object can be rolled.
Definition: define.h:254
short freearr_y[SIZEOFFREE]
Y offset when searching around a spot.
Definition: object.c:71
partylist * party
Party this player is part of.
Definition: player.h:186
void map_newmap_cmd(socket_struct *ns)
Sound related function.
Definition: request.c:615
uint32_t update_look
If true, we need to send the look window.
Definition: newserver.h:115
int16_t y
Position in the map for this object.
Definition: object.h:326
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Same as object_insert_in_map() except it handle separate coordinates and do a clean job preparing mul...
Definition: object.c:1921
int move_to(object *op, int x, int y)
Move an object one square toward a specified destination on the same map.
Definition: move.c:545
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Definition: newclient.h:379
#define FLAG_ALIVE
Object can fight (or be fought)
Definition: define.h:230
#define MSG_TYPE_VICTIM
Something bad is happening to the player.
Definition: newclient.h:392
#define MSG_TYPE_COMMAND_SUCCESS
Successful result from command.
Definition: newclient.h:510
int blocked_link(object *ob, mapstruct *m, int sx, int sy)
Returns true if the given coordinate is blocked except by the object passed is not blocking...
Definition: map.c:346
signed short int16_t
Definition: win32.h:160
int32_t weight
Attributes of the object.
Definition: object.h:365
#define FLAG_UNAGGRESSIVE
Monster doesn&#39;t attack players.
Definition: define.h:272
struct mapdef * map
Pointer to the map in which this object is present.
Definition: object.h:297
#define FOR_INV_FINISH()
Finishes FOR_INV_PREPARE().
Definition: define.h:712
const char * name
The name of the object, obviously...
Definition: object.h:311
int8_t direction
Means the object is moving that way.
Definition: object.h:334
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Basic macro to see if if ob1 can not move onto a space based on the &#39;type&#39; move_block parameter Add c...
Definition: define.h:447
#define SIZEOFFREE
Definition: define.h:154
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:251
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Gets the blocking state of a square.
Definition: map.h:192
struct pl * contr
Pointer to the player which control this object.
Definition: object.h:276
See Shop Mat.
Definition: object.h:184
int teleport(object *teleporter, uint8_t tele_type, object *user)
Teleport an item around a nearby random teleporter of specified type.
Definition: move.c:188
uint32_t tag_t
Object tag, unique during the whole game.
Definition: object.h:12
#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)
Move an object (even linked objects) to another spot on the same map.
Definition: move.c:144
#define HEAD(op)
Returns the head part of an object.
Definition: object.h:594
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:231
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
See Door.
Definition: object.h:126
int16_t x
Definition: object.h:326
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.c:1499
int object_find_first_free_spot(const object *ob, mapstruct *m, int x, int y)
object_find_first_free_spot(archetype, mapstruct, x, y) works like object_find_free_spot(), but it will search max number of squares.
Definition: object.c:3458
static const flag_definition flags[]
Flag mapping.
#define FOR_MAP_FINISH()
Finishes FOR_MAP_PREPARE().
Definition: define.h:765
int8_t Str
Definition: living.h:35
void animate_object(object *op, int dir)
Updates the face-variable of an object.
Definition: anim.c:186
See Player.
Definition: object.h:107
#define PREFER_HIGH
Definition: define.h:599
#define FLAG_NEUTRAL
monster is from type neutral
Definition: define.h:362
#define RANDOM()
Definition: define.h:679
tag_t count
Unique object number for this object.
Definition: object.h:299
living stats
Str, Con, Dex, etc.
Definition: object.h:368
uint8_t will_apply
See crossfire.doc and What monsters apply.
Definition: object.h:392
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:338
uint32_t do_los
If true, need to call update_los() in draw(), and clear.
Definition: player.h:126
void object_set_enemy(object *op, object *enemy)
Sets the enemy of an object.
Definition: object.c:679
void monster_check_doors(object *op, mapstruct *m, int x, int y)
Living creature attempts to open a door.
Definition: monster.c:2022
void monster_check_earthwalls(object *op, mapstruct *m, int x, int y)
Living creature attempts to hit an earthwall.
Definition: monster.c:2006
#define GET_MAP_OB(M, X, Y)
Gets the bottom object on a map.
Definition: map.h:172
int move_object(object *op, int dir)
Try to move op in the direction "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)
This rolls up wall, blocks_magic, blocks_view, etc, all into one function that just returns a P_...
Definition: map.c:302
#define NDI_UNIQUE
Print immediately, don&#39;t buffer.
Definition: newclient.h:245
#define OB_MOVE_BLOCK(ob1, ob2)
Basic macro to see if ob2 blocks ob1 from moving onto this space.
Definition: define.h:438
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
#define MSG_TYPE_VICTIM_WAS_PUSHED
Player was pushed or attempted pushed.
Definition: newclient.h:655
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Constructs a loop iterating over all objects of a map tile.
Definition: define.h:758
int object_find_free_spot(const object *ob, mapstruct *m, int x, int y, int start, int stop)
object_find_free_spot(object, map, x, y, start, stop) will search for a spot at the given map and coo...
Definition: object.c:3415
void query_name(const object *op, char *buf, size_t size)
Describes an item.
Definition: item.c:625
int move_ob(object *op, int dir, object *originator)
Op is trying to move in direction dir.
Definition: move.c:58
This is a game-map.
Definition: map.h:325
#define P_IS_ALIVE
Something alive is on this space.
Definition: map.h:237
int random_roll(int min, int max, const object *op, int goodbad)
Roll a random number between min and max.
Definition: utils.c:42
#define FLAG_WIZPASS
The wizard can go through walls.
Definition: define.h:315
int16_t level
Level of creature or object.
Definition: object.h:351
int8_t facing
Object is oriented/facing that way.
Definition: object.h:335
struct obj * more
Pointer to the rest of a large body of objects.
Definition: object.h:295
object * object_get_owner(object *op)
Returns the object which this object marks as being the owner.
Definition: object.c:559
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:705
void object_remove(object *op)
This function removes the object op from the linked list of objects which it is currently tied to...
Definition: object.c:1654
static int roll_ob(object *op, int dir, object *pusher)
An object is being pushed, and may push other objects.
Definition: move.c:363