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

# User Rev Content
1 elmex 1.1 /*
2 root 1.19 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 pippijn 1.15 *
4 root 1.19 * 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 pippijn 1.15 *
8 root 1.21 * 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 pippijn 1.15 *
13 root 1.21 * 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 pippijn 1.15 *
18 root 1.21 * 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 root 1.19 *
21     * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 pippijn 1.15 */
23 elmex 1.1
24     #include <global.h>
25     #ifndef __CEXTRACT__
26 root 1.7 # include <sproto.h>
27 elmex 1.1 #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 root 1.7 int
38     move_object (object *op, int dir)
39     {
40     return move_ob (op, dir, op);
41 elmex 1.1 }
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 root 1.7 int
53     move_ob (object *op, int dir, object *originator)
54 elmex 1.1 {
55 root 1.7 sint16 newx = op->x + freearr_x[dir];
56     sint16 newy = op->y + freearr_y[dir];
57     object *tmp;
58 root 1.9 maptile *m;
59 root 1.7 int mflags;
60 elmex 1.1
61 root 1.7 if (op == NULL)
62     {
63     LOG (llevError, "Trying to move NULL.\n");
64     return 0;
65 elmex 1.1 }
66    
67 root 1.7 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 elmex 1.1
76 root 1.7 /* 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 elmex 1.1
82 root 1.7 /* 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 root 1.12 if (op->more && !move_ob (op->more, dir, op->more->head))
87 root 1.7 return 0;
88 elmex 1.1
89 root 1.7 op->direction = dir;
90 elmex 1.1
91 root 1.7 if (op->will_apply & 4)
92     check_earthwalls (op, m, newx, newy);
93 root 1.12
94 root 1.7 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 elmex 1.1
107 root 1.7 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 elmex 1.1 }
113    
114 root 1.7 /* 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 elmex 1.1
120 root 1.13 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 elmex 1.1
136 root 1.7 /* 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 elmex 1.1 }
146    
147 root 1.7 /* insert_ob_in_map will deal with any tiling issues */
148     insert_ob_in_map (op, m, originator, 0);
149 elmex 1.1
150 root 1.7 return 1;
151 elmex 1.1 }
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 root 1.7 int
167     transfer_ob (object *op, int x, int y, int randomly, object *originator)
168 elmex 1.1 {
169 root 1.7 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 root 1.10 op->remove ();
183 root 1.7 for (tmp = op; tmp != NULL; tmp = tmp->more)
184 root 1.20 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 elmex 1.1
187 root 1.7 tmp = insert_ob_in_map (op, op->map, originator, 0);
188     if (tmp)
189     return 0;
190     else
191     return 1;
192 elmex 1.1 }
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 root 1.7 int
207     teleport (object *teleporter, uint8 tele_type, object *user)
208 elmex 1.1 {
209 root 1.7 object *altern;
210     int i, j, k, nrofalt = 0;
211     object *other_teleporter, *tmp;
212 root 1.9 maptile *m;
213 root 1.7 sint16 sx, sy;
214    
215     if (user == NULL)
216     return 0;
217     if (user->head != NULL)
218     user = user->head;
219 elmex 1.1
220 root 1.7 /* 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 root 1.12 other_teleporter = GET_MAP_OB (teleporter->map, teleporter->x + i, teleporter->y + j);
233 root 1.7
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 elmex 1.1
244 root 1.7 if (!nrofalt)
245     {
246     LOG (llevError, "No alternative teleporters around!\n");
247     return 0;
248 elmex 1.1 }
249    
250 root 1.7 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 root 1.4
270 root 1.7 if (!OB_TYPE_MOVE_BLOCK (user, GET_MAP_MOVE_BLOCK (m, sx, sy)))
271     break;
272 root 1.4
273     }
274 root 1.7 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 root 1.4 }
280     }
281 root 1.7 else
282     return 0;
283 elmex 1.1 }
284    
285 root 1.10 user->remove ();
286 elmex 1.1
287 root 1.7 /* Update location for the object */
288     for (tmp = user; tmp != NULL; tmp = tmp->more)
289     {
290 root 1.20 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 elmex 1.1 }
293 root 1.7 tmp = insert_ob_in_map (user, other_teleporter->map, NULL, 0);
294     return (tmp == NULL);
295 elmex 1.1 }
296    
297 root 1.7 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 elmex 1.1 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 root 1.7 int
320 root 1.9 try_fit (object *op, maptile *m, int x, int y)
321 elmex 1.1 {
322 root 1.7 object *tmp, *more;
323     sint16 tx, ty;
324     int mflags;
325 root 1.9 maptile *m2;
326 elmex 1.1
327 root 1.7 if (op->head)
328     op = op->head;
329 elmex 1.1
330 root 1.7 for (more = op; more; more = more->more)
331     {
332     tx = x + more->x - op->x;
333     ty = y + more->y - op->y;
334 elmex 1.1
335 root 1.7 mflags = get_map_flags (m, &m2, tx, ty, &tx, &ty);
336 elmex 1.1
337 root 1.7 if (mflags & P_OUT_OF_MAP)
338     return 1;
339 elmex 1.1
340 root 1.12 for (tmp = GET_MAP_OB (m2, tx, ty); tmp; tmp = tmp->above)
341 root 1.7 {
342     if (tmp->head == op || tmp == op)
343     continue;
344 elmex 1.1
345 root 1.7 if ((QUERY_FLAG (tmp, FLAG_ALIVE) && tmp->type != DOOR))
346     return 1;
347 elmex 1.1
348 root 1.7 if (OB_MOVE_BLOCK (op, tmp))
349     return 1;
350 elmex 1.1
351 root 1.4 }
352 elmex 1.1 }
353 root 1.7 return 0;
354 elmex 1.1 }
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 root 1.7 int
363     roll_ob (object *op, int dir, object *pusher)
364     {
365     object *tmp;
366     sint16 x, y;
367     int flags;
368 root 1.9 maptile *m;
369 root 1.7 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 elmex 1.1
380 root 1.7 m = op->map;
381     flags = get_map_flags (m, &m, x, y, &x, &y);
382 elmex 1.1
383 root 1.7 if (flags & (P_OUT_OF_MAP | P_IS_ALIVE))
384     return 0;
385 elmex 1.1
386 root 1.7 move_block = GET_MAP_MOVE_BLOCK (m, x, y);
387 elmex 1.1
388 root 1.7 /* 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 root 1.12 for (tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above)
392 root 1.7 {
393     if (tmp->head == op)
394 root 1.4 continue;
395 root 1.7 if (OB_MOVE_BLOCK (op, tmp) && !roll_ob (tmp, dir, pusher))
396 root 1.4 return 0;
397     }
398 elmex 1.1 }
399 root 1.7 if (try_fit (op, m, x, y))
400     return 0;
401 elmex 1.1
402 root 1.10 op->remove ();
403 root 1.7 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 elmex 1.1 }
408    
409     /* returns 1 if pushing invokes a attack, 0 when not */
410 root 1.7 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 root 1.11 owner = who->owner;
419 root 1.7
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 root 1.18 && ((owner && owner->contr && pusher->contr && same_party (owner->contr->party, pusher->contr->party))
427     || owner == pusher)
428 root 1.7 )
429     {
430     int temp;
431 root 1.9 maptile *m;
432 root 1.7
433 root 1.10 who->remove ();
434     pusher->remove ();
435 root 1.7 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 root 1.4
443 root 1.7 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 elmex 1.1 }
451    
452 root 1.7 /* 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 root 1.16 new_draw_info_format (NDI_UNIQUE, 0, pusher, "You start to attack %s!!", &who->name);
462 root 1.7 CLEAR_FLAG (who, FLAG_UNAGGRESSIVE); /* the sucker don't like you anymore */
463     who->enemy = pusher;
464     return 1;
465 root 1.4 }
466 root 1.7 else
467 root 1.16 new_draw_info_format (NDI_UNIQUE, 0, pusher, "You avoid attacking %s.", &who->name);
468 elmex 1.1 }
469    
470 root 1.7 /* now, lets test stand still. we NEVER can push stand_still monsters. */
471     if (QUERY_FLAG (who, FLAG_STAND_STILL))
472 elmex 1.1 {
473 root 1.7 new_draw_info_format (NDI_UNIQUE, 0, pusher, "You can't push %s.", &who->name);
474     return 0;
475 elmex 1.1 }
476 root 1.7
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 root 1.17 random_roll (str1, str1 * 5 / 2, who, PREFER_HIGH) >=
488     random_roll (str2, str2 * 5 / 2, pusher, PREFER_HIGH) || !move_object (who, dir))
489 root 1.7 {
490     if (who->type == PLAYER)
491 root 1.17 new_draw_info_format (NDI_UNIQUE, 0, who, "%s tried to push you.", &pusher->name);
492    
493 root 1.7 return 0;
494 elmex 1.1 }
495    
496 root 1.7 /* 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 elmex 1.1 }
503 root 1.7 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 elmex 1.1 }