1 // nazghul - an old-school RPG engine
2 // Copyright (C) 2002, 2003 Gordon McNutt
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License as published by the Free
6 // Software Foundation; either version 2 of the License, or (at your option)
7 // any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but WITHOUT
10 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 // more details.
13 //
14 // You should have received a copy of the GNU General Public License along with
15 // this program; if not, write to the Free Foundation, Inc., 59 Temple Place,
16 // Suite 330, Boston, MA 02111-1307 USA
17 //
18 // Gordon McNutt
19 // gmcnutt@users.sourceforge.net
20 //
21 /* 12/14/2002 Sam Glasby added place_get_terrain()
22 */
23 #include "sound.h"
24 #include "place.h"
25 #include "sprite.h"
26 #include "terrain.h"
27 #include "hash.h"
28 #include "screen.h"
29 #include "player.h"
30 #include "sky.h"
31 #include "console.h"
32 #include "wq.h"
33 #include "Field.h"
34 #include "vehicle.h"
35 #include "session.h"
36 #include "log.h"
37 #include "factions.h"
38 #include "vmask.h"
39 #include "combat.h"
40 #include "event.h" /* for demo */
41 #include "kern_intvar.h"
42
43 // #define DEBUG
44 // #undef debug_h
45 #include "debug.h"
46
47 #include <stdlib.h>
48 #include <string.h>
49 #include <math.h>
50
51 #ifndef PLACE_PROFILE
52 #define PLACE_PROFILE 0
53 #endif
54
55 #define HIGHLIGHT_W 2
56 #define HIGHLIGHT_H 2
57
58 #define BLOCKS_PASSABILITY -1
59 #define IGNORES_PASSABILITY 0
60 #define ALLOWS_PASSABILITY 1
61
62 #define WRAP(c,max) (((c) + (max)) % (max))
63 #define WRAP_DISTANCE(a,b,max) (min((a) + (max) - (b), (b) - (a)))
64 #define INDEX(x,y,w) ((x) + (y) * (w))
65 #define WRAP_COORDS(place, x, y) do { \
66 if ((place)->wraps) { \
67 (x) = place_wrap_x((place), (x)); \
68 (y) = place_wrap_y((place), (y)); \
69 } \
70 } while(0)
71
72 #define TERRAIN(p,x,y) ((p)->terrain_map->terrain[(y) * \
73 (p)->terrain_map->w + (x)])
74
75 struct place_pathfind_context {
76 struct place *place;
77 int target_x;
78 int target_y;
79 int pflags;
80 class Object *requestor;
81 };
82
83 typedef struct closure_list {
84 struct list list;
85 closure_t *closure;
86 } closure_list_t;
87
88 struct place *Place;
89
90 /* Tile API ******************************************************************/
91 struct tile {
92 struct olist hashlink;
93 struct node objstack;
94 struct place *subplace;
95 class Vehicle *vehicle;
96 int objects;
97 int lock;
98 };
tile_new(int hashkey)99 static struct tile *tile_new(int hashkey)
100 {
101 struct tile *tile;
102 CREATE(tile, struct tile, 0);
103 olist_init(&tile->hashlink);
104 tile->hashlink.key = hashkey;
105 node_init(&tile->objstack);
106 return tile;
107 }
tile_del(struct tile * tile)108 static void tile_del(struct tile *tile)
109 {
110 /*
111 * FIXME! use a straightforward refcount on the tile instead of an
112 * explicit lock
113 */
114 // Locking prevents me from destroying a tile while the place_for_each
115 // algorithm is using it.
116 if (!tile->lock) {
117 assert(!tile->objects);
118 list_remove(&tile->hashlink.list);
119 //dbg("tile_del: tile=%08lx\n", tile);
120 free(tile);
121 }
122 }
tile_for_each_object(struct tile * tile,void (* fx)(class Object * obj,void * data),void * data)123 static void tile_for_each_object(struct tile *tile,
124 void (*fx)(class Object *obj, void *data),
125 void *data)
126 {
127 struct node *elem;
128 class Object *obj;
129
130 for (elem = node_next(&tile->objstack); elem != &tile->objstack; ) {
131 obj = (class Object *)elem->ptr;
132 elem = node_next(elem);
133 fx(obj, data);
134 }
135
136 if (tile->vehicle)
137 fx(tile->vehicle, data);
138 }
139
tile_get_filtered_object(struct tile * tile,int (* filter)(Object *))140 static Object *tile_get_filtered_object(struct tile *tile, int (*filter)(Object*))
141 {
142 struct node *elem;
143 class Object *obj;
144
145 /* Traverse the list in reverse order, so the first item returned is
146 * the one shown by the UI as topmost in the pile. */
147 for (elem = node_prev(&tile->objstack); elem != &tile->objstack; ) {
148 obj = (class Object *)elem->ptr;
149 elem = node_prev(elem);
150 if (filter(obj))
151 return obj;
152 }
153
154 if (tile->vehicle && filter(tile->vehicle))
155 return tile->vehicle;
156
157 return NULL;
158 }
159
tile_get_top_object_at_layer(struct tile * tile,enum layer layer)160 static Object *tile_get_top_object_at_layer(struct tile *tile, enum layer layer)
161 {
162 struct node *node;
163
164 node = node_lookup(&tile->objstack, layer);
165 if (node)
166 return (Object*)node->ptr;
167
168 return NULL;
169 }
170
tile_get_light(struct tile * tile)171 static int tile_get_light(struct tile *tile)
172 {
173 struct node *node = NULL;
174 int light = 0;
175 class Object *obj = NULL;
176
177 /* Check for a vehicle */
178 if (tile->vehicle)
179 light += tile->vehicle->getLight();
180
181 /* Check all objects */
182 node_for_each(&tile->objstack, node) {
183 obj = (class Object *)node->ptr;
184 light += obj->getLight();
185 }
186
187 return light;
188 }
189
tile_is_transparent(struct tile * tile)190 static int tile_is_transparent(struct tile *tile)
191 {
192 struct node *elem;
193 class Object *obj;
194
195 for (elem = node_next(&tile->objstack); elem != &tile->objstack; ) {
196 obj = (class Object *)elem->ptr;
197 elem = node_next(elem);
198 if (obj->isOpaque())
199 return 0;
200 }
201
202 return 1;
203 }
204
tile_remove_object(struct tile * tile,class Object * object)205 static void tile_remove_object(struct tile *tile, class Object *object)
206 {
207 if (object->isType(VEHICLE_ID)) {
208 if (tile->vehicle == object) {
209 tile->vehicle = 0;
210 tile->objects--;
211 }
212 /* 30Jul2003 gmcnutt: otherwise this vehicle must be occupied,
213 * in which case it does not occupy the tile (it's occupant
214 * does). */
215 } else {
216 /* Splice out the object's container link node and release
217 * it. */
218 assert(object->clink);
219 node_remove(object->clink);
220 node_unref(object->clink);
221 object->clink = NULL;
222 tile->objects--;
223 }
224
225
226 if (!tile->objects) {
227 tile_del(tile);
228 }
229
230 obj_dec_ref(object);
231 }
232
tile_add_object(struct tile * tile,class Object * object)233 static void tile_add_object(struct tile *tile, class Object *object)
234 {
235 obj_inc_ref(object);
236
237 if (object->isType(VEHICLE_ID)) {
238 if (tile->vehicle) {
239 assert(0);
240 }
241 tile->vehicle = (class Vehicle *) object;
242 tile->objects++;
243 return;
244 }
245
246 /* Create a new container link node for the object and add it to the
247 * tile's object stack. */
248 assert(!object->clink);
249 object->clink = node_new_keyed(object, object->getLayer());
250 node_add_keyed(&tile->objstack, object->clink);
251
252 tile->objects++;
253 }
254
tile_add_subplace(struct tile * tile,struct place * place)255 static int tile_add_subplace(struct tile *tile, struct place *place)
256 {
257 if (tile->subplace)
258 return -1;
259
260 tile->subplace = place;
261 tile->objects++;
262 return 0;
263 }
264
tile_remove_subplace(struct tile * tile)265 static void tile_remove_subplace(struct tile *tile)
266 {
267 if (tile->subplace) {
268 tile->subplace = 0;
269 tile->objects--;
270 }
271
272 if (!tile->objects) {
273 tile_del(tile);
274 }
275 }
276
tile_paint(struct tile * tile,int sx,int sy)277 static void tile_paint(struct tile *tile, int sx, int sy)
278 {
279 struct node *node;
280 Object *obj;
281 struct sprite *sprite;
282
283 /* Check for a subplace. The temp combat place won't have a sprite. */
284 if (tile->subplace && tile->subplace->sprite) {
285 assert(tile->subplace->sprite);
286 sprite_paint(tile->subplace->sprite, 0, sx, sy);
287 }
288
289 /* Check for a vehicle */
290 if (tile->vehicle) {
291 tile->vehicle->paint(sx, sy);
292 }
293
294 node_for_each(&tile->objstack, node) {
295 obj = (Object*)node->ptr;
296 sprite = obj->getSprite();
297 if (!sprite)
298 continue;
299
300 // Handle invisible objects and alpha-blending when
301 // invisible objects are revealed. If the global "reveal" flag
302 // is not set then do not paint invisible objects. Otherwise
303 // temporarily set the object's sprite to 'shaded' and paint
304 // it. After painting I need to clear the shaded flags in case
305 // other objects use this sprite, too.
306
307 if (!obj->isVisible()) {
308 if (!Reveal && !obj->isShaded())
309 continue;
310 sprite_fade(sprite);
311 } else if (obj->isShaded()) {
312 sprite_fade(sprite);
313 }
314
315 obj->paint(sx, sy);
316
317 if (obj->isSelected()) {
318 /* Highlight */
319 SDL_Rect rect;
320 rect.x = sx;
321 rect.y = sy;
322 rect.w = TILE_W;
323 rect.h = TILE_H;
324 screenHighlight(&rect);
325 }
326
327 // Paint a red box around hostiles, green around allies, and
328 // yellow around neutrals.
329 else if (Session->show_boxes
330 && Session->subject
331 && obj->getLayer() == being_layer) {
332 SDL_Rect rect;
333 Uint32 color = Yellow;
334 rect.x = sx;
335 rect.y = sy;
336 rect.w = TILE_W;
337 rect.h = TILE_H;
338 if (are_allies(Session->subject, (Being*)obj))
339 color = Green;
340 else if (are_hostile(Session->subject, (Being*)obj))
341 color = Red;
342 screenHighlightColored(&rect, color);
343 }
344
345 if (sprite_is_faded(sprite))
346 sprite_unfade(sprite);
347 }
348 }
349
350 /*****************************************************************************/
351
352 static void place_for_each_tile(struct place *place,
353 void (*fx)(struct tile *tile, void *data),
354 void *data);
355
place_is_diagonal(struct place * place,int from_x,int from_y,int to_x,int to_y)356 static inline int place_is_diagonal(struct place *place, int from_x,
357 int from_y, int to_x, int to_y)
358 {
359 /* FIXME: probably doesn't work on wrapping places... */
360 int dx = from_x - to_x;
361 int dy = from_y - to_y;
362 return (((dx==1) || (dx==-1))
363 && ((dy==1) || (dy==-1)));
364 }
365
place_set_default_edge_entrance(struct place * place)366 static void place_set_default_edge_entrance(struct place *place)
367 {
368 /* Northwest: lower right corner */
369 place->edge_entrance[NORTHWEST][0] = place_w(place) - 1;
370 place->edge_entrance[NORTHWEST][1] = place_h(place) - 1;
371
372 /* North: lower center */
373 place->edge_entrance[NORTH][0] = place_w(place) / 2;
374 place->edge_entrance[NORTH][1] = place_h(place) - 1;
375
376 /* Northeast: lower left corner */
377 place->edge_entrance[NORTHEAST][0] = 0;
378 place->edge_entrance[NORTHEAST][1] = place_h(place) - 1;
379
380 /* West: right center */
381 place->edge_entrance[WEST][0] = place_w(place) - 1;
382 place->edge_entrance[WEST][1] = place_h(place) / 2;
383
384 /* Here: center */
385 place->edge_entrance[HERE][0] = place_w(place) / 2;
386 place->edge_entrance[HERE][1] = place_h(place) / 2;
387
388 /* East: left center */
389 place->edge_entrance[EAST][0] = 0;
390 place->edge_entrance[EAST][1] = place_h(place) / 2;
391
392 /* Southwest: upper right corner */
393 place->edge_entrance[SOUTHWEST][0] = place_w(place) - 1;
394 place->edge_entrance[SOUTHWEST][1] = 0;
395
396 /* South: upper center */
397 place->edge_entrance[SOUTH][0] = place_w(place) / 2;
398 place->edge_entrance[SOUTH][1] = 0;
399
400 /* Southeast: upper left corner */
401 place->edge_entrance[SOUTHEAST][0] = 0;
402 place->edge_entrance[SOUTHEAST][1] = 0;
403 }
404
405 /*****************************************************************************
406 * place_init_turn_list - abstract wrapper for simple init
407 *****************************************************************************/
place_init_turn_list(struct place * place)408 static void place_init_turn_list(struct place *place)
409 {
410 node_init(&place->turn_list);
411 }
412
413 /*****************************************************************************
414 * place_add_to_turn_list - add an object to the turn list
415 *****************************************************************************/
place_add_to_turn_list(struct place * place,Object * object)416 static void place_add_to_turn_list(struct place *place, Object *object)
417 {
418 struct node *node;
419
420 /* The object should not be pointing to an existing node. */
421 assert(! object->turn_list);
422
423 /* Create a new node that points to the object. */
424 node = node_new(object);
425
426 /* Link the object back to its node. */
427 object->turn_list = node;
428
429 /* Add the node to the turn list. */
430 node_add(&place->turn_list, node);
431 }
432
433 /*****************************************************************************
434 * place_remove_from_turn_list - safely remove an object from the turn list
435 *****************************************************************************/
place_remove_from_turn_list(struct place * place,Object * object)436 static void place_remove_from_turn_list(struct place *place, Object *object)
437 {
438
439 struct node *node;
440
441 /* Get a convenience pointer to the node which holds the object */
442 node = object->turn_list;
443
444 /* Should be valid */
445 assert(node);
446
447 /* Note: this is a bit subtle. If it just so happens that we are in the
448 * process of running place_exec, there is a chance that the object we
449 * are removing here is the next object to be processed in the
450 * turn_list. In this special case we must setup the next object after
451 * this to be processed instead. This can happen when one object
452 * destroys or picks up another object on its turn. */
453 if (place->turn_elem == node) {
454 place->turn_elem = node_next(node);
455 }
456
457 /* Unlink the node from the turn list. */
458 node_remove(node);
459
460 /* Zero out the object's link to the node */
461 object->turn_list = NULL;
462
463 /* Release the node for destruction. */
464 node_unref(node);
465 }
466
place_new(const char * tag,const char * name,struct sprite * sprite,struct terrain_map * terrain_map,int wraps,int underground,int wilderness,int wild_combat)467 struct place *place_new(const char *tag,
468 const char *name,
469 struct sprite *sprite,
470 struct terrain_map *terrain_map,
471 int wraps,
472 int underground,
473 int wilderness,
474 int wild_combat)
475
476 {
477 struct place *place;
478
479 CREATE(place, struct place, 0);
480
481 place->tag = strdup(tag);
482 assert(place->tag);
483
484 place->name = strdup(name);
485 assert(place->name);
486
487 place->objects = hash_create(31);
488 assert(place->objects);
489
490 place->magic = PLACE_MAGIC;
491 place->sprite = sprite;
492
493 /* Give the place its own copy of the original map so it can modify it
494 * at run-time (a clever implementation would do copy-on-write...) */
495 place->terrain_map = terrain_map_clone(terrain_map, NULL);
496
497 place->scale = wilderness ? WILDERNESS_SCALE : NON_WILDERNESS_SCALE;
498 place->original_terrain_map = terrain_map;
499 place->wraps = wraps;
500 place->underground = underground;
501 place->wilderness = wilderness;
502 place->is_wilderness_combat = wild_combat;
503
504 place_init_turn_list(place);
505 list_init(&place->subplaces);
506 list_init(&place->container_link);
507 list_init(&place->on_entry_hook);
508 place->turn_elem = &place->turn_list;
509
510 place_set_default_edge_entrance(place);
511
512
513 return place;
514 }
515
place_del_tile_object_visitor(class Object * obj,void * data)516 void place_del_tile_object_visitor(class Object *obj, void *data)
517 {
518 // Called by tile_for_each_object()
519 struct tile *tile = (struct tile*)data;
520 tile_remove_object(tile, obj);
521 }
522
place_del_tile_visitor(struct tile * tile,void * data)523 void place_del_tile_visitor(struct tile *tile, void *data)
524 {
525 // Called by place_for_each_tile()
526 tile->lock++;
527 tile_for_each_object(tile, place_del_tile_object_visitor, tile);
528 if (tile->subplace) {
529 //place_del(tile->subplace);
530 tile_remove_subplace(tile);
531 }
532 tile->lock--;
533 tile_del(tile);
534 }
535
place_del_on_entry_hook(struct place * place)536 static void place_del_on_entry_hook(struct place *place)
537 {
538 struct list *elem = place->on_entry_hook.next;
539 while (elem != &place->on_entry_hook) {
540 closure_list_t *node = (closure_list_t*)elem;
541 elem = elem->next;
542 list_remove(&node->list);
543 closure_unref(node->closure);
544 free(node);
545 }
546 }
547
place_del(struct place * place)548 void place_del(struct place *place)
549 {
550 // --------------------------------------------------------------------
551 // If the place is locked then we cannot delete it now. Mark it for
552 // death so that it will be deleted when unlocked.
553 // --------------------------------------------------------------------
554
555 //dbg("place_del(%s)\n", place->name);
556
557 if (place_is_locked(place)) {
558 place_mark_for_death(place);
559 return;
560 }
561
562 // Destroy all tiles, objects and subplaces recursively.
563 place_for_each_tile(place, place_del_tile_visitor, 0);
564 hash_destroy(place->objects);
565
566 //dbg("place_del %s\n", place->tag);
567
568 if (place->tag)
569 free(place->tag);
570 if (place->name)
571 free(place->name);
572 if (place->terrain_map)
573 terrain_map_unref(place->terrain_map);
574
575 place_del_on_entry_hook(place);
576
577 free(place);
578 }
579
place_generic_is_passable(class Object * subject,int flags,int pclass,struct closure * effect)580 static int place_generic_is_passable(class Object *subject, int flags,
581 int pclass, struct closure *effect)
582 {
583 // Is it passable?
584 if (subject->isPassable(pclass))
585 return 1;
586
587 // Is the caller actually trying to move the subject there?
588 if (0 == (flags & PFLAG_MOVEATTEMPT))
589 return 0;
590
591 // Is there an effect to apply on failed entry?
592 if (effect) {
593 subject->applyEffect(effect);
594 }
595
596 return 0;
597 }
598
place_terrain_is_passable(struct place * place,int x,int y,class Object * subject,int flags)599 static int place_terrain_is_passable(struct place *place, int x, int y,
600 class Object *subject, int flags)
601 {
602 struct terrain *terrain;
603
604 terrain = place->terrain_map->terrain[y * place->terrain_map->w + x];
605
606 // Can we use the generic passability test?
607 if (flags & PFLAG_IGNOREVEHICLES)
608 return place_generic_is_passable(subject, flags,
609 terrain_pclass(terrain),
610 terrain->effect);
611
612 // Is the terrain passable?
613 if (subject->isPassable(terrain_pclass(terrain)))
614 return 1;
615
616 // Is there a vehicle there?
617 if (place_get_vehicle(place, x, y))
618 return 1;
619
620 // Is the caller actually trying to move the subject there?
621 if (0 == (flags & PFLAG_MOVEATTEMPT))
622 return 0;
623
624 // Does the terrain apply an effect on failed entry?
625 if (terrain->effect) {
626 subject->applyEffect(terrain->effect);
627 }
628
629 return 0;
630 }
631
place_field_is_passable(struct place * place,int x,int y,class Object * subject,int flags)632 static int place_field_is_passable(struct place *place, int x, int y,
633 class Object *subject, int flags)
634 {
635 class Field *field;
636
637 // Is there a field there?
638 field = (class Field *) place_get_object(place, x, y, field_layer);
639 if (! field)
640 return 1;
641
642 return place_generic_is_passable(subject, flags,
643 field->getPclass(),
644 field->getObjectType()->effect);
645 }
646
place_obj_is_passable(class Object * obj,class Object * subject,int flags)647 static int place_obj_is_passable(class Object *obj,
648 class Object *subject, int flags)
649 {
650 int pclass = obj->getPclass();
651
652 // Does the object care about passability?
653 if (pclass == PCLASS_NONE)
654 return IGNORES_PASSABILITY;
655
656 // Is the obj passable?
657 if (subject->isPassable(obj->getPclass()))
658 return ALLOWS_PASSABILITY;
659
660 return BLOCKS_PASSABILITY;
661
662 }
663
place_is_passable(struct place * place,int x,int y,class Object * subject,int flags)664 int place_is_passable(struct place *place, int x, int y,
665 class Object *subject, int flags)
666 {
667 class Object *mech;
668 int tfeat_pass = IGNORES_PASSABILITY;
669
670 // For a wrapping place, wrap out-of-bounds x,y
671 // For a non-wrapping place, return impassable.
672 if (place->wraps) {
673 x = WRAP(x, place->terrain_map->w);
674 y = WRAP(y, place->terrain_map->h);
675 } else if (y < 0 || y >= place->terrain_map->h ||
676 x < 0 || x >= place->terrain_map->w)
677 return 0;
678
679 // Does the caller want to check terrain features?
680 if (0 == (flags & PFLAG_IGNORETFEAT)) {
681 class Object *tfeat = NULL;
682
683 tfeat = place_get_object(place, x, y, tfeat_layer);
684 if (tfeat) {
685 tfeat_pass = place_obj_is_passable(tfeat, subject,
686 flags);
687
688 // Does it specifically block passability?
689 if (tfeat_pass == BLOCKS_PASSABILITY)
690 return 0;
691 }
692 }
693
694 // Does the caller want to check terrain, and if so is there no
695 // overriding terrain feature?
696 if (0 == (flags & PFLAG_IGNORETERRAIN) &&
697 IGNORES_PASSABILITY == tfeat_pass) {
698
699 // Is the terrain passable?
700 if (! place_terrain_is_passable(place, x, y, subject, flags))
701 return 0;
702 }
703
704 // Does the caller want to check fields?
705 if (0 == (flags & PFLAG_IGNOREFIELDS)) {
706
707 // Is the field passable?
708 if (! place_field_is_passable(place, x, y, subject, flags))
709 return 0;
710
711 }
712
713 // Does the caller want to check mechs?
714 if (0 == (flags & PFLAG_IGNOREMECHS)) {
715
716 // Is the mech passable?
717 mech = place_get_object(place, x, y, mech_layer);
718 if (mech &&
719 (place_obj_is_passable(mech, subject, flags) ==
720 BLOCKS_PASSABILITY))
721 return 0;
722 }
723
724 return 1;
725 }
726
place_move_is_passable(struct place * place,int from_x,int from_y,int to_x,int to_y,class Object * subject,int flags)727 int place_move_is_passable(struct place *place, int from_x, int from_y,
728 int to_x, int to_y,
729 class Object *subject, int flags)
730 {
731 /* check destination tile */
732 if (! place_is_passable(place, to_x, to_y, subject, flags)) {
733 return 0;
734 }
735
736 /* check if this is a one-tile diagonal move then check both adjacent
737 * neighbors. */
738 if (place_is_diagonal(place, from_x, from_y, to_x, to_y)
739 && ! place_is_passable(place, from_x, to_y, subject, flags & ~PFLAG_MOVEATTEMPT)
740 && ! place_is_passable(place, to_x, from_y, subject, flags & ~PFLAG_MOVEATTEMPT)) {
741 return 0;
742 }
743
744 return 1;
745 }
746
747
place_is_occupied(struct place * place,int x,int y)748 int place_is_occupied(struct place *place, int x, int y)
749 {
750 WRAP_COORDS(place, x, y);
751 return (place_get_object(place, x, y, being_layer) != 0);
752 }
753
place_lookup_tile(struct place * place,int x,int y)754 static struct tile *place_lookup_tile(struct place *place, int x, int y)
755 {
756 struct olist *olist;
757 if (place_off_map(place, x, y))
758 return 0;
759 olist = hash_lookup(place->objects, INDEX(x, y, place_w(place)));
760 if (!olist)
761 return 0;
762 return outcast(olist, struct tile, hashlink);
763 }
764
place_create_and_add_tile(struct place * place,int x,int y)765 static struct tile *place_create_and_add_tile(struct place *place, int x,
766 int y)
767 {
768 struct tile *tile;
769 tile = tile_new(INDEX(x, y, place_w(place)));
770 if (!tile)
771 return 0;
772 hash_add(place->objects, &tile->hashlink);
773 //dbg("place_create_and_add_tile: place=%s tile=%08lx x=%d y=%d\n", place->name, tile, x, y);
774 return tile;
775 }
776
placeGetTile(struct place * place,int x,int y)777 struct tile *placeGetTile(struct place *place, int x, int y)
778 {
779 struct tile *tile = place_lookup_tile(place, x, y);
780 if (tile)
781 return tile;
782 return place_create_and_add_tile(place, x, y);
783 }
784
place_paint_objects(struct place * place,int mx,int my,int sx,int sy)785 void place_paint_objects(struct place *place, int mx, int my,
786 int sx, int sy)
787 {
788 struct tile *tile;
789
790 tile = place_lookup_tile(place, mx, my);
791 if (!tile)
792 return;
793
794 tile_paint(tile, sx, sy);
795 }
796
place_visibility(struct place * place,int x,int y)797 int place_visibility(struct place *place, int x, int y)
798 {
799 struct terrain *terrain;
800 struct tile *tile;
801
802 if (place->wraps) {
803 x = WRAP(x, place->terrain_map->w);
804 y = WRAP(y, place->terrain_map->h);
805 } else if (x < 0 || x >= place->terrain_map->w || y < 0 ||
806 y >= place->terrain_map->h)
807 return ALPHA_OPAQUE;
808
809 terrain = place->terrain_map->terrain[y * place->terrain_map->w + x];
810 if (!terrain)
811 return ALPHA_OPAQUE;
812
813 tile = place_lookup_tile(place, x, y);
814 if (tile && ! tile_is_transparent(tile))
815 return ALPHA_OPAQUE;
816
817 return terrain->alpha;
818 }
819
place_walking_distance(struct place * place,int x0,int y0,int x1,int y1)820 unsigned int place_walking_distance(struct place *place,
821 int x0, int y0, int x1, int y1)
822 {
823 int dx;
824 int dy;
825
826 if (place->wraps) {
827 dx = WRAP_DISTANCE(min(x0, x1), max(x0, x1),
828 place->terrain_map->w);
829 dy = WRAP_DISTANCE(min(y0, y1), max(y0, y1),
830 place->terrain_map->h);
831 } else {
832 dx = max(x0, x1) - min(x0, x1);
833 dy = max(y0, y1) - min(y0, y1);
834 }
835
836 return dx + dy;
837 }
838
place_flying_distance(struct place * place,int x0,int y0,int x1,int y1)839 int place_flying_distance(struct place *place,
840 int x0, int y0, int x1, int y1)
841 {
842 int dx;
843 int dy;
844
845 place_get_direction_vector(place, x0, y0, x1, y1, &dx, &dy);
846 dx = abs(dx);
847 dy = abs(dy);
848
849 // This approx comes from the angband LOS source, and overestimates
850 // about one tile per fifteen tiles of distance.
851 return ((dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1)));
852 }
853
place_get_direction_vector(struct place * place,int x0,int y0,int x1,int y1,int * dx,int * dy)854 void place_get_direction_vector(struct place *place, int x0, int y0, int x1,
855 int y1, int *dx, int *dy)
856 {
857 int east, west, north, south;
858
859 // fixme: is this code assuming that (x0,y0) and (x1,y1) are already
860 // wrapped?
861
862 if (! place->wraps) {
863 *dx = x1 - x0;
864 *dy = y1 - y0;
865 return;
866 }
867
868 // Four possibilities for dx:
869 //
870 // | x0-->x1 | (a) direct east
871 // |<--x0 x1<--| (b) west across map boundary
872 // | x1<--x0 | (c) direct west
873 // |-->x1 x0-->| (d) east across map boundary
874 //
875 // Note that since west is always to the left, it is a negative vector.
876 // We compute it as a positive value to make it easy to compare against
877 // east, but convert it to negative before returning it as dx.
878
879 if (x1 > x0) {
880 east = x1 - x0;
881 west = x0 + place->terrain_map->w - x1;
882 } else {
883 west = x0 - x1;
884 east = x1 + place->terrain_map->w - x0;
885 }
886
887 if (west < east)
888 *dx = -west;
889 else
890 *dx = east;
891
892 // Four possibilities for dy:
893 //
894 // ---------------
895 // ^ |
896 // y0 | y1 v
897 // | y0 ^ y1
898 // v |
899 // y1 y0
900 // y1 y0
901 // ^ |
902 // | V
903 // ---------------
904 // (a) (b) (c) (d)
905 //
906 // (a) direct south
907 // (b) north across map boundary
908 // (c) direct north
909 // (d) south across map boundary
910 //
911 // Note that north is always a negative vector.
912
913 if (y1 > y0) {
914 south = y1 - y0;
915 north = y0 + place->terrain_map->h - y1;
916 } else {
917 north = y0 - y1;
918 south = y1 + place->terrain_map->h - y0;
919 }
920
921 if (north < south)
922 *dy = -north;
923 else
924 *dy = south;
925 }
926
place_move_object(struct place * place,Object * object,int newx,int newy)927 void place_move_object(struct place *place, Object * object,
928 int newx, int newy)
929 {
930 struct tile *old_tile;
931 struct tile *new_tile;
932
933 if (newx == object->getX() &&
934 newy == object->getY())
935 return;
936
937 old_tile = place_lookup_tile(place, object->getX(), object->getY());
938 assert(old_tile);
939
940 new_tile = place_lookup_tile(place, newx, newy);
941
942 if (!new_tile) {
943 new_tile = place_create_and_add_tile(place, newx, newy);
944 assert(new_tile);
945 }
946
947 obj_inc_ref(object);
948 tile_remove_object(old_tile, object);
949 tile_add_object(new_tile, object);
950 obj_dec_ref(object);
951 }
952
place_add_object(struct place * place,Object * object)953 int place_add_object(struct place *place, Object * object)
954 {
955 struct tile *tile = place_lookup_tile(place, object->getX(),
956 object->getY());
957
958 if (!tile) {
959 tile = place_create_and_add_tile(place, object->getX(),
960 object->getY());
961 if (!tile)
962 return -1;
963 }
964
965 tile_add_object(tile, object);
966 place_add_to_turn_list(place, object);
967 mapSetDirty();
968 return 0;
969 }
970
place_remove_object(struct place * place,Object * object)971 void place_remove_object(struct place *place, Object * object)
972 {
973 struct tile *tile = place_lookup_tile(place, object->getX(),
974 object->getY());
975 assert(tile);
976
977 obj_inc_ref(object);
978 tile_remove_object(tile, object);
979 place_remove_from_turn_list(place, object);
980 obj_dec_ref(object);
981
982 }
983
place_get_object(struct place * place,int x,int y,enum layer layer)984 Object *place_get_object(struct place *place, int x, int y, enum layer layer)
985 {
986 struct tile *tile;
987
988 WRAP_COORDS(place, x, y);
989
990 tile = place_lookup_tile(place, x, y);
991 if (!tile)
992 return 0;
993
994 return tile_get_top_object_at_layer(tile, layer);
995 }
996
place_get_filtered_object(struct place * place,int x,int y,int (* filter)(Object *))997 Object *place_get_filtered_object(struct place *place, int x, int y, int (*filter)(Object*))
998 {
999 struct tile *tile;
1000
1001 WRAP_COORDS(place, x, y);
1002
1003 tile = place_lookup_tile(place, x, y);
1004 if (!tile)
1005 return 0;
1006
1007 return tile_get_filtered_object(tile, filter);
1008 }
1009
place_get_Party(struct place * place,int x,int y)1010 class Party *place_get_Party(struct place * place, int x, int y)
1011 {
1012 Object *object;
1013
1014 WRAP_COORDS(place, x, y);
1015
1016 // fixme: this will get Character's, too... and be careful because the
1017 // pathfinding code appears to rely on this fact.
1018 object = place_get_object(place, x, y, being_layer);
1019 if (!object)
1020 return 0;
1021
1022 if (!object->isType(PARTY_ID))
1023 return 0;
1024
1025 return (class Party *) object;
1026 }
1027
place_get_vehicle(struct place * place,int x,int y)1028 class Vehicle *place_get_vehicle(struct place * place, int x, int y)
1029 {
1030 struct tile *tile;
1031
1032 WRAP_COORDS(place, x, y);
1033 tile = place_lookup_tile(place, x, y);
1034 if (!tile)
1035 return 0;
1036 return tile->vehicle;
1037 }
1038
place_get_combat_terrain_map(struct place * place,int x,int y)1039 struct terrain_map *place_get_combat_terrain_map(struct place *place,
1040 int x, int y)
1041 {
1042 struct terrain *terrain;
1043
1044 WRAP_COORDS(place, x, y);
1045 terrain = place->terrain_map->terrain[y * place->terrain_map->w + x];
1046 return terrain_combat_map(terrain);
1047 }
1048
1049 /* Pathfinding ***************************************************************/
1050
place_pathfind_is_valid_location(struct place_pathfind_context * context,int from_x,int from_y,int x,int y)1051 static int place_pathfind_is_valid_location(
1052 struct place_pathfind_context *context, int from_x, int from_y,
1053 int x, int y)
1054 {
1055 class Object *portal;
1056
1057 //dbg("[%d %d]...", x, y);
1058
1059 /* I used to check this after passability, but it really belongs first.
1060 * In several cases the target location may not be passable but if the
1061 * seeker can get adjacent to it that will be good enough. If this is
1062 * not what the caller wants, they need to se the PFLAG_ADJACENTNOTOK
1063 * flag. */
1064 if ((!(context->pflags & PFLAG_ADJACENTNOTOK))
1065 && x == context->target_x
1066 && y == context->target_y) {
1067 //dbg("ok\n");
1068 return 1;
1069 }
1070
1071 if (!place_move_is_passable(context->place, from_x, from_y, x, y,
1072 context->requestor, context->pflags)) {
1073 //dbg("impassable\n");
1074 return 0;
1075 }
1076
1077 // --------------------------------------------------------------------
1078 // Check if the caller is blocked by an occupant on this tile.
1079 // --------------------------------------------------------------------
1080
1081 if (0 == (context->pflags & PFLAG_IGNOREBEINGS)) {
1082 class Object *occupant;
1083 occupant = place_get_object(context->place, x, y, being_layer);
1084 if (occupant != NULL) {
1085 if (0 == (context->pflags & PFLAG_IGNORECOMPANIONS) ||
1086 ! context->requestor->isCompanionOf(occupant)) {
1087 //dbg("occupied!\n");
1088 return 0;
1089 }
1090 }
1091 }
1092
1093 // --------------------------------------------------------------------
1094 // I used to penalize portals in the heuristic routine, but that was
1095 // back in the day when I would pathfind for the player on a
1096 // right-click. Any more pathfinding is used exclusively for NPCs (or
1097 // PC's in follow mode) and I NEVER want them to enter a portal unless
1098 // they explicitly want to (and currently they never do). Likewise for
1099 // open moongates.
1100 //
1101 // Addendum: portals are now mechs with "step" signal handlers. The
1102 // code below avoids any mechanism which responds to the "step" signal,
1103 // including non-portals. That's fine, because anything which responds
1104 // to "step" is probably something I want to avoid.
1105 //
1106 // Addendum 2: fix for SF Bug #[ 1523230 ] "pathfinding across
1107 // mechs". Added the new IGNORESTEPTRIG flag and wrapped the following
1108 // with a check. Currently the only time this flag should be set is in
1109 // pathfinding done by player party member's in Follow Mode.
1110 // --------------------------------------------------------------------
1111
1112 if (0 == (context->pflags & PFLAG_IGNORESTEPTRIG)) {
1113 if ((portal =
1114 place_get_object(context->place, x, y, mech_layer))
1115 && portal->canStep()) {
1116 //dbg("portal!\n");
1117 return 0;
1118 }
1119 }
1120
1121 //dbg("ok\n");
1122 return 1;
1123 }
1124
place_pathfind_heuristic(struct astar_search_info * info,int * goodness,int * cost,int from_x,int from_y)1125 static void place_pathfind_heuristic(struct astar_search_info *info,
1126 int *goodness, int *cost,
1127 int from_x, int from_y)
1128 {
1129 struct terrain *terrain;
1130 struct place_pathfind_context *context;
1131
1132 context = (struct place_pathfind_context *) info->context;
1133
1134 /* The basic goodness is walking distance. Duplicate that algorithm
1135 * except pay attention to the info->flags. */
1136
1137 if ((info->flags & ASTAR_HORZ) == 0) {
1138 // Yes, we are interested in the x coordinate of the
1139 // destination.
1140 if (context->place->wraps) {
1141 *goodness -= WRAP_DISTANCE(min(info->x0, info->x1),
1142 max(info->x0, info->x1),
1143 context->place->terrain_map->w);
1144 } else {
1145 *goodness -= max(info->x0, info->x1) -
1146 min(info->x0, info->x1);
1147 }
1148 }
1149
1150 if ((info->flags & ASTAR_VERT) == 0) {
1151 // Yes, we are interested in the y coordinate of the
1152 // destination.
1153 if (context->place->wraps) {
1154 *goodness -= WRAP_DISTANCE(min(info->y0, info->y1),
1155 max(info->y0, info->y1),
1156 context->place->terrain_map->h);
1157 } else {
1158 *goodness -= max(info->y0, info->y1) -
1159 min(info->y0, info->y1);
1160 }
1161 }
1162
1163 /* Add the terrain cost. */
1164 *cost += place_get_diagonal_movement_cost(context->place, from_x,
1165 from_y, info->x0, info->y0,
1166 context->requestor, PFLAG_IGNOREMECHS);
1167
1168 /* And penalize tiles with hazards on them. I really should assign
1169 * different penalties to different hazerds. */
1170 terrain = place_get_terrain(context->place, info->x0, info->y0);
1171 if (terrain->effect)
1172 *cost += kern_intvar_get("AP_TOTAL:normal_human") * 9;
1173
1174 if (place_get_object(context->place, info->x0, info->y0,
1175 field_layer) != NULL)
1176 *cost += kern_intvar_get("AP_TOTAL:normal_human") * 9;
1177 }
1178
1179 /* If pathfinding over a nontrivial distance, make a quick check to see if a
1180 * path is impossible due to impassable terrain surrounding the target. This is
1181 * an optimization hack to avoid the worst-case search time where a path cannot
1182 * be found.
1183 */
place_find_path_impossible(struct place_pathfind_context * context)1184 static int place_find_path_impossible(struct place_pathfind_context *context)
1185 {
1186 /* Check final destination */
1187 if ((context->pflags & PFLAG_ADJACENTNOTOK)
1188 && ! place_is_passable(context->place, context->target_x,
1189 context->target_y, context->requestor,
1190 context->pflags)) {
1191 return 0;
1192 }
1193
1194 /* check four neighbors */
1195 if (place_pathfind_is_valid_location(context,
1196 context->target_x,
1197 context->target_y,
1198 context->target_x-1,
1199 context->target_y)
1200 || place_pathfind_is_valid_location(context,
1201 context->target_x,
1202 context->target_y,
1203 context->target_x+1,
1204 context->target_y)
1205 || place_pathfind_is_valid_location(context,
1206 context->target_x,
1207 context->target_y,
1208 context->target_x,
1209 context->target_y-1)
1210 || place_pathfind_is_valid_location(context,
1211 context->target_x,
1212 context->target_y,
1213 context->target_x,
1214 context->target_y+1))
1215 return 0;
1216
1217 /* all 4 neighbors impassable so forget it */
1218 return 1;
1219
1220 }
1221
place_find_path(struct place * place,struct astar_search_info * info,class Object * requestor)1222 struct astar_node *place_find_path(struct place *place,
1223 struct astar_search_info *info,
1224 class Object *requestor)
1225 {
1226 struct astar_node *path;
1227 struct place_pathfind_context context;
1228 int t1;
1229
1230 /* Store the target location as the context */
1231 context.place = place;
1232 context.target_x = info->x1;
1233 context.target_y = info->y1;
1234 context.pflags = info->flags;
1235 context.requestor = requestor;
1236
1237 if (place_find_path_impossible(&context))
1238 return NULL;
1239
1240 /* Fill out the search information */
1241 info->is_valid_location =
1242 (int (*)(void *, int, int, int, int))
1243 place_pathfind_is_valid_location;
1244 info->heuristic = place_pathfind_heuristic;
1245 info->width = place_w(place);
1246 info->height = place_h(place);
1247 info->wraps = place->wraps;
1248 info->context = &context;
1249
1250 /* Run the pathfinding alg */
1251 t1 = SDL_GetTicks();
1252 path = astar_search(info);
1253 ////dbg("place_find_path: %d msecs\n", SDL_GetTicks() - t1);
1254
1255 return path;
1256
1257 }
1258
1259 /**
1260 * Calls place_find_path with the search info setup to seek a map edge.x
1261 */
place_find_path_to_edge(struct place * place,int x0,int y0,int edgedir,int pflags,class Object * requestor)1262 struct astar_node *place_find_path_to_edge(struct place *place, int x0, int y0,
1263 int edgedir, int pflags,
1264 class Object *requestor)
1265 {
1266 struct astar_search_info info;
1267
1268 info.x0 = x0;
1269 info.y0 = y0;
1270 info.flags = pflags;
1271
1272 /* setup search info to seek the map edge */
1273 switch (edgedir) {
1274 case NORTH:
1275 info.x1 = 0;
1276 info.y1 = 0;
1277 info.flags |= ASTAR_HORZ;
1278 break;
1279 case SOUTH:
1280 info.x1 = 0;
1281 info.y1 = place_h(place)-1;
1282 info.flags |= ASTAR_HORZ;
1283 break;
1284 case EAST:
1285 info.x1 = place_w(place)-1;
1286 info.y1 = 0;
1287 info.flags |= ASTAR_VERT;
1288 break;
1289 case WEST:
1290 info.x1 = 0;
1291 info.y1 = place_h(place)-1;
1292 info.flags |= ASTAR_VERT;
1293 break;
1294 default:
1295 return 0;
1296 }
1297
1298 return place_find_path(place, &info, requestor);
1299
1300 }
1301
place_get_light(struct place * place,int x,int y)1302 int place_get_light(struct place *place, int x, int y)
1303 {
1304 int light;
1305 struct tile *tile;
1306
1307 if (place->wraps) {
1308 x = place_wrap_x(place, x);
1309 y = place_wrap_y(place, y);
1310 }
1311
1312 /* Check if the coordinates are off-map */
1313 else if (place_off_map(place, x, y))
1314 return 0;
1315
1316 /* Assign lighting from terrain */
1317 light =
1318 place->terrain_map->terrain[y * place->terrain_map->w + x]->light;
1319
1320 /* Assign lighting from tile objects */
1321 tile = place_lookup_tile(place, x, y);
1322 if (!tile)
1323 return light;
1324 light += tile_get_light(tile);
1325
1326 return light;
1327 }
1328
place_synchronize(struct place * place)1329 void place_synchronize(struct place *place)
1330 {
1331 session_synch_sched_chars(Session);
1332 }
1333
place_run_on_entry_hook(struct place * place)1334 static void place_run_on_entry_hook(struct place *place)
1335 {
1336 struct list *elem;
1337 list_for_each(&place->on_entry_hook, elem) {
1338 closure_list_t *node = (closure_list_t*)elem;
1339 closure_exec(node->closure, "pp", place, player_party);
1340 }
1341 }
1342
place_enter(struct place * place)1343 void place_enter(struct place *place)
1344 {
1345 place_run_on_entry_hook(place);
1346 place_synchronize(place);
1347 }
1348
place_get_movement_cost(struct place * place,int x,int y,class Object * obj,int flags)1349 int place_get_movement_cost(struct place *place, int x, int y,
1350 class Object *obj, int flags)
1351 {
1352 int cost;
1353 struct terrain *t;
1354 class Object *tfeat = NULL;
1355 class Object *mech = NULL;
1356
1357 WRAP_COORDS(place, x, y);
1358
1359 /* Terrain features override terrain */
1360 tfeat = place_get_object(place, x, y, tfeat_layer);
1361 if (tfeat) {
1362 return obj->getMovementCost(tfeat->getPclass());
1363 }
1364
1365 t = TERRAIN(place, x, y);
1366 cost = obj->getMovementCost(terrain_pclass(t));
1367
1368 /* Impassable terrain must have a vehicle that makes it passable; use
1369 * the cost of vehicle movement */
1370 if (PTABLE_IMPASSABLE == cost) {
1371 class Vehicle *vehicle;
1372 vehicle = place_get_vehicle(place, x, y);
1373 if (vehicle)
1374 cost = vehicle->getMovementCost(terrain_pclass(t));
1375 }
1376
1377 // Is the mech passable?
1378 //(place_obj_is_passable(mech, subject, flags) ==
1379 // BLOCKS_PASSABILITY))
1380 // return 0;
1381
1382 if (0 == (flags & PFLAG_IGNOREMECHS))
1383 {
1384 mech = place_get_object(place, x, y, mech_layer);
1385 if (mech)
1386 {
1387 int mechcost = obj->getMovementCost(mech->getPclass());
1388 if (mechcost > cost)
1389 {
1390 cost = mechcost;
1391 }
1392 }
1393 }
1394
1395 return cost;
1396 }
1397
place_get_diagonal_movement_cost(struct place * place,int from_x,int from_y,int to_x,int to_y,class Object * obj,int flags)1398 int place_get_diagonal_movement_cost(struct place *place,
1399 int from_x, int from_y,
1400 int to_x, int to_y, class Object *obj,int flags)
1401 {
1402 int cost = place_get_movement_cost(place, to_x, to_y, obj, flags);
1403
1404 /* Multiply cost of diagonals by 1.4 (pythagorean theorem) */
1405 if (place_is_diagonal(place, from_x, from_y, to_x, to_y)) {
1406 cost *= 14;
1407 cost /= 10;
1408 }
1409
1410 return cost;
1411 }
1412
place_is_hazardous(struct place * place,int x,int y)1413 int place_is_hazardous(struct place *place, int x, int y)
1414 {
1415 WRAP_COORDS(place, x, y);
1416 struct terrain *t = TERRAIN(place, x, y);
1417 if (t->effect)
1418 return 1;
1419 if (place_get_object(place, x, y, field_layer) != NULL)
1420 return 1;
1421 return 0;
1422 }
1423
place_set_terrain(struct place * place,int x,int y,struct terrain * terrain)1424 void place_set_terrain(struct place *place, int x, int y,
1425 struct terrain *terrain)
1426 {
1427 WRAP_COORDS(place, x, y);
1428 TERRAIN(place, x, y) = terrain;
1429 }
1430
place_get_terrain(struct place * place,int x,int y)1431 struct terrain *place_get_terrain(struct place *place, int x, int y)
1432 {
1433 WRAP_COORDS(place, x, y);
1434 if (place_off_map(place, x, y))
1435 return NULL;
1436 x = place_wrap_x(place, x);
1437 y = place_wrap_y(place, y);
1438 return TERRAIN(place, x, y);
1439 }
1440
place_describe_terrain(struct place * place,int x,int y)1441 static void place_describe_terrain(struct place *place, int x, int y)
1442 {
1443 struct terrain *t = place_get_terrain(place, x, y);
1444 log_continue("%s", t->name);
1445 }
1446
place_describe_objects(struct place * place,int x,int y,int first_thing_listed)1447 static int place_describe_objects(struct place *place, int x, int y,
1448 int first_thing_listed)
1449 {
1450
1451 struct node *l;
1452 struct tile *tile;
1453 Object *obj = NULL, *prev_obj = NULL;
1454 class ObjectType *type = NULL;
1455 int n_types;
1456 int n_described = 0;
1457
1458 tile = place_lookup_tile(place, x, y);
1459 if (!tile)
1460 return n_described;
1461
1462 // Let's make things simple. Inefficient, but simple. Efficiency is not
1463 // so critical here. We'll do this in two passes. Pass one will count
1464 // the number of things we need to list. Pass two will print the things
1465 // with the proper punctuation.
1466
1467 // Step 1: count the number of different types of things we need to
1468 // list (multiple counts of one type of thing count as 1)
1469
1470 type = NULL;
1471 n_types = 0;
1472
1473 if (tile->subplace) {
1474
1475 // ------------------------------------------------------------
1476 // FIXME: This has not been fully debugged. It works ok when
1477 // there are no objects on the tile, but I don't think the
1478 // 'ands' and commas are correct if there are.
1479 // ------------------------------------------------------------
1480 log_continue(" and the entrance to %s", tile->subplace->name);
1481 }
1482
1483 node_for_each(&tile->objstack, l) {
1484
1485 obj = (Object *)l->ptr;
1486
1487 if (obj->getLayer() == cursor_layer)
1488 // Special case: don't describe the cursor
1489 continue;
1490
1491 if (! obj->isVisible() && ! Reveal && ! obj->isShaded())
1492 continue;
1493
1494 // hack: objects without names are assumed to be invisible
1495 if (! obj->getName())
1496 continue;
1497
1498 if(obj->getObjectType() != type
1499 || ! obj->getObjectType()) {
1500
1501 // We just found a new type of thing (we know because
1502 // it's different from the last type of thing).
1503 type = obj->getObjectType();
1504 if (obj->isVisible() || Reveal || obj->isShaded())
1505 n_types++;
1506
1507 }
1508 }
1509
1510 if (tile->vehicle && (tile->vehicle->isVisible() || Reveal ||
1511 obj->isShaded()))
1512 n_types++;
1513
1514 if (n_types == 0)
1515 // Nothing to list so we're done.
1516 return n_described;
1517
1518
1519 // Step 2: now we actually list the things, using the count to help us
1520 // decide how to punctuate.
1521
1522 type = NULL;
1523 prev_obj = NULL;
1524
1525 node_for_each(&tile->objstack, l) {
1526
1527 obj = (Object *)l->ptr;
1528
1529 if (obj->getLayer() == cursor_layer)
1530 // Special case: don't describe the cursor
1531 continue;
1532
1533 if (! obj->isVisible() && ! Reveal && ! obj->isShaded())
1534 continue;
1535
1536 // hack: objects without names are assumed to be invisible
1537 if (! obj->getName())
1538 continue;
1539
1540 if (prev_obj == NULL) {
1541
1542 // This is the first type of thing we need to
1543 // list. Don't print it until we find out how many
1544 // there are.
1545 type = obj->getObjectType();
1546
1547 } else if (obj->getObjectType() != type
1548 || !obj->getObjectType()) {
1549
1550 // We just found a new type of thing (we know because
1551 // it's different from the last type of thing). Now we
1552 // can print the last type of thing since we know how
1553 // many there are of it.
1554
1555 if (prev_obj->isVisible() || Reveal ||
1556 prev_obj->isShaded()) {
1557 if (first_thing_listed) {
1558 first_thing_listed = 0;
1559 } else {
1560 if (n_types == 1)
1561 log_continue(" and ");
1562 else
1563 log_continue(", ");
1564 }
1565
1566 prev_obj->describe();
1567 n_described++;
1568 n_types--;
1569 }
1570
1571 type = obj->getObjectType();
1572
1573 }
1574
1575 prev_obj = obj;
1576 }
1577
1578 // Now we have to print the last object in the stack.
1579 if (prev_obj && (prev_obj->isVisible() || Reveal ||
1580 prev_obj->isShaded())) {
1581 if (!first_thing_listed) {
1582 if (n_types == 1)
1583 log_continue(" and ");
1584 else
1585 log_continue(", ");
1586 }
1587
1588 prev_obj->describe();
1589 n_described++;
1590 n_types--;
1591 }
1592
1593 if (tile->vehicle && (tile->vehicle->isVisible() || Reveal ||
1594 obj->isShaded())) {
1595 if (n_types == 1)
1596 log_continue(" and ");
1597 else
1598 log_continue(", ");
1599 tile->vehicle->describe();
1600 n_described++;
1601 n_types--;
1602 }
1603
1604 return n_described;
1605
1606 } // myPlaceDescribeObjects()
1607
place_examine_objects(struct place * place,int x,int y)1608 static void place_examine_objects(struct place *place, int x, int y)
1609 {
1610 //can simplify from place_describe, since entries will have their own
1611 //lines.
1612 struct node *l;
1613 struct tile *tile;
1614 Object *obj = NULL;
1615
1616 tile = place_lookup_tile(place, x, y);
1617 if (!tile)
1618 return;
1619
1620 if (tile->subplace) {
1621 log_continue("\nthe entrance to %s", tile->subplace->name);
1622 }
1623
1624 node_for_each(&tile->objstack, l) {
1625 obj = (Object *)l->ptr;
1626
1627 if (obj->getLayer() == cursor_layer)
1628 // Special case: don't describe the cursor
1629 continue;
1630
1631 if (! obj->isVisible() && ! Reveal && ! obj->isShaded())
1632 continue;
1633
1634 // hack: objects without names are assumed to be invisible
1635 if (! obj->getName())
1636 continue;
1637
1638 log_end("");
1639 log_begin("");
1640 obj->examine();
1641 }
1642
1643 if (tile->vehicle && (tile->vehicle->isVisible() || Reveal ||
1644 obj->isShaded())) {
1645 log_end("");
1646 log_begin("");
1647 tile->vehicle->examine();
1648 }
1649 } // place_examine_objects()
1650
place_describe(struct place * place,int x,int y,int flags)1651 void place_describe(struct place *place, int x, int y, int flags)
1652 {
1653 int count = 0;
1654
1655 WRAP_COORDS(place, x, y);
1656
1657 if (place_off_map(place, x, y)) {
1658 log_continue("nothing!");
1659 return;
1660 }
1661 if (flags & PLACE_DESCRIBE_TERRAIN) {
1662 place_describe_terrain(place, x, y);
1663 count = 1;
1664 }
1665 if (flags & PLACE_DESCRIBE_OBJECTS)
1666 count += place_describe_objects(place, x, y,
1667 (flags & PLACE_DESCRIBE_TERRAIN) == 0);
1668 if (!count)
1669 log_continue("nothing!");
1670 }
1671
place_examine(struct place * place,int x,int y)1672 void place_examine(struct place *place, int x, int y)
1673 {
1674 WRAP_COORDS(place, x, y);
1675
1676 if (place_off_map(place, x, y)) {
1677 log_continue("nothing!");
1678 return;
1679 }
1680
1681 place_describe_terrain(place, x, y);
1682 place_examine_objects(place, x, y);
1683 }
1684
place_for_each_tile(struct place * place,void (* fx)(struct tile * tile,void * data),void * data)1685 void place_for_each_tile(struct place *place,
1686 void (*fx)(struct tile *tile, void *data), void *data)
1687 {
1688 int i;
1689 struct olist *tileList;
1690 struct list *tileElem, *tileTmp;
1691 struct tile *tile;
1692 int count = 0;
1693
1694 /* Note: took the !Quit check out of these loops because this is called
1695 * as part of place_del(), so that check means we never delete any
1696 * objects! */
1697
1698 /* for each bucket */
1699 for (i = 0; i < place->objects->n /*&& !Quit*/; i++) {
1700
1701 tileList = &place->objects->buckets[i];
1702 tileElem = tileList->list.next;
1703 assert(tileElem->prev == &tileList->list);
1704
1705 /* for each tile */
1706 while (tileElem != &tileList->list /*&& !Quit*/) {
1707
1708 tileTmp = tileElem->next;
1709 tile = outcast(tileElem, struct tile, hashlink.list);
1710
1711 /* invoke the function on the tile */
1712 tile->lock++;
1713 fx(tile, data);
1714 tile->lock--;
1715 if (!tile->objects)
1716 tile_del(tile);
1717
1718 tileElem = tileTmp;
1719 count++;
1720 }
1721 }
1722 }
1723
1724 struct forobj_tile_visitor_info {
1725 void (*fx) (class Object *, void *data);
1726 void *data;
1727 };
1728
place_forobj_tile_visitor(struct tile * tile,void * data)1729 void place_forobj_tile_visitor(struct tile *tile, void *data)
1730 {
1731 struct forobj_tile_visitor_info *info;
1732 info = (struct forobj_tile_visitor_info*)data;
1733 tile_for_each_object(tile, info->fx, info->data);
1734 }
1735
place_for_each_object(struct place * place,void (* fx)(class Object *,void * data),void * data)1736 void place_for_each_object(struct place *place,
1737 void (*fx) (class Object *, void *data),
1738 void *data)
1739 {
1740 struct forobj_tile_visitor_info info;
1741 info.fx = fx;
1742 info.data = data;
1743 place_for_each_tile(place, place_forobj_tile_visitor, &info);
1744 }
1745
place_remove_and_destroy_object(class Object * obj,void * unused)1746 static void place_remove_and_destroy_object(class Object *obj, void *unused)
1747 {
1748 obj_inc_ref(obj);
1749 obj->remove();
1750 obj_dec_ref(obj);
1751 }
1752
place_remove_and_destroy_all_objects(struct place * place)1753 void place_remove_and_destroy_all_objects(struct place *place)
1754 {
1755 place_for_each_object(place, place_remove_and_destroy_object, NULL);
1756 }
1757
place_apply_tile_effects(struct place * place,class Object * obj)1758 void place_apply_tile_effects(struct place *place, class Object *obj)
1759 {
1760 class Object *tfeat;
1761 class Field *field;
1762
1763 assert(! obj->isDestroyed());
1764
1765 // --------------------------------------------------------------------
1766 // First check for a terrain feature, which will override any terrain
1767 // effects.
1768 // --------------------------------------------------------------------
1769 tfeat = place_get_object(place, obj->getX(), obj->getY(), tfeat_layer);
1770 if (tfeat) {
1771 if (tfeat->canStep())
1772 tfeat->step(obj);
1773 } else {
1774 struct terrain *terrain;
1775 terrain = place_get_terrain(place, obj->getX(), obj->getY());
1776 if (terrain->effect) {
1777 obj->applyEffect(terrain->effect);
1778 }
1779 }
1780
1781 // --------------------------------------------------------------------
1782 // Check if the terrain or feature effect destroyed the object.
1783 // --------------------------------------------------------------------
1784 if (obj->isDestroyed())
1785 return;
1786
1787 // --------------------------------------------------------------------
1788 // Now apply effects from any fields on that tile.
1789 // --------------------------------------------------------------------
1790 field = (class Field *)place_get_object(place,
1791 obj->getX(),
1792 obj->getY(),
1793 field_layer);
1794 if (field &&
1795 field != obj &&
1796 field->getObjectType()->effect) {
1797 obj->applyEffect(field->getObjectType()->effect);
1798 }
1799
1800 }
1801
place_timed_obj_exec(Object * obj)1802 static int place_timed_obj_exec(Object *obj)
1803 {
1804 int time = SDL_GetTicks();
1805 obj->exec();
1806 /* if (obj->isPlayerControlled()) */
1807 /* return 0; */
1808 return SDL_GetTicks() - time;
1809
1810 }
1811
1812 /*****************************************************************************
1813 * place_exec - run all the objects in a place through one turn
1814 *****************************************************************************/
place_exec(struct place * place)1815 void place_exec(struct place *place)
1816 {
1817 class Object *obj = NULL;
1818 int times = 0;
1819
1820 /* FIXME: not sure if we still need this assert */
1821 assert(Place == place);
1822
1823 /* Prevent destruction of the place. */
1824 place_lock(place);
1825
1826 /* Flush ambient noises one cycle */
1827 sound_flush_ambient();
1828
1829 /* Start with the first node */
1830 place->turn_elem = node_next(&place->turn_list);
1831
1832 /* Loop over all nodes or until the player quits or dies. */
1833 while (place->turn_elem != &place->turn_list
1834 && ! Quit
1835 && ! player_party->allDead()
1836 && ! Reload
1837 && place == Place /* otherwise projectiles and damage flashes
1838 * from the old place are shown in the new
1839 * place until the turn is over when the
1840 * player does a switch */
1841 ) {
1842
1843 /* Keep a pointer to the object in the node */
1844 obj = (class Object *)place->turn_elem->ptr;
1845 obj_inc_ref(obj);
1846
1847 /* Advance the node pointer now in case we need to remove the
1848 * object while running it. */
1849 place->turn_elem = node_next(place->turn_elem);
1850
1851 /* Check the global time stop thread. Only the player gets to
1852 * move during time stop and no tile effects are applied to
1853 * anything. */
1854 if (TimeStop) {
1855 if (obj->isPlayerControlled()
1856 || obj->ignoresTimeStop()) {
1857 obj->exec();
1858 }
1859 } else {
1860 /* 'run' the object */
1861 int t = place_timed_obj_exec(obj);
1862 if (PLACE_PROFILE) {
1863 printf("%32s: %d ticks\n", obj->getName(), t);
1864 times += t;
1865 }
1866
1867 /* Apply terrain, field and any other environmental
1868 * effects. */
1869 if (obj->isOnMap()
1870 && null_layer != obj->getLayer()
1871 ) {
1872 /* Bugfix: as a result of executing its turn,
1873 * the object may now be in a different
1874 * place! */
1875 t = SDL_GetTicks();
1876 place_apply_tile_effects(obj->getPlace(), obj);
1877 times += (SDL_GetTicks() - t);
1878 }
1879 }
1880
1881 /* Check if the object was destroyed. */
1882 if (obj->isDestroyed()) {
1883
1884 /* Don't delete the player party here, that will be
1885 * handled by the caller. */
1886 if (obj == player_party) {
1887 place_unlock(place);
1888 obj_dec_ref(obj);
1889 return;
1890 }
1891
1892 /* Make sure the object was already removed. */
1893 assert(! obj->isOnMap());
1894
1895 /* Used to destroy the object explicitly here with
1896 * delete. Now rely on the unref at the bottom of the
1897 * loop to destory it. */
1898 }
1899
1900 /* check for end of combat */
1901 //if (combat_get_state() != COMBAT_STATE_DONE) {
1902 combat_analyze_results_of_last_turn();
1903 //}
1904
1905 obj_dec_ref(obj);
1906
1907 /* Reduce the latency of keystroke handling while running the
1908 * demo in parallel with the menus. */
1909 if (Session->is_demo) {
1910 eventHandlePending();
1911 }
1912 }
1913
1914 if (PLACE_PROFILE) {
1915 printf("*** place_exec: total=%d\n", times);
1916 }
1917
1918 /* Allow the place to be destroyed. */
1919 place_unlock(place);
1920 }
1921
place_clip_to_map(struct place * place,int * x,int * y)1922 void place_clip_to_map(struct place *place, int *x, int *y)
1923 {
1924 if (place->wraps)
1925 return;
1926
1927 *x = max(*x, 0);
1928 *x = min(*x, place_w(place) - 1);
1929 *y = max(*y, 0);
1930 *y = min(*y, place_h(place) - 1);
1931 }
1932
1933 // fixme -- combine with mapAnimateProjectile(), use callback for animation
place_los_blocked(struct place * place,int Ax,int Ay,int Bx,int By)1934 int place_los_blocked(struct place *place, int Ax, int Ay, int Bx, int By)
1935 {
1936 // Should be called from source to target. Does not test for los on
1937 // target tile itself, but stops one short. So opaque tiles are visible
1938 // if they are the destination.
1939
1940 // Apply the bresenhaum algorithm to walk the line from (x0, y0) to
1941 // (x1, y1) and check for visibility at each step. Note that the real
1942 // intention here is to see if I can fire an arrow from one point to
1943 // another. The missile flight code in Missile:animate() uses a test
1944 // for visibility on each tile to determine if a missile is blocked in
1945 // its flight path.
1946
1947 int steps = 0;
1948
1949 //Ax = place_wrap_x(place, Ax);
1950 //Ay = place_wrap_y(place, Ay);
1951 //Bx = place_wrap_x(place, Bx);
1952 //By = place_wrap_y(place, By);
1953
1954 int Px = Ax;
1955 int Py = Ay;
1956
1957 // Get the distance components
1958 int dX = Bx - Ax;
1959 int dY = By - Ay;
1960 int AdX = abs(dX);
1961 int AdY = abs(dY);
1962
1963 // Moving left?
1964 int Xincr = (Ax > Bx) ? -1 : 1;
1965
1966 // Moving down?
1967 int Yincr = (Ay > By) ? -1 : 1;
1968
1969 // Walk the x-axis
1970 if (AdX >= AdY) {
1971
1972 int dPr = AdY << 1;
1973 int dPru = dPr - (AdX << 1);
1974 int P = dPr - AdX;
1975
1976 // For each x
1977 for (int i = AdX; i >= 0; i--) {
1978
1979 if (steps > 1 && i > 0) {
1980 if (!place_visibility(place, Px, Py))
1981 return 1;
1982 }
1983
1984 steps++;
1985
1986 if (P > 0) {
1987 Px += Xincr;
1988 Py += Yincr;
1989 P += dPru;
1990 }
1991 else {
1992 Px += Xincr;
1993 P += dPr;
1994 }
1995 }
1996 }
1997 // Walk the y-axis
1998 else {
1999 int dPr = AdX << 1;
2000 int dPru = dPr - (AdY << 1);
2001 int P = dPr - AdY;
2002
2003 // For each y
2004 for (int i = AdY; i >= 0; i--) {
2005
2006 if (steps > 1 && i > 0) {
2007 if (!place_visibility(place, Px, Py))
2008 return 1;
2009 }
2010
2011 steps++;
2012
2013 if (P > 0) {
2014 Px += Xincr;
2015 Py += Yincr;
2016 P += dPru;
2017 }
2018 else {
2019 Py += Yincr;
2020 P += dPr;
2021 }
2022 }
2023 }
2024
2025 return 0;
2026 }
2027
2028 /*****************************************************************************
2029 * place_contains_hostile -- helper utility to scan for hostiles
2030 *****************************************************************************/
place_contains_hostiles(struct place * place,Being * subject)2031 int place_contains_hostiles(struct place *place, Being *subject)
2032 {
2033 struct node *node;
2034 class Object *obj;
2035
2036 node_for_each(&place->turn_list, node) {
2037 obj = (class Object *)node->ptr;
2038 if (! obj_is_being(obj))
2039 continue;
2040 if (are_hostile((Being*)obj, subject))
2041 return 1;
2042 }
2043
2044 return 0;
2045 }
2046
place_save_object(class Object * object,void * data)2047 static void place_save_object(class Object *object, void *data)
2048 {
2049 struct save *save;
2050 save = (struct save*)data;
2051 save->enter(save, "(list\n");
2052 object->save(save);
2053 save->exit(save, "%d %d)\n", object->getX(), object->getY());
2054 }
2055
place_save_objects(struct place * place,struct save * save)2056 static void place_save_objects(struct place *place, struct save *save)
2057 {
2058 struct node *pnode;
2059
2060 /* Two subtle points about this process:
2061 1. to preserve order, save backwards, because the list will be
2062 reloaded backwards
2063 2. the object which is currently being exec'd gets saved at the head
2064 of the list; this ensure that when we restart, the order of exec
2065 will continue right where it left off
2066 */
2067
2068 save->enter(save, "(list ;; objects in %s\n", place->tag);
2069
2070 pnode = node_prev(place->turn_elem);
2071 do {
2072 pnode = node_prev(pnode);
2073 if (pnode != &place->turn_list) {
2074 place_save_object((class Object*)pnode->ptr, save);
2075 }
2076 } while (pnode != node_prev(place->turn_elem));
2077
2078 save->exit(save, ") ;; end of objects in %s\n", place->tag);
2079 }
2080
place_add_on_entry_hook(struct place * place,closure_t * hook_fx)2081 void place_add_on_entry_hook(struct place *place, closure_t *hook_fx)
2082 {
2083 closure_list_t *node = (closure_list_t*)malloc(sizeof(*node));
2084 assert(node);
2085 node->closure = hook_fx;
2086 list_add(&place->on_entry_hook, &node->list);
2087 }
2088
place_save_hooks(struct place * place,struct save * save)2089 static void place_save_hooks(struct place *place, struct save *save)
2090 {
2091 struct list *elem;
2092
2093 if (list_empty(&place->on_entry_hook)) {
2094 save->write(save, "nil ;; on-entry-hook\n");
2095 return;
2096 }
2097
2098 save->enter(save, "(list ;; on-entry-hooks\n");
2099 list_for_each(&place->on_entry_hook, elem) {
2100 closure_list_t *node = (closure_list_t*)elem;
2101 closure_save(node->closure, save);
2102 }
2103 save->exit(save, ")\n");
2104 }
2105
place_save_edge_entrances(struct place * place,struct save * save)2106 static void place_save_edge_entrances(struct place *place, struct save *save)
2107 {
2108 int dir;
2109
2110 save->enter(save, "(list ;; edge entrances\n");
2111 for (dir = 0; dir < NUM_PLANAR_DIRECTIONS; dir++) {
2112 save->write(save, "(list %3d %3d %3d) ;; %s\n", dir,
2113 place->edge_entrance[dir][0],
2114 place->edge_entrance[dir][1],
2115 directionToString(dir));
2116 }
2117 save->exit(save, ")\n");
2118 }
2119
place_save(struct save * save,void * val)2120 void place_save(struct save *save, void *val)
2121 {
2122 struct place *place;
2123 struct list *elem;
2124 int i;
2125
2126 place = (struct place *)val;
2127
2128 if (place->saved == save->session_id) {
2129 /* Already saved once for this session, so do nothing. NOTE: If
2130 * someone is trying to reference this place they need to peek
2131 * in here and use the tag if the place is already saved. It's
2132 * not always valid for me to just write the tag here
2133 * automatically. ADDENDUM: not possible when the session is
2134 * ripping through its entries to save them (unless I add
2135 * another wrapper for place_save), and it turns out to be
2136 * harmless. It puts the occasional place var alone in the
2137 * script, but the interpreter just evaluates it and goes on.
2138 */
2139 save->write(save, "%s\n", place->tag);
2140 return;
2141 }
2142
2143 place->saved = save->session_id;
2144 place->saving_now = 1;
2145
2146 save->enter(save, "(kern-mk-place '%s \"%s\"\n",
2147 place->tag,
2148 place->name);
2149 save->write(save, "%s ;; sprite\n",
2150 place->sprite ? sprite_get_tag(place->sprite) : "nil");
2151 terrain_map_save(save, place->terrain_map);
2152 save->write(save, "%s %s %s %s\n",
2153 place->wraps ? "#t" : "#f",
2154 place->underground ? "#t" : "#f",
2155 place->wilderness ? "#t" : "#f",
2156 place->is_wilderness_combat ? "#t" : "#f"
2157 );
2158
2159 /* Save all subplaces recursively. */
2160
2161 save->write(save, ";; subplaces\n");
2162 if (list_empty(&place->subplaces)) {
2163 save->write(save, "nil\n");
2164 } else {
2165 save->enter(save, "(list\n");
2166 list_for_each(&place->subplaces, elem) {
2167 struct place *subplace;
2168 subplace = outcast(elem, struct place, container_link);
2169 assert(subplace->location.place = place);
2170 save->enter(save, "(list\n");
2171 place_save(save, subplace);
2172 save->exit(save, "%d %d) ;; coords of %s\n",
2173 subplace->location.x,
2174 subplace->location.y,
2175 subplace->tag);
2176 }
2177 save->exit(save, ") ; end of subplaces\n");
2178 }
2179
2180 /* Save the neighbors recursively. Subtle note: because neighborness
2181 * is a symmetric relationship, and scheme does not permit forward
2182 * declarations, and I don't want to use tags in lieue of recursive
2183 * definitions, I don't reference neighbors that have already been
2184 * saved. The neighborly relation was already recorded when the
2185 * neighbor was saved, and it will be duly restored when the neighbor
2186 * is loaded, so we don't need to do it here. Furthermore, we CANNOT do
2187 * it here because we are probably dead smack in the middle of the save
2188 * routine for that neighbor, so we can't refer to it yet.
2189 *
2190 * Even more subtle: if the neighbor is in the process of being saved
2191 * right now, but it is not our immediate parent in the save call
2192 * stack, then upon return to it it will NOT save the neighborly
2193 * relation if it thinks we already saved it here! For this reason I
2194 * added the "saving_now" flag. If the neighbor is saved, but not
2195 * saving now, then I can safely return to its scheme variable name
2196 * now, and should do so to preserve the relation.
2197 */
2198
2199 /* first check if there are any unsaved neighbors */
2200 for (i = 0; i < array_sz(place->neighbors); i++) {
2201 if (place->neighbors[i]
2202 //&& place->neighbors[i]->saved != save->session_id
2203 && ! place->neighbors[i]->saving_now
2204 )
2205 break;
2206 }
2207
2208 if (i == array_sz(place->neighbors)) {
2209 /* nope */
2210 save->write(save, "nil ;; neighbors\n");
2211 } else {
2212 /* yep */
2213 save->enter(save, "(list\n");
2214 for (i = 0; i < array_sz(place->neighbors); i++) {
2215 if (place->neighbors[i]
2216 //&& place->neighbors[i]->saved != save->session_id
2217 && ! place->neighbors[i]->saving_now
2218 ) {
2219 save->enter(save, "(list\n");
2220 place_save(save, place->neighbors[i]);
2221 save->exit(save, "%d)\n", i);
2222 }
2223 }
2224 save->exit(save, ") ;; end neighbors of %s\n", place->tag);
2225 }
2226
2227 /* Save the contents */
2228
2229 //save->enter(save, "(list ;; objects in %s\n", place->tag);
2230 //place_for_each_object(place, place_save_object, save);
2231 //save->exit(save, ") ;; end of objects in %s\n", place->tag);
2232 place_save_objects(place, save);
2233 place_save_hooks(place, save);
2234 place_save_edge_entrances(place, save);
2235 save->exit(save, ") ;; end of place %s\n\n", place->tag);
2236 place->saving_now = 0;
2237 }
2238
place_start_object(class Object * object,void * data)2239 static void place_start_object(class Object *object, void *data)
2240 {
2241 object->start();
2242 }
2243
place_start(void * val)2244 void place_start(void *val)
2245 {
2246 struct place *place;
2247 struct list *elem;
2248
2249 place = (struct place *)val;
2250
2251 /* Don't start places multiple times. It tends to screw up things, like
2252 * starting cumulative effects on objects multiple times. */
2253 if (place->started) {
2254 return;
2255 }
2256 place->started = 1;
2257
2258 // --------------------------------------------------------------------
2259 // Start all subplaces recursively.
2260 // --------------------------------------------------------------------
2261
2262 list_for_each(&place->subplaces, elem) {
2263 struct place *subplace;
2264 subplace = outcast(elem, struct place, container_link);
2265 place_start(subplace);
2266 }
2267
2268 // --------------------------------------------------------------------
2269 // Save the contents.
2270 // --------------------------------------------------------------------
2271
2272 place_for_each_object(place, place_start_object, NULL);
2273 }
2274
place_add_subplace(struct place * place,struct place * subplace,int x,int y)2275 int place_add_subplace(struct place *place, struct place *subplace,
2276 int x, int y)
2277 {
2278 struct tile *tile;
2279
2280 if (place_off_map(place, x, y))
2281 return 0;
2282
2283 tile = place_lookup_tile(place, x, y);
2284
2285 if (!tile) {
2286 tile = place_create_and_add_tile(place, x, y);
2287 if (!tile)
2288 return -1;
2289 }
2290
2291 /* Bugfix: combat_enter() was calling this and not checking the return
2292 * value. If combat was started over a town this would fail, but combat
2293 * would proceed normally. This caused two problems: 1. If we saved in
2294 * such a combat the game would fail to reload. Or, 2. when
2295 * combat_exit() called it would remove the town! The temp combat place
2296 * doesn't really need to be attached to the tile, as long as it gets
2297 * on the subplace list it will be properly saved/loaded.
2298 */
2299 if (!subplace->is_wilderness_combat) {
2300 if (tile_add_subplace(tile, subplace))
2301 return -1;
2302 }
2303
2304 // Why was I doing this?
2305 /* if (subplace->handle) { */
2306 /* session_rm(Session, subplace->handle); */
2307 /* subplace->handle = 0; */
2308 /* } */
2309
2310 subplace->location.place = place;
2311 subplace->location.x = x;
2312 subplace->location.y = y;
2313
2314 list_add(&place->subplaces, &subplace->container_link);
2315 return 0;
2316 }
2317
place_get_subplace(struct place * place,int x,int y)2318 struct place *place_get_subplace(struct place *place, int x, int y)
2319 {
2320 struct tile *tile;
2321
2322 WRAP_COORDS(place, x, y);
2323
2324 tile = place_lookup_tile(place, x, y);
2325 if (!tile)
2326 return 0;
2327 return tile->subplace;
2328 }
2329
place_remove_subplace(struct place * place,struct place * subplace)2330 void place_remove_subplace(struct place *place, struct place *subplace)
2331 {
2332 /* Bugfix: if wilderness combat is initiated over a town, on exit the
2333 * town would be removed from the map! See comments in
2334 * place_add_subplace() */
2335 if (! subplace->is_wilderness_combat) {
2336 struct tile *tile;
2337 tile = place_lookup_tile(place, subplace->location.x,
2338 subplace->location.y);
2339 assert(tile);
2340 tile_remove_subplace(tile);
2341 }
2342 list_remove(&subplace->container_link);
2343 // FIXME: make it an orphan?
2344 }
2345
place_for_each_object_at(struct place * place,int x,int y,void (* fx)(class Object *,void *),void * data)2346 void place_for_each_object_at(struct place *place, int x, int y,
2347 void (*fx)(class Object *, void *), void *data)
2348 {
2349 struct tile *tile;
2350
2351 tile = place_lookup_tile(place, x, y);
2352 if (tile)
2353 tile_for_each_object(tile, fx, data);
2354 }
2355
place_remove_and_destroy_temporary_object(class Object * obj,void * unused)2356 static void place_remove_and_destroy_temporary_object(class Object *obj,
2357 void *unused)
2358 {
2359 if (obj->isTemporary()) {
2360 place_remove_and_destroy_object(obj, unused);
2361 }
2362 }
2363
place_exit(struct place * place)2364 void place_exit(struct place *place)
2365 {
2366 place_for_each_object(place, place_remove_and_destroy_temporary_object,
2367 NULL);
2368 }
2369
place_unlock(struct place * place)2370 void place_unlock(struct place *place)
2371 {
2372 assert(place->lock);
2373
2374 place->lock--;
2375
2376 if (!place->lock && place_is_marked_for_death(place))
2377 place_del(place);
2378 }
2379
place_get_edge_entrance(struct place * place,int dir,int * x,int * y)2380 int place_get_edge_entrance(struct place *place, int dir, int *x, int *y)
2381 {
2382 if (dir < 0 || dir >= NUM_PLANAR_DIRECTIONS)
2383 return -1;
2384
2385 *x = place->edge_entrance[dir][0];
2386 *y = place->edge_entrance[dir][1];
2387
2388 return 0;
2389 }
2390
place_set_edge_entrance(struct place * place,int dir,int x,int y)2391 int place_set_edge_entrance(struct place *place, int dir, int x, int y)
2392 {
2393 if (dir < 0 || dir >= NUM_PLANAR_DIRECTIONS)
2394 return -1;
2395
2396 if (x < 0 || x >= place_w(place) ||
2397 y < 0 || y >= place_h(place))
2398 return -1;
2399
2400 place->edge_entrance[dir][0] = x;
2401 place->edge_entrance[dir][1] = y;
2402
2403 return 0;
2404 }
2405
place_get_neighbor(struct place * place,int dir)2406 struct place *place_get_neighbor(struct place *place, int dir)
2407 {
2408 if (dir < 0 || dir >= NUM_DIRECTIONS) {
2409 return NULL;
2410 }
2411 return place->neighbors[dir];
2412 }
2413
place_in_los(struct place * p1,int x1,int y1,struct place * p2,int x2,int y2)2414 int place_in_los(struct place *p1, int x1, int y1,
2415 struct place *p2, int x2, int y2)
2416 {
2417 char *vmask;
2418 int x3, y3;
2419
2420 /* Get the vmask for the origin (viewer's coords) */
2421 vmask = vmask_get(p1, x1, y1);
2422
2423 /* Translate (x2, y2) into vmask coordinates. Notationally, let:
2424
2425 a = vector from place origin to (x1, y1)
2426 b = vector from place origin to (x2, y2)
2427 o = vector from place origin to vmask origin
2428
2429 We already know a, b and (a - o) = (VMASK_W/2, VMASK_H/2). We need
2430 to solve for (b - o) and then wrap if the place supports wrapping.
2431
2432 b - a + (a - o) = b - o
2433 */
2434
2435 x3 = place_wrap_x(p1, x2 - x1 + VMASK_W/2);
2436 y3 = place_wrap_y(p1, y2 - y1 + VMASK_H/2);
2437
2438 if (x3 < 0 ||
2439 y3 < 0 ||
2440 x3 >= VMASK_W ||
2441 y3 >= VMASK_H)
2442 return 0;
2443
2444 return vmask[x3 + y3 * VMASK_W];
2445 }
2446
place_set_neighbor(struct place * place,int dir,struct place * neighbor)2447 void place_set_neighbor(struct place *place, int dir, struct place *neighbor)
2448 {
2449 int opdir = directionToOpposite(dir);
2450
2451 /* unlink current neighbors */
2452 if (place->neighbors[dir]) {
2453 place->neighbors[dir]->neighbors[opdir] = 0;
2454 }
2455
2456 if (neighbor && neighbor->neighbors[opdir]) {
2457 neighbor->neighbors[opdir]->neighbors[dir] = 0;
2458 }
2459
2460 /* link new neighbors */
2461 place->neighbors[dir] = neighbor;
2462 if (neighbor)
2463 neighbor->neighbors[opdir] = place;
2464 }
2465
place_set_terrain_map(struct place * place,struct terrain_map * map)2466 void place_set_terrain_map(struct place *place, struct terrain_map *map)
2467 {
2468 if (map == place->terrain_map)
2469 return;
2470
2471 if (place->terrain_map) {
2472 terrain_map_unref(place->terrain_map);
2473 place->terrain_map = 0;
2474 }
2475
2476 if (map) {
2477 place->terrain_map = terrain_map_clone(map, 0);
2478 }
2479 }
2480