ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/move.C
Revision: 1.7
Committed: Sun Sep 10 15:59:57 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.6: +333 -312 lines
Log Message:
indent

File Contents

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