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