ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/move.C
Revision: 1.21
Committed: Sun Jul 1 05:00:20 2007 UTC (16 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_2, rel-2_3
Changes since 1.20: +10 -11 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

# Content
1 /*
2 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Crossfire TRT 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 3 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, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 */
23
24 #include <global.h>
25 #ifndef __CEXTRACT__
26 # include <sproto.h>
27 #endif
28
29 /*
30 * move_object() tries to move object op in the direction "dir".
31 * If it fails (something blocks the passage), it returns 0,
32 * otherwise 1.
33 * This is an improvement from the previous move_ob(), which
34 * removed and inserted objects even if they were unable to move.
35 */
36
37 int
38 move_object (object *op, int dir)
39 {
40 return move_ob (op, dir, op);
41 }
42
43 /* object op is trying to move in direction dir.
44 * originator is typically the same as op, but
45 * can be different if originator is causing op to
46 * move (originator is pushing op)
47 * returns 0 if the object is not able to move to the
48 * desired space, 1 otherwise (in which case we also
49 * move the object accordingly. This function is
50 * very similiar to move_object.
51 */
52 int
53 move_ob (object *op, int dir, object *originator)
54 {
55 sint16 newx = op->x + freearr_x[dir];
56 sint16 newy = op->y + freearr_y[dir];
57 object *tmp;
58 maptile *m;
59 int mflags;
60
61 if (op == NULL)
62 {
63 LOG (llevError, "Trying to move NULL.\n");
64 return 0;
65 }
66
67 m = op->map;
68 mflags = get_map_flags (m, &m, newx, newy, &newx, &newy);
69
70 /* If the space the player is trying to is out of the map,
71 * bail now - we know it can't work.
72 */
73 if (mflags & P_OUT_OF_MAP)
74 return 0;
75
76 /* Is this space blocked? Players with wizpass are immune to
77 * this condition.
78 */
79 if (blocked_link (op, m, newx, newy) && !QUERY_FLAG (op, FLAG_WIZPASS))
80 return 0;
81
82 /* 0.94.2 - we need to set the direction for the new animation code.
83 * it uses it to figure out face to use - I can't see it
84 * breaking anything, but it might.
85 */
86 if (op->more && !move_ob (op->more, dir, op->more->head))
87 return 0;
88
89 op->direction = dir;
90
91 if (op->will_apply & 4)
92 check_earthwalls (op, m, newx, newy);
93
94 if (op->will_apply & 8)
95 check_doors (op, m, newx, newy);
96
97 /* 0.94.1 - I got a stack trace that showed it crash with remove_ob trying
98 * to remove a removed object, and this function was the culprit. A possible
99 * guess I have is that check_doors above ran into a trap, killing the
100 * monster.
101 *
102 * Unfortunately, it doesn't appear that the calling functions of move_object
103 * deal very well with op being killed, so all this might do is just
104 * migrate the problem someplace else.
105 */
106
107 if (QUERY_FLAG (op, FLAG_REMOVED))
108 {
109 LOG (llevDebug, "move_object: monster has been removed - will not process further\n");
110 /* Was not successful, but don't want to try and move again */
111 return 1;
112 }
113
114 /* If this is a tail portion, just want to tell caller that move is
115 * ok - the caller will deal with actual object removal/insertion
116 */
117 if (op->head)
118 return 1;
119
120 if (m != op->map && op->contr)
121 {
122 if (INVOKE_MAP (LEAVE, op->map, ARG_PLAYER (op->contr)))
123 return 0;
124
125 op->remove ();
126
127 if (INVOKE_PLAYER (MAP_CHANGE, op->contr, ARG_MAP (m), ARG_INT (newx), ARG_INT (newy)))
128 return 0;
129
130 if (INVOKE_MAP (ENTER, m, ARG_PLAYER (op->contr), ARG_INT (newx), ARG_INT (newy)))
131 return 0;
132 }
133 else
134 op->remove ();
135
136 /* we already have newx, newy, and m, so lets use them.
137 * In addition, this fixes potential crashes, because multipart object was
138 * on edge of map, +=x, +=y doesn't make correct coordinates.
139 */
140 for (tmp = op; tmp != NULL; tmp = tmp->more)
141 {
142 tmp->x += freearr_x[dir];
143 tmp->y += freearr_y[dir];
144 tmp->map = get_map_from_coord (tmp->map, &tmp->x, &tmp->y);
145 }
146
147 /* insert_ob_in_map will deal with any tiling issues */
148 insert_ob_in_map (op, m, originator, 0);
149
150 return 1;
151 }
152
153
154 /*
155 * transfer_ob(): Move an object (even linked objects) to another spot
156 * on the same map.
157 *
158 * Does nothing if there is no free spot.
159 *
160 * randomly: If true, use find_free_spot() to find the destination, otherwise
161 * use find_first_free_spot().
162 *
163 * Return value: 1 if object was destroyed, 0 otherwise.
164 */
165
166 int
167 transfer_ob (object *op, int x, int y, int randomly, object *originator)
168 {
169 int i;
170 object *tmp;
171
172 if (randomly)
173 i = find_free_spot (op, op->map, x, y, 0, SIZEOFFREE);
174 else
175 i = find_first_free_spot (op, op->map, x, y);
176
177 if (i == -1)
178 return 0; /* No free spot */
179
180 if (op->head != NULL)
181 op = op->head;
182 op->remove ();
183 for (tmp = op; tmp != NULL; tmp = tmp->more)
184 tmp->x = x + freearr_x[i] + (tmp->arch == NULL ? 0 : tmp->arch->x),
185 tmp->y = y + freearr_y[i] + (tmp->arch == NULL ? 0 : tmp->arch->y);
186
187 tmp = insert_ob_in_map (op, op->map, originator, 0);
188 if (tmp)
189 return 0;
190 else
191 return 1;
192 }
193
194 /*
195 * Return value: 1 if object was destroyed, 0 otherwise.
196 * Modified so that instead of passing the 'originator' that had no
197 * real use, instead we pass the 'user' of the teleporter. All the
198 * callers know what they wanted to teleporter (move_teleporter or
199 * shop map code)
200 * tele_type is the type of teleporter we want to match against -
201 * currently, this is either set to SHOP_MAT or TELEPORTER.
202 * It is basically used so that shop_mats and normal teleporters can
203 * be used close to each other and not have the player put to the
204 * one of another type.
205 */
206 int
207 teleport (object *teleporter, uint8 tele_type, object *user)
208 {
209 object *altern;
210 int i, j, k, nrofalt = 0;
211 object *other_teleporter, *tmp;
212 maptile *m;
213 sint16 sx, sy;
214
215 if (user == NULL)
216 return 0;
217 if (user->head != NULL)
218 user = user->head;
219
220 /* Find all other teleporters within range. This range
221 * should really be setable by some object attribute instead of
222 * using hard coded values.
223 */
224 for (i = -5; i < 6; i++)
225 for (j = -5; j < 6; j++)
226 {
227 if (i == 0 && j == 0)
228 continue;
229 /* Perhaps this should be extended to support tiled maps */
230 if (OUT_OF_REAL_MAP (teleporter->map, teleporter->x + i, teleporter->y + j))
231 continue;
232 other_teleporter = GET_MAP_OB (teleporter->map, teleporter->x + i, teleporter->y + j);
233
234 while (other_teleporter)
235 {
236 if (other_teleporter->type == tele_type)
237 break;
238 other_teleporter = other_teleporter->above;
239 }
240 if (other_teleporter && !(RANDOM () % ++nrofalt))
241 altern = other_teleporter;
242 }
243
244 if (!nrofalt)
245 {
246 LOG (llevError, "No alternative teleporters around!\n");
247 return 0;
248 }
249
250 other_teleporter = altern;
251 k = find_free_spot (user, other_teleporter->map, other_teleporter->x, other_teleporter->y, 1, 9);
252
253 /* if k==-1, unable to find a free spot. If this is shop
254 * mat that the player is using, find someplace to move
255 * the player - otherwise, player can get trapped in the shops
256 * that appear in random dungeons. We basically just make
257 * sure the space isn't no pass (eg wall), and don't care
258 * about is alive.
259 */
260 if (k == -1)
261 {
262 if (tele_type == SHOP_MAT && user->type == PLAYER)
263 {
264 for (k = 1; k < 9; k++)
265 {
266 if (get_map_flags (other_teleporter->map, &m,
267 other_teleporter->x + freearr_x[k], other_teleporter->y + freearr_y[k], &sx, &sy) & P_OUT_OF_MAP)
268 continue;
269
270 if (!OB_TYPE_MOVE_BLOCK (user, GET_MAP_MOVE_BLOCK (m, sx, sy)))
271 break;
272
273 }
274 if (k == 9)
275 {
276 LOG (llevError, "Shop mat %s (%d, %d) is in solid rock?\n",
277 &other_teleporter->name, other_teleporter->x, other_teleporter->y);
278 return 0;
279 }
280 }
281 else
282 return 0;
283 }
284
285 user->remove ();
286
287 /* Update location for the object */
288 for (tmp = user; tmp != NULL; tmp = tmp->more)
289 {
290 tmp->x = other_teleporter->x + freearr_x[k] + (tmp->arch == NULL ? 0 : tmp->arch->x);
291 tmp->y = other_teleporter->y + freearr_y[k] + (tmp->arch == NULL ? 0 : tmp->arch->y);
292 }
293 tmp = insert_ob_in_map (user, other_teleporter->map, NULL, 0);
294 return (tmp == NULL);
295 }
296
297 void
298 recursive_roll (object *op, int dir, object *pusher)
299 {
300 if (!roll_ob (op, dir, pusher))
301 {
302 new_draw_info_format (NDI_UNIQUE, 0, pusher, "You fail to push the %s.", query_name (op));
303 return;
304 }
305 (void) move_ob (pusher, dir, pusher);
306 new_draw_info_format (NDI_BLACK, 0, pusher, "You move the %s.", query_name (op));
307 return;
308 }
309
310 /*
311 * This is a new version of blocked, this one handles objects
312 * that can be passed through by monsters with the CAN_PASS_THRU defined.
313 *
314 * very new version handles also multipart objects
315 * This is currently only used for the boulder roll code.
316 * Returns 1 if object does not fit, 0 if it does.
317 */
318
319 int
320 try_fit (object *op, maptile *m, int x, int y)
321 {
322 object *tmp, *more;
323 sint16 tx, ty;
324 int mflags;
325 maptile *m2;
326
327 if (op->head)
328 op = op->head;
329
330 for (more = op; more; more = more->more)
331 {
332 tx = x + more->x - op->x;
333 ty = y + more->y - op->y;
334
335 mflags = get_map_flags (m, &m2, tx, ty, &tx, &ty);
336
337 if (mflags & P_OUT_OF_MAP)
338 return 1;
339
340 for (tmp = GET_MAP_OB (m2, tx, ty); tmp; tmp = tmp->above)
341 {
342 if (tmp->head == op || tmp == op)
343 continue;
344
345 if ((QUERY_FLAG (tmp, FLAG_ALIVE) && tmp->type != DOOR))
346 return 1;
347
348 if (OB_MOVE_BLOCK (op, tmp))
349 return 1;
350
351 }
352 }
353 return 0;
354 }
355
356 /*
357 * this is not perfect yet.
358 * it does not roll objects behind multipart objects properly.
359 * Support for rolling multipart objects is questionable.
360 */
361
362 int
363 roll_ob (object *op, int dir, object *pusher)
364 {
365 object *tmp;
366 sint16 x, y;
367 int flags;
368 maptile *m;
369 MoveType move_block;
370
371 if (op->head)
372 op = op->head;
373
374 x = op->x + freearr_x[dir];
375 y = op->y + freearr_y[dir];
376
377 if (!QUERY_FLAG (op, FLAG_CAN_ROLL) || (op->weight && random_roll (0, op->weight / 50000 - 1, pusher, PREFER_LOW) > pusher->stats.Str))
378 return 0;
379
380 m = op->map;
381 flags = get_map_flags (m, &m, x, y, &x, &y);
382
383 if (flags & (P_OUT_OF_MAP | P_IS_ALIVE))
384 return 0;
385
386 move_block = GET_MAP_MOVE_BLOCK (m, x, y);
387
388 /* If the target space is not blocked, no need to look at the objects on it */
389 if ((op->move_type & move_block) == op->move_type)
390 {
391 for (tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above)
392 {
393 if (tmp->head == op)
394 continue;
395 if (OB_MOVE_BLOCK (op, tmp) && !roll_ob (tmp, dir, pusher))
396 return 0;
397 }
398 }
399 if (try_fit (op, m, x, y))
400 return 0;
401
402 op->remove ();
403 for (tmp = op; tmp != NULL; tmp = tmp->more)
404 tmp->x += freearr_x[dir], tmp->y += freearr_y[dir];
405 insert_ob_in_map (op, op->map, pusher, 0);
406 return 1;
407 }
408
409 /* returns 1 if pushing invokes a attack, 0 when not */
410 int
411 push_ob (object *who, int dir, object *pusher)
412 {
413 int str1, str2;
414 object *owner;
415
416 if (who->head != NULL)
417 who = who->head;
418 owner = who->owner;
419
420 /* Wake up sleeping monsters that may be pushed */
421 CLEAR_FLAG (who, FLAG_SLEEP);
422
423 /* player change place with his pets or summoned creature */
424 /* TODO: allow multi arch pushing. Can't be very difficult */
425 if (who->more == NULL
426 && ((owner && owner->contr && pusher->contr && same_party (owner->contr->party, pusher->contr->party))
427 || owner == pusher)
428 )
429 {
430 int temp;
431 maptile *m;
432
433 who->remove ();
434 pusher->remove ();
435 temp = pusher->x;
436 pusher->x = who->x;
437 who->x = temp;
438
439 temp = pusher->y;
440 pusher->y = who->y;
441 who->y = temp;
442
443 m = pusher->map;
444 pusher->map = who->map;
445 who->map = m;
446
447 insert_ob_in_map (who, who->map, pusher, 0);
448 insert_ob_in_map (pusher, pusher->map, pusher, 0);
449 return 0;
450 }
451
452 /* We want ONLY become enemy of evil, unaggressive monster. We must RUN in them */
453 /* In original we have here a unaggressive check only - that was the reason why */
454 /* we so often become an enemy of friendly monsters... */
455 /* funny: was they set to unaggressive 0 (= not so nice) they don't attack */
456 if (owner != pusher && pusher->type == PLAYER && who->type != PLAYER &&
457 !QUERY_FLAG (who, FLAG_FRIENDLY) && !QUERY_FLAG (who, FLAG_NEUTRAL))
458 {
459 if (pusher->contr->run_on) /* only when we run */
460 {
461 new_draw_info_format (NDI_UNIQUE, 0, pusher, "You start to attack %s!!", &who->name);
462 CLEAR_FLAG (who, FLAG_UNAGGRESSIVE); /* the sucker don't like you anymore */
463 who->enemy = pusher;
464 return 1;
465 }
466 else
467 new_draw_info_format (NDI_UNIQUE, 0, pusher, "You avoid attacking %s.", &who->name);
468 }
469
470 /* now, lets test stand still. we NEVER can push stand_still monsters. */
471 if (QUERY_FLAG (who, FLAG_STAND_STILL))
472 {
473 new_draw_info_format (NDI_UNIQUE, 0, pusher, "You can't push %s.", &who->name);
474 return 0;
475 }
476
477 /* This block is basically if you are pushing friendly but
478 * non pet creaturs.
479 * It basically does a random strength comparision to
480 * determine if you can push someone around. Note that
481 * this pushes the other person away - its not a swap.
482 */
483
484 str1 = (who->stats.Str > 0 ? who->stats.Str : who->level);
485 str2 = (pusher->stats.Str > 0 ? pusher->stats.Str : pusher->level);
486 if (QUERY_FLAG (who, FLAG_WIZ) ||
487 random_roll (str1, str1 * 5 / 2, who, PREFER_HIGH) >=
488 random_roll (str2, str2 * 5 / 2, pusher, PREFER_HIGH) || !move_object (who, dir))
489 {
490 if (who->type == PLAYER)
491 new_draw_info_format (NDI_UNIQUE, 0, who, "%s tried to push you.", &pusher->name);
492
493 return 0;
494 }
495
496 /* If we get here, the push succeeded.
497 * Let everyone know the status.
498 */
499 if (who->type == PLAYER)
500 {
501 new_draw_info_format (NDI_UNIQUE, 0, who, "%s pushed you.", &pusher->name);
502 }
503 if (pusher->type == PLAYER)
504 {
505 new_draw_info_format (NDI_UNIQUE, 0, pusher, "You pushed %s back.", &who->name);
506 }
507
508 return 1;
509 }