OSDN Git Service

0b6179570e83941144543daf149d2f2e291988da
[nazghul-jp/nazghul-jp.git] / src / object.c
1 //
2 // nazghul - an old-school RPG engine
3 // Copyright (C) 2002, 2003 Gordon McNutt
4 //e
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 2 of the License, or (at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 // more details.
14 //
15 // You should have received a copy of the GNU General Public License along with
16 // this program; if not, write to the Free Foundation, Inc., 59 Temple Place,
17 // Suite 330, Boston, MA 02111-1307 USA
18 //
19 // Gordon McNutt
20 // gmcnutt@users.sourceforge.net
21 //
22 #include "conv.h"
23 #include "gob.h"
24 #include "object.h"
25 #include "session.h"
26 #include "place.h"
27 #include "character.h"
28 #include "map.h"
29 #include "sprite.h"
30 #include "screen.h"
31 #include "console.h"
32 #include "sound.h"
33 #include "player.h"
34 #include "terrain.h"
35 #include "vmask.h"
36 #include "Field.h"
37 #include "dice.h"
38 #include "effect.h"
39 #include "mmode.h"
40 #include "combat.h"  // for combat_get_state()
41 #include "log.h"
42
43 #include <assert.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 /*****************************************************************************/
49
50 //
51 // These GIFC_CAN_* bits need to match the script:
52 //
53 #define GIFC_CAN_GET          (1<<0)
54 #define GIFC_CAN_USE          (1<<1)
55 #define GIFC_CAN_EXEC         (1<<2)
56 #define GIFC_CAN_OPEN         (1<<3)
57 #define GIFC_CAN_HANDLE       (1<<4)
58 #define GIFC_CAN_STEP         (1<<5)
59 #define GIFC_CAN_ATTACK       (1<<6)
60 #define GIFC_CAN_MIX          (1<<7)
61 #define GIFC_CAN_ENTER        (1<<8)
62 #define GIFC_CAN_CAST         (1<<9)
63 #define GIFC_CAN_BUMP         (1<<10)
64 #define GIFC_CAN_HIT_LOCATION (1<<11)
65 #define GIFC_CAN_BUY          (1<<12)
66 #define GIFC_CAN_SEARCH       (1<<13)
67 #define GIFC_CAN_SENSE        (1<<14)
68 #define GIFC_CAN_XAMINE       (1<<15)
69 #define GIFC_CAN_DESCRIBE     (1<<16)
70 #define GIFC_CAN_ON_ATTACK     (1<<17)
71
72 ObjectType::ObjectType()
73 {
74         assert(false);
75 }
76
77 ObjectType::ObjectType(const char *tag, const char *sname, struct sprite *sprite_, 
78                        enum layer layer_)
79         : sprite(sprite_), layer(layer_), speed(0), required_action_points(0), 
80           max_hp(0), gifc(NULL), gifc_cap(0), gob(NULL), pluralName(NULL)
81 {
82         this->tag = strdup(tag);
83         assert(this->tag);
84
85         if (sname) {
86                 this->name = strdup(sname);
87                 assert(this->name);
88         } else {
89                 this->name = 0;
90         }
91 }
92
93 ObjectType::~ObjectType()
94 {
95         if (tag)
96                 free(tag);
97         if (name)
98                 free(name);
99         if (gifc)
100                 closure_unref(gifc);
101         if (pluralName)
102                 free(pluralName);
103         if (gob)
104                 gob_unref(gob);
105 }
106
107 void ObjectType::setPluralName(char *val)
108 {
109         if (pluralName)
110                 free(pluralName);
111         if (val)
112                 pluralName=strdup(val);
113         else
114                 pluralName=NULL;
115 }
116
117 char *ObjectType::getPluralName()
118 {
119         return pluralName;
120 }
121
122 bool ObjectType::init(char *tag, char *name, enum layer layer,
123                       struct sprite *sprite)
124 {
125         this->tag = strdup(tag);
126         this->name = strdup(name);
127         this->sprite = sprite;
128         this->layer = layer;
129         return (this->tag != 0 && this->name != 0);
130 }
131
132 class Object *ObjectType::createInstance()
133 {
134         return new Object(this);
135 }
136
137 void ObjectType::setSprite(struct sprite *sprite)
138 {
139         this->sprite = sprite;        
140 }
141
142 static int endsWith(const char *word, const char *end)
143 {
144         int wlen=strlen(word)-1;
145         int elen=strlen(end)-1;
146
147         if (wlen<elen)
148                 return 0;
149         
150         while (elen>=0) {
151                 if (word[wlen--]!=end[elen--])
152                         return 0;
153         }
154
155         return 1;
156 }
157
158 void ObjectType::describeType(int count)
159 {
160         if (1 == count) {
161                 if (isvowel(name[0]))
162                         log_continue("an ");
163                 else
164                         log_continue("a ");
165                 log_continue("%s", getName());
166         } else if (getPluralName()) {
167                 log_continue("some %s (%d)", getPluralName(), count);
168         } else {
169                 if (endsWith(name, "s")
170                     || endsWith(name, "sh"))
171                         log_continue("some %ses (%d)", getName(), count);
172                 else
173                         log_continue("some %ss (%d)", getName(), count);
174         }
175 }
176
177 void ObjectType::describe(Object *obj)
178 {
179         if (hasDescribeHook()) {
180                 runDescribeHook(obj);
181                 return;
182         }
183
184         describeType(obj->getCount());
185 }
186
187 bool ObjectType::isType(int classID) 
188 {
189         return (classID == OBJECT_TYPE_ID);
190 }
191
192 int ObjectType::getType()
193 {
194         return OBJECT_TYPE_ID;
195 }
196
197 const char *ObjectType::getTag()
198 {
199         return tag;
200 }
201
202 const char *ObjectType::getName()
203 {
204         return name;
205 }
206
207 struct sprite *ObjectType::getSprite()
208 {
209         return sprite;
210 }
211
212 enum layer ObjectType::getLayer()
213 {
214         return layer;
215 }
216
217 bool ObjectType::isVisible()
218 {
219         return true;
220 }
221
222 int ObjectType::getSpeed()
223 {
224         return speed;
225 }
226
227 int ObjectType::getRequiredActionPoints()
228 {
229         return required_action_points;
230 }
231
232 int ObjectType::getMaxHp()
233 {
234         return max_hp;
235 }
236
237 //////////////////////////////////////////////////////////////////////////////
238 //
239 // hook_list api
240 //
241 //////////////////////////////////////////////////////////////////////////////
242 #define hook_list_init(hl) do { list_init(&(hl)->list); (hl)->lock = 0; } while (0)
243 #define hook_list_add(hl,en) (list_add(&(hl)->list, (en)))
244 #define hook_list_lock(hl) (++(hl)->lock)
245 #define hook_list_unlock(hl) ((hl)->lock--)
246 #define hook_list_first(hl) ((hl)->list.next)
247 #define hook_list_end(hl) (&(hl)->list)
248 #define hook_list_empty(hl) (list_empty(&(hl)->list))
249 #define hook_list_for_each(hl,ptr) list_for_each(&(hl)->list, (ptr))
250 #define hook_list_locked(hl) ((hl)->lock)
251 #define hook_list_trylock(hl) (hook_list_locked(hl) ? 0 : hook_list_lock(hl))
252 #define hook_list_tryunlock(hl,lock) ((lock) ? hook_list_unlock(hl) : 0)
253
254 //////////////////////////////////////////////////////////////////////////////
255 //
256 // hook_entry api
257 //
258 //////////////////////////////////////////////////////////////////////////////
259
260 #define HEF_INVALID (1<<0)
261 #define HEF_DETECTED (1<<1)
262 #define HEF_STARTED  (1<<2)
263
264 #define hook_entry_invalidate(he) ((he)->flags |= HEF_INVALID)
265 #define hook_entry_detected(he) ((he)->flags & HEF_DETECTED)
266 #define hook_entry_detect(he) ((he)->flags |= HEF_DETECTED)
267 #define hook_entry_gob(he) ((he)->gob->p)
268 #define hook_entry_started(he) ((he)->started)
269 #define hook_entry_set_started(he) ((he)->started=1)
270
271 hook_entry_t *hook_entry_new(struct effect *effect, struct gob *gob)
272 {
273         hook_entry_t *entry;
274
275         entry = (hook_entry_t*)calloc(1, sizeof(*entry));
276         assert(entry);
277         list_init(&entry->list);
278         entry->effect = effect;
279         entry->gob = gob;
280         gob_ref(gob);
281         
282         if (effect_will_expire(effect)) {
283                 clock_alarm_set(&entry->expiration, effect->duration);
284         }
285
286         //dbg("hook_entry_new: %p\n", entry);
287         return entry;
288 }
289
290 void hook_entry_del(hook_entry_t *entry)
291 {
292         //dbg("hook_entry_del: %p\n", entry);
293         if (entry->gob)
294                 gob_unref(entry->gob);
295         free(entry);
296 }
297
298 void hook_entry_save(hook_entry_t *entry, struct save *save)
299 {
300         // Note: saved effects are loaded in kern.c:kern_mk_char() & attached
301         // via restoreEffect().
302
303         save->enter(save, "(list\n");
304         save->write(save, "%s\n", entry->effect->tag);
305         if (entry->gob)
306                 gob_save(entry->gob, save);
307         else
308                 save->write(save, "nil\n");
309         save->write(save, "%d\n", entry->flags);
310         clock_alarm_save(entry->expiration, save);
311         save->exit(save, ")\n");
312 }
313
314 static inline int hook_entry_is_invalid(hook_entry_t *entry)
315 {
316         return ((entry->flags & HEF_INVALID) ||
317                 (effect_will_expire(entry->effect) &&
318                  clock_alarm_is_expired(&entry->expiration)));
319 }
320
321 //////////////////////////////////////////////////////////////////////////////
322 //
323 // Object class methods
324 //
325 //////////////////////////////////////////////////////////////////////////////
326
327 void Object::init(int x, int y, struct place *place, class ObjectType * type)
328 {
329         // fixme: obsolete?
330         this->type = type;
331         setX(x);
332         setY(y);
333         setPlace(place);
334 }
335
336 void Object::init(class ObjectType * type)
337 {
338         this->type = type;
339 }
340
341 bool Object::tryToRelocateToNewPlace(struct place *newplace, 
342                                      int newx, int newy,
343                                      struct closure *cutscene)
344 {
345         obj_inc_ref(this);
346         place_remove_object(getPlace(), this);
347
348         if (cutscene) {
349                 
350                 mapUpdate(0);
351                 closure_exec(cutscene, NULL);
352                 
353         }
354
355         setPlace(newplace);
356         setX(newx);
357         setY(newy);
358         place_add_object(getPlace(), this);
359         obj_dec_ref(this);
360         changePlaceHook();
361         return true;
362 }
363
364 ////
365 // Trigger the topmost sense mechanism on a tile, using this as the subject
366 //
367 // @param tilePlace place the tile is in
368 // @param tileX coord of tile
369 // @param tileY coord of tile
370 //
371 void Object::triggerSense(struct place *tilePlace, int tileX, int tileY)
372 {
373         Object *mech = place_get_object(tilePlace, tileX, tileY, mech_layer);
374         if (mech 
375             && mech != this 
376             && mech->getObjectType()->canSense()) {
377                 mech->getObjectType()->sense(mech, this);
378         }
379 }
380
381 ////
382 // Trigger the topmost step mechanism on a tile, using this as the subject
383 //
384 // @param tilePlace place the tile is in
385 // @param tileX coord of tile
386 // @param tileY coord of tile
387 //
388 void Object::triggerStep(struct place *tilePlace, int tileX, int tileY)
389 {
390         Object *mech = place_get_object(tilePlace, tileX, tileY, mech_layer);
391         if (mech 
392             && mech != this 
393             && mech->getObjectType()->canStep()) {
394                 mech->getObjectType()->step(mech, this);
395         }
396 }
397
398 ////
399 // Run the step and sense triggers on tile entry using this as the subject,
400 // flags permitting.
401 //
402 // @param tilePlace place the tile is in
403 // @param tileX coord of tile
404 // @param tileY coord of tile
405 // @param flags relocation flags which specify what types of triggers to avoid
406 //
407 void Object::triggerOnTileEntry(struct place *tilePlace, int tileX, int tileY,
408                                 int flags)
409 {
410         bool sense = !(flags & REL_NOSENSE);
411         bool step = !(flags & REL_NOSTEP);
412
413         if (sense) {
414                 triggerSense(tilePlace, tileX, tileY);
415         }
416
417         if (step) {
418                 triggerStep(tilePlace, tileX, tileY);
419         }
420 }
421
422 ////
423 // Run the sense trigger on tile exit using this as the subject, flags
424 // permitting.
425 //
426 // @param tilePlace place the tile is in
427 // @param tileX coord of tile
428 // @param tileY coord of tile
429 // @param flags relocation flags which specify what types of triggers to avoid
430 //
431 void Object::triggerOnTileExit(struct place *tilePlace, int tileX, int tileY,
432                                int flags)
433 {
434         bool sense = ! (flags & REL_NOSENSE);
435
436         if (sense) {
437                 triggerSense(tilePlace, tileX, tileY);
438         }
439 }
440
441 void Object::relocate(struct place *newplace, int newx, int newy, int flags,
442                       struct closure *place_switch_hook)
443 {
444         int volume;
445         int distance;
446         struct place *foc_place;
447         int foc_x, foc_y;
448         struct place *oldPlace = getPlace(); // remember for tile exit
449         int oldX = getX(); // remember for tile exit
450         int oldY = getY(); // remember for tile exit
451
452         assert(newplace);
453
454         if (isOnMap()) {
455
456                 assert(getPlace());
457
458                 if (newplace == getPlace()) {
459
460                         // Moving from one tile to another in the same place.
461                         if (place_switch_hook) {
462
463                                 // A cut scene was specified (this happens for
464                                 // moongate entry, for example). Remove the
465                                 // object, play the cut scene, and put the
466                                 // object back down at the new location.
467                                 mapUpdate(0);
468                                 setOnMap(false);
469                                 rmView();
470                                 obj_inc_ref(this);
471                                 place_remove_object(getPlace(), this);
472
473                                 closure_exec(place_switch_hook, NULL);
474
475                                 setPlace(newplace);
476                                 setX(newx);
477                                 setY(newy);
478                                 place_add_object(newplace, this);
479                                 obj_dec_ref(this);
480                                 setOnMap(true);
481                                 addView();
482
483                         } else {
484
485                                 place_move_object(newplace, this, newx, newy);
486                                 setX(newx);
487                                 setY(newy);
488                         }
489
490                 } else {
491
492                         // Place-to-place movement, where the object is on the
493                         // map. This is a special case for character objects so
494                         // use an overloadable method to implement it.
495                         if (! tryToRelocateToNewPlace(newplace, newx, newy,
496                                                       place_switch_hook)) {
497                                 return;
498                         }
499
500                         // This object may no longer be on a map as a result of
501                         // the above call. If so then finish processing.
502                         if (! isOnMap()) {
503
504                                 // Run the exit triggers before returning
505                                 if (oldPlace) {
506                                         triggerOnTileExit(oldPlace, oldX, oldY, flags);
507                                 }
508
509                                 return;
510                         }
511                 }
512
513         } else {
514
515                 // Place-to-place movement, where the object is off-map in the
516                 // old place. I assume by default it will be on-map in the new
517                 // place and let changePlaceHook() fix things up if necessary.
518                 setPlace(newplace);
519                 setX(place_wrap_x(newplace, newx));
520                 setY(place_wrap_y(newplace, newy));
521                 place_add_object(getPlace(), this);
522                 setOnMap(true);
523                 addView();
524                 changePlaceHook();
525
526         }
527
528         // Run the exit triggers.
529         if (oldPlace) {
530                 triggerOnTileExit(oldPlace, oldX, oldY, flags);
531         }
532         
533         mapSetDirty();
534
535         // It's possible that changePlaceHook() removed this object from the
536         // map. This certainly happens when the player party moves from town to
537         // wilderness, for example. In this case I probably want to skip all of
538         // what follows. I ABSOLUTELY want to skip the call to updateView() at
539         // the end, and in fact I changed updateView() to assert if this object
540         // is not on the map.
541         if (! isOnMap())
542                 return;
543
544         // Attenuate movement sound based on distance from the camera's focal
545         // point.
546         volume = SOUND_MAX_VOLUME;
547         mapGetCameraFocus(&foc_place, &foc_x, &foc_y);
548         if (foc_place == getPlace()) {
549                 distance = place_flying_distance(foc_place, foc_x, foc_y, 
550                                                  getX(), getY());
551                 if (distance > 1)
552                         volume = (volume * (20 - distance))/20;
553                 if (volume > 0)
554                         sound_play(get_movement_sound(), volume);
555         }
556
557         // If the camera is attached to this object then update it to focus on
558         // the object's new location.
559         if (isCameraAttached()) {
560                 mapCenterCamera(getX(), getY());
561         }
562         
563         updateView();
564
565         // Run the entry triggers.
566         triggerOnTileEntry(getPlace(), getX(), getY(), flags);
567 }
568
569 void Object::setOnMap(bool val)
570 {
571         is_on_map = val;
572 }
573
574 void Object::remove()
575 {
576         obj_inc_ref(this);
577         if (isOnMap())
578         {
579                 struct place *oldPlace = getPlace(); // remember for tile exit
580                 int oldX = getX(); // remember for tile exit
581                 int oldY = getY(); // remember for tile exit
582                 setOnMap(false);
583                 rmView();
584                 
585                 place_remove_object(getPlace(), this);
586                 
587                 // Run the exit triggers.
588                 if (oldPlace)
589                 {
590                         triggerOnTileExit(oldPlace, oldX, oldY, 0);
591                 }
592                 
593                 if (isOpaque()) {
594                         vmask_flush_all();
595                 }
596
597                 // Note: do NOT call setPlace(NULL) here. When the player party
598                 // object is removed from the map it still needs to "know" what
599                 // place the members are in.
600         }
601         endTurn();
602         attachCamera(false);
603         obj_dec_ref(this);
604 }
605
606 void Object::paint(int sx, int sy)
607 {
608         struct sprite *sprite = getSprite();
609         if (sprite) {
610                 int origFacing = sprite_get_facing(sprite);
611                 sprite_set_facing(sprite, facing);
612                 if (TimeStop
613                     && isPlayerControlled()) {
614                         sprite_paint(sprite, 
615                                      sprite_frame + Session->time_stop_ticks, 
616                                      sx, sy);
617                 } else {
618                         sprite_paint(sprite, sprite_frame, sx, sy);
619                 }
620                 sprite_set_facing(sprite, origFacing);
621         }
622 }
623
624 void Object::describe()
625 {
626         assert(getObjectType()); // else implement this method in subclass
627         getObjectType()->describe(this);
628         if (!isVisible()) {
629                 log_continue(" (invisible)");
630         }
631         if (isSubmerged()) {
632                 log_continue(" (submerged)");
633         }
634 }
635
636 void Object::examine()
637 {
638         assert(getObjectType()); // else implement this method in subclass
639         describe();
640
641         //todo: dont have examiner to pass in to ifc
642         if (getObjectType()->canXamine()) {
643                 log_end(":");
644                 getObjectType()->xamine(this, this);
645                 log_begin("");
646         }
647 }
648
649 sound_t *Object::get_movement_sound()
650 {
651         return NULL_SOUND;
652 }
653
654 class Object *Object::clone()
655 {
656         // gmcnutt: added support for an optional quantity field for placed
657         // objects.
658
659         class Object *obj;
660
661         obj = getObjectType()->createInstance();
662         obj->setPlace(getPlace());
663         obj->setX(getX());
664         obj->setY(getY());
665
666         // FIXME: should assign the new object a unique script tag
667
668         return obj;
669 }
670
671 //////////////////////////////////////////////////
672
673 bool Object::isType(int classID) 
674 {
675         return (classID == OBJECT_ID);
676 }
677 int Object::getType() 
678 {
679         return OBJECT_ID;
680 }
681
682 Object::Object()
683 {
684         type = NULL;
685         setup();
686 }
687
688 Object::Object(class ObjectType * type) 
689 {
690         this->type = type;
691         setup();
692 }
693
694 void Object::setup()
695 {
696         int i;
697
698         clink     = NULL;
699         turn_list = NULL;
700
701         for (i = 0; i < OBJ_NUM_HOOKS; i++) {
702                 hook_list_init(&hooks[i]);
703         }
704
705         x               = -1;
706         y               = -1;
707         place           = NULL;
708         selected        = false;
709         destroyed       = false;
710         tag             = 0;
711         action_points   = 0; /* FIXME: assumes no debt */
712         control_mode    = CONTROL_MODE_AUTO;
713         camera_attached = false;
714         hp              = 0;
715         is_on_map       = false;
716         conv            = NULL;
717         view            = NULL;
718         saved           = 0;
719         handle          = 0;
720         refcount        = 0;
721         count           = 1;
722         gob             = NULL;
723         current_sprite  = NULL;
724         opacity         = false;
725         light           = 0;
726         temporary       = false;
727         forceEffect     = false;
728         pclass          = PCLASS_NONE;
729         ttl             = -1; // everlasting by default
730         started         = false;
731         facing          = SPRITE_DEF_FACING;
732         ignoreTimeStop  = false;
733         submerged       = false;
734         portrait        = NULL;
735
736         if (getObjectType() && ! getObjectType()->isVisible())
737                 visible = 0;
738         else
739                 visible = 1;
740
741         // Four is the typical max number of frames, so using more will not
742         // help (it won't hurt either, sprites.c will ensure the frame is
743         // within what the sprite will actually support).
744         sprite_frame = rand() % 4;
745 }
746
747 Object::~Object()
748 {
749         int i;
750
751         if (refcount) {
752                 dbg("refcount=%d\n", refcount);
753                 assert(! refcount);
754         }
755
756         //dbg("destroying %d %08lx %s\n", refcount, this, getName());
757
758         if (handle) {
759                 session_rm(Session, handle);
760                 handle = 0;
761         }
762         if (tag) {
763                 free(tag);
764                 tag = 0;
765         }
766
767         if (gob)
768                 gob_del(gob);
769
770         if (getView()) {
771                 rmView();
772                 mapDestroyView(getView());
773                 setView(NULL);                
774         }                
775
776         if (conv) {
777                 conv_unref(conv);
778         }
779
780         // For each type of hook...
781         for (i = 0; i < OBJ_NUM_HOOKS; i++) {
782
783                 // Shouldn't be destroying the object while one of its hook
784                 // lists is locked.
785                 assert(! hook_list_locked(&hooks[i]));
786
787                 // This is a hack to workaround a bug due to a design flaw. A
788                 // request has been logged to fix the design flaw [SF
789                 // 1568398]. It will be a "deep" fix, and I expect it to add
790                 // lots of new bugs, so I'm going to see if this relatively
791                 // easy change will get us by a bit longer.
792                 //
793                 // The bug is this: we land here in the process of a
794                 // session_del() call. Some of our effects have already been
795                 // destroyed. In hookForEach() it will inspect some of these
796                 // effects, not knowing that they are destroyed, and cause a
797                 // crash. So instead of using hookForEach() I'm going to
798                 // destroy the lists by hand right here without calling any
799                 // hook removal closures or anything like that.
800                 struct list *lptr;
801                 struct hook_list *hl;
802                 hl = &hooks[i];
803                 lptr = hook_list_first(hl);
804                 while (lptr != hook_list_end(hl)) {
805                         hook_entry_t *he = outcast(lptr, hook_entry_t, list);
806                         lptr = lptr->next;
807                         list_remove(&he->list);
808                         hook_entry_del(he);
809                 }
810         }
811 }
812
813 int Object::getX()
814 {
815         return x;
816 }
817
818 int Object::getY()
819 {
820         return y;
821 }
822
823 struct place *Object::getPlace()
824 {
825         return place;
826 }
827
828 struct sprite *Object::getSprite()
829 {
830         if (current_sprite)
831                 return current_sprite;
832         if (type)
833                 return type->getSprite();
834         return NULL;
835 }
836
837 bool Object::isSelected()
838 {
839         return selected;
840 }
841
842 enum layer Object::getLayer(void)
843 {
844         // subtle: ~Being runs, calls ~Object, calls hookForEach to delete all
845         // the hooks, but there's an invalid hook which gets it's rm closure
846         // invoked. In that closure it calls kern-obj-is-being, which lands us
847         // here instead of Being::getLayer() because of where we are in the
848         // destructor chain, and Being's have no object type... hopefully since
849         // the object is being destroyed it doesn't really matter what the
850         // layer is.
851         if (getObjectType())
852                 return getObjectType()->getLayer();
853         else
854                 return null_layer;
855 }
856
857 const char *Object::getName(void)
858 {
859         if (type)
860                 return type->getName();
861         return "<no type>";
862 }
863
864 class ObjectType *Object::getObjectType()
865 {
866         return type;
867 }
868
869 bool Object::isDestroyed()
870 {
871         return destroyed;
872 }
873
874
875 void Object::setX(int x)
876 {
877         this->x = x;
878 }
879
880 void Object::setY(int y)
881 {
882         this->y = y;
883 }
884
885 void Object::changeX(int dx)
886 {
887         this->x += dx;
888 }
889
890 void Object::changeY(int dy)
891 {
892         this->y += dy;
893 }
894
895 void Object::setPlace(struct place *place)
896 {
897         this->place = place;
898 }
899
900 void Object::select(bool val)
901 {
902         if (val == isSelected())
903                 return;
904
905         if (val) {
906                 mapSetSelected(this);
907         } else if (isSelected()) {
908                 mapSetSelected(NULL);
909         }
910
911         selected = val;
912         mapUpdateTile(getPlace(), getX(), getY());        
913         //mapUpdate(0);
914 }
915
916 void Object::destroy()
917 {
918         destroyed = true;
919         if (isSelected())
920                 select(false);
921         remove();
922 }
923
924 int Object::getLight()
925 {
926         return light;
927 }
928
929 void Object::exec()
930 {
931         startTurn();
932         if (getObjectType()->canExec())
933                 getObjectType()->exec(this);
934         endTurn(); // warn: might destroy this!
935         Object::decrementTTL(this); // might destroy the object!
936 }
937
938 void Object::synchronize()
939 {
940 }
941
942 bool Object::isVisible()
943 {
944         //return getObjectType()->isVisible();
945         return visible > 0;
946 }
947
948 void Object::setVisible(bool val)
949 {
950         if (val)
951                 visible++;
952         else {
953                 visible--;
954                 if (visible < 0 && getName()) {
955                         printf("%s: %d\n", getName(), visible);
956                 }
957         }
958 }
959
960 bool Object::isSubmerged()
961 {
962         return submerged;
963 }
964
965 void Object::setSubmerged(bool val)
966 {
967         submerged = val;
968 }
969
970 bool Object::isShaded()
971 {
972         return isSubmerged();
973 }
974
975 void Object::setOpacity(bool val)
976 {
977         // If the opacity is changing then invalidate the view mask cache in
978         // the surrounding area.
979         if (val != opacity && isOnMap())
980                 vmask_invalidate(getPlace(), getX(), getY(), 1, 1);                
981
982         opacity = val;
983 }
984
985 bool Object::isOpaque()
986 {
987         return opacity;
988 }
989
990 bool Object::joinPlayer()
991 {
992         return false;
993 }
994
995 int Object::getActivity()
996 {
997         return 0;
998 }
999
1000 int Object::getActionPointsPerTurn()
1001 {
1002         int baseAP = (int)(getSpeed() * session_get_time_accel());
1003
1004         // If 'Quicken' is in effect then give player-controlled objects bonus
1005         // action points per turn.
1006         if (Quicken > 0 && isPlayerControlled()) {
1007                 return baseAP * 2;
1008         }
1009
1010         // If 'TimeStop' is in effect then give action points ONLY to
1011         // player-controlled objects.
1012         if (TimeStop && ! isPlayerControlled()) {
1013                 return 0;
1014         }
1015
1016         return baseAP;
1017 }
1018
1019 void Object::applyEffect(closure_t *effect)
1020 {
1021         closure_exec(effect, "p", this);
1022 }
1023
1024 void Object::burn()
1025 {
1026 }
1027
1028 void Object::sleep()
1029 {
1030 }
1031
1032 sound_t *Object::getDamageSound()
1033 {
1034         return NULL_SOUND;
1035 }
1036
1037 void Object::damage(int amount)
1038 {
1039         // Paint the red "*" damage symbol over the character's icon on the map
1040         if (isOnMap()) {
1041                 mapPaintDamage(getX(), getY());        
1042                 sound_play(getDamageSound(), SOUND_MAX_VOLUME);
1043         }
1044
1045         runHook(OBJ_HOOK_DAMAGE, 0);
1046 }
1047
1048 void Object::inflictDamage(int amount, class Character *attacker)
1049 {
1050     damage(amount);
1051 }
1052
1053 int Object::getActionPoints()
1054 {
1055         return action_points;
1056 }
1057
1058 void Object::decActionPoints(int points)
1059 {
1060         setActionPoints(action_points - points);
1061 }
1062
1063 void Object::endTurn()
1064 {
1065         if (action_points > 0)
1066         {
1067                 setActionPoints(0);
1068         }
1069 }
1070
1071 void Object::startTurn()
1072 {
1073         setActionPoints(action_points + getActionPointsPerTurn());
1074         runHook(OBJ_HOOK_START_OF_TURN, 0);
1075 }
1076
1077 int Object::getSpeed()
1078 {
1079         return getObjectType()->getSpeed();
1080 }
1081
1082 int Object::getRequiredActionPoints()
1083 {
1084         return getObjectType()->getRequiredActionPoints();
1085 }
1086
1087 bool Object::isOnMap()
1088 {
1089         return is_on_map;
1090 }
1091
1092 bool Object::isDead()
1093 {
1094         return false;
1095 }
1096
1097 enum control_mode Object::getControlMode()
1098 {
1099         return control_mode;
1100 }
1101
1102 void Object::setControlMode(enum control_mode mode)
1103 {
1104     control_mode = mode;
1105 }
1106
1107 void Object::attachCamera(bool val)
1108 {
1109         if (camera_attached == val)
1110                 return;
1111
1112         camera_attached = val;
1113
1114         if (val)
1115                 mapAttachCamera(this);
1116         else
1117                 mapDetachCamera(this);
1118 }
1119
1120 bool Object::isCameraAttached()
1121 {
1122         return camera_attached;
1123 }
1124
1125 bool Object::isTurnEnded()
1126 {
1127         return (getActionPoints() <= 0 ||
1128                 isDead() ||
1129                 Quit);
1130 }
1131
1132 bool Object::isPlayerPartyMember()
1133 {
1134         return false;
1135 }
1136
1137 bool Object::addToInventory(class Object *object)
1138 {
1139         return false;
1140 }
1141
1142 bool Object::hasInInventory(class ObjectType *object)
1143 {
1144         return false;
1145 }
1146
1147 void Object::heal(int amount)
1148 {
1149         amount = min(amount, getMaxHp() - hp);
1150         hp += amount;
1151 }
1152
1153 bool Object::isPlayerControlled()
1154 {
1155         return false;
1156 }
1157
1158 int Object::getMaxHp()
1159 {
1160         return getObjectType()->getMaxHp();
1161 }
1162
1163 int Object::getHp()
1164 {
1165         return hp;
1166 }
1167
1168 bool Object::isCompanionOf(class Object *other)
1169 {
1170         return false;
1171 }
1172
1173 int Object::getPclass()
1174 {
1175         return pclass;
1176 }
1177
1178 void Object::setPclass(int val)
1179 {
1180         pclass = val;
1181 }
1182
1183 int Object::getMovementCost(int pclass)
1184 {        
1185         if (pclass == PCLASS_NONE)
1186                 return 0;
1187
1188         struct mmode *mmode = getMovementMode();
1189         if (! mmode)
1190                 return PTABLE_IMPASSABLE;
1191
1192         return ptable_get(session_ptable(), mmode->index, pclass);
1193 }
1194
1195 bool Object::isPassable(int pclass)
1196 {
1197         return (getMovementCost(pclass) != PTABLE_IMPASSABLE);
1198 }
1199
1200 bool Object::putOnMap(struct place *new_place, int new_x, int new_y, int r,
1201                       int flags)
1202 {
1203         // --------------------------------------------------------------------
1204         // Put an object on a map. If possible, put it at (new_x, new_y). If
1205         // that's not possible then put it at some other (x, y) such that:
1206         // 
1207         // o (x, y) is with radius r of (new_x, new_y)
1208         //
1209         // o The object can find a path from (x, y) to (new_x, new_y)
1210         //
1211         // If no such (x, y) exists then return false without placing the
1212         // object.
1213         // --------------------------------------------------------------------
1214
1215         char *visited;
1216         char *queued;
1217         bool ret = false;
1218         int i;
1219         int rx;
1220         int ry;
1221         int *q_x;
1222         int *q_y;
1223         int index;
1224         int q_head;
1225         int q_tail;
1226         int q_size;
1227         int x_offsets[] = { -1, 1, 0, 0 };
1228         int y_offsets[] = { 0, 0, -1, 1 };
1229         
1230         printf("Putting %s near (%d %d)\n", getName(), new_x, new_y);
1231
1232         // --------------------------------------------------------------------
1233         // Although the caller specified a radius, internally I use a bounding
1234         // box. Assign the upper left corner in place coordinates. I don't
1235         // *think* I have to worry about wrapping coordinates because all the
1236         // place_* methods should do that internally.
1237         // --------------------------------------------------------------------
1238
1239         rx = new_x - (r / 2);
1240         ry = new_y - (r / 2);
1241
1242         // --------------------------------------------------------------------
1243         // Initialize the 'visited' table and the coordinate search queues. The
1244         // queues must be large enough to hold all the tiles in the bounding
1245         // box, plus an extra ring around it. The extra ring is for the case
1246         // where we enqueue the neighbors of tiles that are right on the edge
1247         // of the bounding box. We won't know the neighbors are bad until we
1248         // pop them off the queue and check them.
1249         // --------------------------------------------------------------------
1250
1251         q_size = r * r;
1252
1253         visited = (char*)calloc(sizeof(char), q_size);
1254         if (NULL == visited)
1255                 return false;
1256                 
1257         q_x = (int*)calloc(sizeof(int), q_size);
1258         if (NULL == q_x) {
1259                 goto free_visited;
1260         }
1261
1262         q_y = (int*)calloc(sizeof(int), q_size);
1263         if (NULL == q_y) {
1264                 goto free_q_x;
1265         }
1266
1267         queued = (char*)calloc(sizeof(char), q_size);
1268         if (NULL == queued)
1269                 goto free_q_y;
1270
1271         // --------------------------------------------------------------------
1272         // Enqueue the preferred location to start the search.
1273         // --------------------------------------------------------------------
1274
1275 #define INDEX(x,y) (((y)-ry) * r + ((x)-rx))
1276
1277         q_head        = 0;
1278         q_tail        = 0;
1279         q_x[q_tail]   = new_x;
1280         q_y[q_tail]   = new_y;
1281         index         = INDEX(new_x, new_y);
1282         assert(index >= 0);
1283         assert(index < q_size);
1284         queued[index] = 1;
1285         q_tail++;
1286
1287         // --------------------------------------------------------------------
1288         // Run through the search queue until it is exhausted or a safe
1289         // position has been found.
1290         // --------------------------------------------------------------------
1291
1292         while (q_head != q_tail) {
1293
1294                 // ------------------------------------------------------------
1295                 // Dequeue the next location to check.
1296                 // ------------------------------------------------------------
1297
1298                 new_x = q_x[q_head];
1299                 new_y = q_y[q_head];
1300                 q_head++;
1301
1302                 printf("Checking (%d,%d)...", new_x, new_y);
1303
1304                 // ------------------------------------------------------------
1305                 // Has the location already been visited? (If not then mark it
1306                 // as visited now).
1307                 // ------------------------------------------------------------
1308                 
1309                 index = INDEX(new_x, new_y);
1310                 assert(index >= 0);
1311                 assert(index < q_size);
1312
1313                 if (0 != visited[index]) {
1314                         printf("already checked\n");
1315                         continue;
1316                 }
1317                 visited[index] = 1;
1318
1319                 // ------------------------------------------------------------
1320                 // Is the location off the map or impassable?
1321                 // ------------------------------------------------------------
1322                 
1323                 if (place_off_map(new_place, new_x, new_y) ||
1324                     ! place_is_passable(new_place, new_x, new_y, this, flags)){
1325                         continue;
1326                 }
1327
1328                 // ------------------------------------------------------------
1329                 // Is the location occupied or hazardous?
1330                 // ------------------------------------------------------------
1331
1332                 if ((! (flags & PFLAG_IGNOREBEINGS) &&
1333                      place_is_occupied(new_place, new_x, new_y)) ||
1334                     (! (flags & PFLAG_IGNOREHAZARDS) &&
1335                      place_is_hazardous(new_place, new_x, new_y))) {
1336
1337                         printf("occupied or hazardous\n");
1338
1339                         // ----------------------------------------------------
1340                         // This place is not suitable, but its neighbors might
1341                         // be. Put them on the queue.
1342                         // ----------------------------------------------------
1343
1344                         for (i = 0; i < array_sz(x_offsets); i++) {
1345
1346                                 int neighbor_x;
1347                                 int neighbor_y;
1348                                 int neighbor_index;
1349
1350                                 neighbor_x = new_x + x_offsets[i];
1351                                 neighbor_y = new_y + y_offsets[i];
1352
1353                                 // --------------------------------------------
1354                                 // Is the neighbor outside the search radius?
1355                                 // --------------------------------------------
1356
1357                                 if (neighbor_x < rx        ||
1358                                     neighbor_y < ry        ||
1359                                     neighbor_x >= (rx + r) ||
1360                                     neighbor_y >= (ry + r)) {
1361                                         continue;
1362                                 }
1363                                 
1364                                 // --------------------------------------------
1365                                 // Has the neighbor already been queued?
1366                                 // --------------------------------------------
1367
1368                                 neighbor_index = INDEX(neighbor_x, neighbor_y);
1369                                 assert(neighbor_index >= 0);
1370                                 assert(neighbor_index < (q_size));
1371
1372                                 if (queued[neighbor_index])
1373                                         continue;
1374
1375                                 // --------------------------------------------
1376                                 // Enqueue the neighbor
1377                                 // --------------------------------------------
1378
1379                                 assert(q_tail < (q_size));
1380
1381                                 q_x[q_tail] = neighbor_x;
1382                                 q_y[q_tail] = neighbor_y;
1383                                 q_tail++;
1384                                 queued[neighbor_index] = 1;
1385                         }
1386                         
1387                         continue;
1388                 }
1389
1390                 // ------------------------------------------------------------
1391                 // I've found a good spot, and I know that I can pathfind back
1392                 // to the preferred location from here because of the manner in
1393                 // which I found it.
1394                 //
1395                 // Note: we don't want to activate step triggers while doing
1396                 // this. One example of why this is bad is if we get here by
1397                 // coming through a moongate on a town map. If the moongate is
1398                 // in a state where it leads to itself we could end up
1399                 // re-entering it with the relocate call below if we allowed
1400                 // stepping.
1401                 // ------------------------------------------------------------
1402
1403                 printf("OK!\n");
1404                 relocate(new_place, new_x, new_y, REL_NOSTEP, NULL);
1405                 ret = true;
1406
1407                 goto done;
1408         }
1409
1410         // --------------------------------------------------------------------
1411         // Didn't find anyplace suitable. Return false. If the caller wants to
1412         // force placement I'll leave it to their discretion.
1413         // --------------------------------------------------------------------
1414
1415         printf("NO PLACE FOUND!\n");
1416
1417  done:
1418         free(queued);
1419  free_q_y:
1420         free(q_y);
1421  free_q_x:
1422         free(q_x);
1423  free_visited:
1424         free(visited);
1425
1426         return ret;
1427
1428 }
1429
1430 struct mview *Object::getView()
1431 {
1432         return view;
1433 }
1434
1435 int Object::getVisionRadius()
1436 {
1437         return 0;
1438 }
1439
1440 void Object::addView()
1441 {
1442         if (NULL != getView()) {
1443                 mapAddView(getView());
1444                 updateView();
1445         }
1446 }
1447
1448 void Object::rmView()
1449 {
1450         if (NULL != getView()) {
1451                 mapRmView(getView());
1452         }
1453 }
1454
1455 void Object::updateView()
1456 {
1457         assert(isOnMap());
1458
1459         if (NULL != getView()) {
1460                 mapCenterView(getView(), getX(), getY());
1461                 mapSetRadius(getView(), min(getVisionRadius(), MAX_VISION_RADIUS));
1462                 mapSetDirty();
1463         }
1464 }
1465
1466 void Object::setView(struct mview *new_view)
1467 {
1468         view = new_view;
1469 }
1470
1471 void Object::changePlaceHook()
1472 {
1473 }
1474
1475 int Object::getDx()
1476 {
1477         return dx;
1478 }
1479
1480 int Object::getDy()
1481 {
1482         return dy;
1483 }
1484 bool Object::canWanderTo(int newx, int newy)
1485 {
1486         return true;
1487 }
1488 enum MoveResult Object::move(int dx, int dy)
1489 {
1490         return NotApplicable;
1491 }
1492
1493 void Object::save(struct save *save)
1494 {
1495         // Create the object within a 'let' block
1496         save->enter(save, "(let ((kobj (kern-mk-obj %s %d\n", 
1497                     getObjectType()->getTag(), 
1498                     getCount());
1499         saveHooks(save);
1500         save->write(save, ")))\n");
1501
1502         // Assign the tag.
1503         if (tag) {
1504                 save->write(save, "(kern-tag '%s kobj)\n", tag);
1505         }
1506
1507         // Save the gob binding.
1508         if (getGob()) {
1509                 save->enter(save, "(bind kobj\n");
1510                 gob_save(getGob(), save);
1511                 save->exit(save, ")\n");
1512         }
1513
1514         // Save time-to-live.
1515         if (getTTL() != -1) {
1516                 save->write(save, "(kern-obj-set-ttl kobj %d)\n",
1517                             getTTL());
1518         }
1519
1520         // Set the custom sprite.
1521         if (current_sprite) {
1522                 save->enter(save, "(kern-obj-set-sprite kobj\n");
1523                 sprite_save(current_sprite, save);
1524                 save->exit(save, ")\n");
1525         }
1526
1527         // Set the facing
1528         if (SPRITE_DEF_FACING != facing) {
1529                 save->write(save, "(kern-obj-set-facing kobj %d)\n", facing);
1530         }
1531
1532         // Set the ignore-time-stop flag
1533         if (ignoreTimeStop) {
1534                 save->write(save, "(kern-obj-set-ignore-time-stop kobj #t)\n");
1535         }
1536
1537         // Set the submerged flag
1538         if (submerged) {
1539                 save->write(save, "(kern-obj-set-submerged kobj #t)\n");
1540         }
1541
1542         // Close the 'let' block, returning kobj as the last thing evaluated.
1543         save->exit(save, "kobj)\n");
1544
1545 }
1546
1547
1548 #define VALID_HOOK_ID(id) ((id) >= 0 && (id) < OBJ_NUM_HOOKS)
1549
1550 void Object::hookForEach(int hook_id, 
1551                          int (*cb)(struct hook_entry *entry, void *data),
1552                          void *data)
1553 {
1554         struct list *elem;
1555         struct hook_list *hl;
1556         int locked;
1557
1558         //dbg("hookForEach entry\n");
1559
1560         assert(VALID_HOOK_ID(hook_id));
1561         hl = &hooks[hook_id];
1562
1563         // Lock the hook list to prevent any removals or entry deletions while
1564         // we're running it.
1565         locked = hook_list_trylock(hl);
1566
1567         elem = hook_list_first(hl);
1568         while (elem != hook_list_end(hl)) {
1569         
1570                 hook_entry_t *entry;
1571
1572                 entry = outcast(elem, hook_entry_t, list);
1573                 elem = elem->next;
1574
1575                 // Check if the entry is invalid. Invalid entries are entries
1576                 // that somebody tried to remove while we had the hook list
1577                 // locked. Since we have the lock, we can remove/delete them
1578                 // now.
1579                 if (hook_entry_is_invalid(entry)) {
1580                         if (locked) {
1581                                 //dbg("hookForEach: delete %p\n", entry);
1582                                 if (entry->effect->rm)
1583                                         closure_exec(entry->effect->rm, "lp", 
1584                                                      hook_entry_gob(entry), 
1585                                                      this);
1586                                 list_remove(&entry->list);
1587                                 hook_entry_del(entry);
1588                         }
1589                         continue;
1590                 }
1591
1592                 // Invoke the callback on the hook entry... this is the part
1593                 // that does anything interesting. If it returns non-zero then
1594                 // we skip running the rest of the hooks.
1595                 if (cb(entry, data))
1596                         break;
1597                         
1598                 if (isDestroyed())
1599                         break;
1600         }
1601
1602         hook_list_tryunlock(hl, locked);
1603         //dbg("hookForEach exit\n");
1604
1605 }
1606
1607 static int object_start_effect(struct hook_entry *entry, void *data)
1608 {
1609         if (entry->effect->restart
1610             && ! hook_entry_started(entry)) {
1611                 hook_entry_set_started(entry);
1612                 closure_exec(entry->effect->restart, "lp", hook_entry_gob(entry),
1613                              data);
1614         }
1615         return 0;
1616 }
1617
1618 void Object::start(void)
1619 {
1620         int i;
1621
1622         // Don't start effects multiple times.
1623         if (started) {
1624                 return;
1625         }
1626         started = true;
1627
1628         forceEffect = true;
1629         for (i = 0; i < OBJ_NUM_HOOKS; i++) {
1630                 hookForEach(i, object_start_effect, this);
1631         }
1632         forceEffect = false;
1633 }
1634
1635 struct add_hook_hook_data {
1636         struct effect *effect;
1637         char reject : 1;
1638 };
1639
1640 int object_run_add_hook_hook(hook_entry_t *entry, void *data)
1641 {
1642         struct add_hook_hook_data *context;
1643         context = (struct add_hook_hook_data *)data;
1644         if (entry->effect->exec &&
1645             closure_exec(entry->effect->exec, "lp", hook_entry_gob(entry),
1646                          context->effect))
1647                 context->reject = 1;
1648         return context->reject;
1649 }
1650
1651 int object_find_effect(hook_entry_t *entry, void *data)
1652 {
1653         struct add_hook_hook_data *context;
1654         context = (struct add_hook_hook_data *)data;
1655         if (entry->effect == context->effect)
1656                 context->reject = 1;
1657         return context->reject;
1658 }
1659
1660 bool Object::addEffect(struct effect *effect, struct gob *gob)
1661 {
1662         hook_entry_t *entry;
1663         struct add_hook_hook_data data;
1664         int hook_id = effect->hook_id;
1665         
1666         assert(VALID_HOOK_ID(effect->hook_id));
1667
1668         // Hack: NPC's don't go through a keystroke handler. For these,
1669         // substitute the start-of-turn-hook for the keystroke-hook.
1670         if (effect->hook_id == OBJ_HOOK_KEYSTROKE &&
1671             ! isPlayerControlled())
1672                 hook_id = OBJ_HOOK_START_OF_TURN;
1673
1674         // Use the same data structure to search for the effect and to check
1675         // for countereffects.
1676         data.effect = effect;
1677         data.reject = 0;
1678
1679         // For non-cumulative effects Check if the effect is already applied.
1680         if (! effect->cumulative) {
1681                 hookForEach(hook_id, object_find_effect, &data);
1682                 if (data.reject)
1683                         return false;
1684         }
1685
1686         // If we're starting up the object then "force" effects to be applied,
1687         // without checking for immunities. This works around the
1688         // script-kernel-script recursion that will otherwise occur, and which
1689         // will make summoning creatures with native effects not work from the
1690         // script.
1691         if (! forceEffect) {
1692
1693                 // Run the add-hook entries to see if any of these will block
1694                 // this new entry from being added. This is how immunities are
1695                 // implemented, BTW.
1696                 hookForEach(OBJ_HOOK_ADD_HOOK, object_run_add_hook_hook, 
1697                             &data);
1698
1699                 if (data.reject) {
1700                         return false;
1701                 }
1702         }
1703
1704         // Run the "apply" procedure of the effect if it has one.
1705         if (effect->apply) {
1706                 closure_exec(effect->apply, "lp", gob? gob->p : NULL, this);
1707         }
1708
1709         entry = hook_entry_new(effect, gob);
1710         hook_entry_set_started(entry);
1711
1712         // Roll to see if the character detects the effect (it won't show up in
1713         // stats if not)
1714         if (dice_roll("1d20") > effect->detect_dc) {
1715                 hook_entry_detect(entry);
1716         }
1717
1718         hook_list_add(&hooks[hook_id], &entry->list);
1719
1720         // gmcnutt: I saw a crash on reload because we ran through this code
1721         // before the player party was created in the new session, and the
1722         // status window tried to access it because of this next call. I don't
1723         // think we need to be updating status for every object, anyway, only
1724         // party members.
1725         if (isPlayerControlled()) {
1726                 statusRepaint();
1727         }
1728
1729         return true;
1730 }
1731
1732 void Object::restoreEffect(struct effect *effect, struct gob *gob, int flags, 
1733                            clock_alarm_t expiration)
1734 {
1735         hook_entry_t *entry;
1736
1737         assert(VALID_HOOK_ID(effect->hook_id));
1738
1739         // Note: do NOT run the "apply" procedure of the effect here (already
1740         // tried this - causes script recursion while loading which as we know
1741         // aborts the load prematurely). Instead the "restart" procedure will
1742         // be run as part of our start() method, called on all objects near the
1743         // end of session_load().
1744
1745         entry = hook_entry_new(effect, gob);
1746         entry->flags = flags;
1747         entry->expiration = expiration;
1748         hook_list_add(&hooks[effect->hook_id], &entry->list);
1749
1750 }
1751
1752 struct object_run_hook_entry_data {
1753         Object *obj;
1754         const char *fmt;
1755         va_list args;
1756 };
1757
1758 static int object_run_hook_entry(struct hook_entry *entry, void *data)
1759 {
1760         struct object_run_hook_entry_data *info;
1761         info = (struct object_run_hook_entry_data *)data;
1762
1763         if (entry->effect->exec)
1764                 return closure_execlpv(entry->effect->exec, 
1765                                        hook_entry_gob(entry),
1766                                        info->obj,
1767                                        info->fmt, 
1768                                        info->args);
1769         return 0;
1770 }
1771
1772 void Object::runHook(int hook_id, const char *fmt, ...)
1773 {
1774         struct object_run_hook_entry_data data;
1775
1776         data.obj = this;
1777         data.fmt = fmt;
1778         va_start(data.args, fmt);
1779         hookForEach(hook_id, object_run_hook_entry, &data);
1780         va_end(data.args);
1781 }
1782
1783 void Object::saveHooks(struct save *save)
1784 {
1785         int i;
1786
1787         save->write(save, ";; hooks\n");
1788         save->enter(save, "(list\n");
1789         for (i = 0; i < OBJ_NUM_HOOKS; i++) {
1790                 struct list *elem;
1791                 hook_list_for_each(&hooks[i], elem) {
1792                         hook_entry_t *entry;
1793                         entry = outcast(elem, hook_entry_t, list);
1794                         hook_entry_save(entry, save);
1795                 }
1796         }
1797         save->exit(save, ")\n");
1798 }
1799
1800 bool Object::removeEffect(struct effect *effect)
1801 {
1802         struct list *elem;
1803         struct hook_list *hl;
1804         int hook_id = effect->hook_id;
1805
1806         assert(VALID_HOOK_ID(effect->hook_id));
1807         
1808         // Hack: NPC's don't go through a keystroke handler. For these,
1809         // substitute the start-of-turn-hook for the keystroke-hook.
1810         if (effect->hook_id == OBJ_HOOK_KEYSTROKE &&
1811             ! isPlayerControlled())
1812                 hook_id = OBJ_HOOK_START_OF_TURN;
1813
1814         hl = &hooks[hook_id];
1815
1816         elem = hook_list_first(hl);
1817         while (elem != hook_list_end(hl)) {
1818                 hook_entry_t *entry;
1819
1820                 entry = outcast(elem, hook_entry_t, list);
1821                 elem = elem->next;
1822
1823                 if (hook_entry_is_invalid(entry))
1824                         // Already pending removal.
1825                         continue;
1826
1827                 if (effect == entry->effect) {
1828
1829                         // If the hook list is locked we can't remove/delete
1830                         // the entry, but if we mark it invalid then the
1831                         // runHooks() method will eventually clean it up.
1832                         if (hook_list_locked(hl)) {
1833                                 hook_entry_invalidate(entry);
1834                         } else {
1835                                 if (entry->effect->rm)
1836                                         closure_exec(entry->effect->rm, "lp", 
1837                                                      hook_entry_gob(entry), 
1838                                                      this);
1839                                 list_remove(&entry->list);
1840                                 hook_entry_del(entry);
1841                         }
1842
1843                         statusRepaint();
1844
1845                         return true;
1846                 }
1847         }
1848
1849         return false;
1850 }
1851
1852 int Object::getCount()
1853 {
1854         return count;
1855 }
1856
1857 void Object::setCount(int c)
1858 {
1859         count = c;
1860 }
1861
1862 bool ObjectType::isUsable()
1863 {
1864         return (gifc_cap & GIFC_CAN_USE);
1865 }
1866
1867 bool ObjectType::isReadyable()
1868 {
1869         return isType(ARMS_TYPE_ID);
1870 }
1871
1872 bool ObjectType::isMixable()
1873 {
1874         return (gifc_cap & GIFC_CAN_MIX);
1875 }
1876
1877 bool ObjectType::isCastable()
1878 {
1879         return (gifc_cap & GIFC_CAN_CAST);
1880 }
1881
1882 bool ObjectType::canExec()
1883 {
1884         return (gifc_cap & GIFC_CAN_EXEC);
1885 }
1886
1887 bool ObjectType::canStep()
1888 {
1889         return (gifc_cap & GIFC_CAN_STEP);
1890 }
1891
1892 bool ObjectType::canSense()
1893 {
1894         return (gifc_cap & GIFC_CAN_SENSE);
1895 }
1896
1897 bool ObjectType::canXamine()
1898 {
1899         return (gifc_cap & GIFC_CAN_XAMINE);
1900 }
1901
1902 bool ObjectType::canAttack()
1903 {
1904         return (gifc_cap & GIFC_CAN_ATTACK);
1905 }
1906
1907 bool ObjectType::canEnter()
1908 {
1909         return (gifc_cap & GIFC_CAN_ENTER);
1910 }
1911
1912 bool ObjectType::canGet()
1913 {
1914         // Hack: arms types not converted over to use gifc's yet
1915         return (gifc_cap & GIFC_CAN_GET);
1916 }
1917
1918 bool ObjectType::canBuy()
1919 {
1920         return (gifc_cap & GIFC_CAN_BUY);
1921 }
1922
1923 bool ObjectType::canSearch()
1924 {
1925         return (gifc_cap & GIFC_CAN_SEARCH);
1926 }
1927
1928 bool ObjectType::canOpen()
1929 {
1930         return (gifc_cap & GIFC_CAN_OPEN);
1931 }
1932
1933 bool ObjectType::canBump()
1934 {
1935         return (gifc_cap & GIFC_CAN_BUMP);
1936 }
1937
1938 int ObjectType::open(Object *obj, Object *opener)
1939 {
1940         return closure_exec(gifc, "ypp", "open", obj, opener);
1941 }
1942
1943 int ObjectType::bump(Object *obj, Object *bumper)
1944 {
1945         return closure_exec(gifc, "ypp", "bump", obj, bumper);
1946 }
1947
1948 bool ObjectType::canHandle()
1949 {
1950         return (gifc_cap & GIFC_CAN_HANDLE);
1951 }
1952
1953 int ObjectType::handle(Object *obj, Object *handler)
1954 {
1955         return closure_exec(gifc, "ypp", "handle", obj, handler);
1956 }
1957
1958 bool ObjectType::canHitLocation()
1959 {
1960         return (gifc_cap & GIFC_CAN_HIT_LOCATION);
1961 }
1962
1963 int ObjectType::hitLocation(Object *obj, Object *attacker, Object *target, struct place *place, int x, int y, int dam)
1964 {
1965         return closure_exec(gifc, "yppppddd", "hit-loc", obj, attacker, target, place, x, y, dam);
1966 }
1967
1968 int ObjectType::step(Object *obj, Object *stepper)
1969 {
1970         return closure_exec(gifc, "ypp", "step", obj, stepper);
1971 }
1972
1973 int ObjectType::sense(Object *obj, Object *stepper)
1974 {
1975         return closure_exec(gifc, "ypp", "sense", obj, stepper);
1976 }
1977
1978 int ObjectType::xamine(Object *obj, Object *xaminer)
1979 {
1980         return closure_exec(gifc, "ypp", "xamine", obj, xaminer);
1981 }
1982
1983 int ObjectType::attack(Object *obj, Object *stepper)
1984 {
1985         return closure_exec(gifc, "ypp", "attack", obj, stepper);
1986 }
1987
1988 bool ObjectType::canOnAttack()
1989 {
1990         return (gifc_cap & GIFC_CAN_ON_ATTACK);
1991 }
1992
1993 int ObjectType::onAttack(Object *obj, Object *stepper)
1994 {
1995         return closure_exec(gifc, "yp", "on-attack", stepper);
1996 }
1997
1998 int ObjectType::enter(Object *obj, Object *stepper)
1999 {
2000         return closure_exec(gifc, "ypp", "enter", obj, stepper);
2001 }
2002
2003 int ObjectType::exec(Object *obj)
2004 {
2005         return closure_exec(gifc, "yp", "exec", obj);
2006 }
2007
2008 int ObjectType::use(Object *user)
2009 {
2010         return closure_exec(gifc, "ypp", "use", this, user);
2011 }
2012
2013 int ObjectType::cast(Object *caster)
2014 {
2015         return closure_exec(gifc, "yp", "cast", caster);
2016 }
2017
2018 int ObjectType::get(Object *obj, Object *getter)
2019 {
2020         return closure_exec(gifc, "ypp", "get", obj, getter);
2021 }
2022
2023 int ObjectType::buy(Object *buyer, int q)
2024 {
2025         return closure_exec(gifc, "ypd", "buy", buyer, q);
2026 }
2027
2028 int ObjectType::search(Object *obj, Object *searcher)
2029 {
2030         return closure_exec(gifc, "ypp", "search", obj, searcher);
2031 }
2032
2033 closure_t *ObjectType::getGifc()
2034 {
2035         return gifc;
2036 }
2037
2038 void ObjectType::setGifc(closure_t *g, int cap)
2039 {
2040         // out with the old
2041         if (gifc) {
2042                 closure_unref(gifc);
2043                 gifc = NULL;
2044                 gifc_cap = 0;
2045         }
2046
2047         // in with the new
2048         if (g) {
2049                 closure_ref(g);
2050                 gifc = g;
2051                 gifc_cap = cap;
2052         }
2053 }
2054
2055 void ObjectType::setGob(struct gob *g)
2056 {
2057         if (gob) {
2058                 gob_unref(gob);
2059                 gob = 0;
2060         }
2061
2062         if (g) {
2063                 gob = g;
2064                 gob_ref(g);
2065         }
2066 }
2067
2068 struct gob * ObjectType::getGob()
2069 {
2070         return gob;
2071 }
2072
2073 bool ObjectType::hasDescribeHook()
2074 {
2075         return (gifc_cap & GIFC_CAN_DESCRIBE);
2076 }
2077
2078 void ObjectType::runDescribeHook(Object *obj)
2079 {
2080         closure_exec(gifc, "ypd", "describe", obj, obj->getCount());
2081 }
2082
2083 bool ObjectType::isQuestItem()
2084 {
2085         return questItemFlag;
2086 }
2087
2088 void ObjectType::setQuestItemFlag(bool val)
2089 {
2090         questItemFlag = val;
2091 }
2092
2093 struct mmode *ObjectType::getMovementMode()
2094 {
2095    return movementMode;
2096 }
2097
2098 void ObjectType::setMovementMode(struct mmode *mmode)
2099 {
2100         movementMode = mmode;
2101 }
2102
2103 /////////////////////////////////////////////////////////////////////////////////////////////
2104 // Object
2105
2106 bool Object::add(ObjectType *type, int amount)
2107 {
2108         return false; // subclasses will overload
2109 }
2110
2111 bool Object::takeOut(ObjectType *type, int amount)
2112 {
2113         return false; // subclasses will overload
2114 }
2115
2116 bool Object::addFood(int amount)
2117 {
2118         return false; // subclasses will overload
2119 }
2120
2121 bool Object::addGold(int amount)
2122 {
2123         return false; // subclasses will overload
2124 }
2125
2126 void Object::setGob(struct gob *g)
2127 {
2128         gob = g;
2129 }
2130
2131 struct gob * Object::getGob()
2132 {
2133         return gob;
2134 }
2135
2136 void Object::setSprite(struct sprite *sprite)
2137 {
2138         current_sprite = sprite;
2139 }
2140
2141 void Object::step(Object *stepper)
2142 {        
2143         if (! getObjectType() ||
2144             ! getObjectType()->canStep())
2145                 return;
2146
2147         getObjectType()->step(this, stepper);
2148 }
2149
2150 void Object::sense(Object *stepper)
2151 {        
2152         if (! getObjectType() ||
2153             ! getObjectType()->canSense())
2154                 return;
2155
2156         getObjectType()->sense(this, stepper);
2157 }
2158
2159 void Object::attack(Object *stepper)
2160 {        
2161         if (! getObjectType() ||
2162             ! getObjectType()->canAttack())
2163                 return;
2164
2165         getObjectType()->attack(this, stepper);
2166 }
2167
2168 void Object::onAttack(Object *user)
2169 {        
2170         if (! getObjectType() ||
2171             ! getObjectType()->canOnAttack())
2172                 return;
2173
2174         getObjectType()->onAttack(this, user);
2175 }
2176
2177 struct conv *Object::getConversation()
2178 {
2179         return conv;
2180 }
2181
2182 void Object::setConversation(struct conv *val)
2183 {
2184         // out with the old
2185         if (conv) {
2186                 conv_unref(conv);
2187                 conv = NULL;
2188         }
2189
2190         // in with the new
2191         if (val) {
2192                 conv_ref(val);
2193                 conv = val;
2194         }
2195 }
2196
2197 bool Object::canEnter()
2198 {
2199         return (getObjectType() && getObjectType()->canEnter());
2200 }
2201
2202 void Object::enter(Object *enterer)
2203 {
2204         getObjectType()->enter(this, enterer);
2205 }
2206
2207 bool Object::canStep()
2208 {
2209         return (getObjectType() && getObjectType()->canStep());
2210 }
2211
2212 bool Object::canSense()
2213 {
2214         return (getObjectType() && getObjectType()->canSense());
2215 }
2216
2217 void Object::setLight(int val)
2218 {
2219         light = val;
2220         if (light < 0)
2221                 light = 0;
2222 }
2223
2224 bool Object::isTemporary()
2225 {
2226         return temporary;
2227 }
2228
2229 void Object::setTemporary(bool val)
2230 {
2231         temporary = val;
2232 }
2233
2234 struct mmode *Object::getMovementMode()
2235 {
2236         return getObjectType()->getMovementMode();
2237 }
2238
2239 void Object::setMovementMode(struct mmode *mmode)
2240 {
2241         // nop
2242 }
2243
2244 Object *Object::getSpeaker()
2245 {
2246         return this;
2247 }
2248
2249 void Object::resetActionPoints()
2250 {
2251         setActionPoints(0);
2252 }
2253
2254 void Object::setActionPoints(int amount)
2255 {
2256         action_points = amount;
2257         if (isPlayerControlled()) {
2258                 statusRepaint();
2259         }
2260 }
2261
2262 void obj_inc_ref(Object *obj)
2263 {
2264         obj->refcount++;
2265 #if 0
2266         if (obj->getName() && ! strcmp(obj->getName(), "player party"
2267                                        /*"The Wanderer"*/)) {
2268                 printf("obj_inc_ref: %d\n", obj->refcount);
2269         }
2270 #endif
2271 }
2272
2273 void obj_dec_ref(Object *obj)
2274 {
2275         assert((obj)->refcount >= 0);
2276         (obj)->refcount--;
2277 #if 0
2278         if (obj->getName() && ! strcmp(obj->getName(), "player party"
2279                                        /*"The Wanderer"*/)) {
2280                 printf("obj_dec_ref: %d\n", obj->refcount);
2281         }
2282 #endif
2283         if (! obj->refcount)
2284                 delete obj;
2285 }
2286
2287 int Object::getTTL(void) { return ttl; }
2288
2289 bool Object::surreptitiouslyRemove()
2290 {
2291         // crasher fix: check for player party; this may be called on boot if
2292         // the load file has (kern-obj-set-ttl kobj 0). That scenario happens
2293         // when an object's ttl is expired but the player is still in LOS, and
2294         // then the player saves the game.
2295
2296         if (!isOnMap()) {
2297                 return false;
2298         }
2299
2300         if (player_party
2301             && getPlace()==player_party->getPlace()
2302             && (place_flying_distance(player_party->getPlace(),
2303                                       player_party->getX(),
2304                                       player_party->getY(),
2305                                       getX(),
2306                                       getY())
2307                 < player_party->getVisionRadius())
2308             && place_in_los(player_party->getPlace(),
2309                             player_party->getX(),
2310                             player_party->getY(),
2311                             getPlace(),
2312                             getX(),
2313                             getY())) {
2314                 return false;
2315         }
2316
2317         remove();
2318         return true;
2319 }
2320
2321 void Object::setTTL(class Object *obj, int val)
2322 {
2323         obj->ttl = val;
2324         if (!obj->ttl) {
2325                 obj->surreptitiouslyRemove(); // may destroy obj!
2326         }
2327 }
2328
2329 void Object::decrementTTL(class Object *obj)
2330 {
2331         // don't decrement if everlasting
2332         if (-1==obj->getTTL())
2333                 return;
2334
2335         if (0==obj->getTTL()) {
2336                 obj->surreptitiouslyRemove(); // may destroy obj!
2337                 return;
2338         }
2339
2340         obj->setTTL(obj, obj->getTTL() - 1); // may destroy obj!
2341 }
2342
2343 bool Object::isStationary()
2344 {
2345         return false;
2346 }
2347
2348 bool Object::setFacing(int val)
2349 {
2350         if (!sprite_can_face(getSprite(), val))
2351                 return false;
2352         facing = val;
2353         return true;
2354 }
2355
2356 int Object::getFacing()
2357 {
2358         return facing;
2359 }
2360
2361 bool Object::ignoresTimeStop()
2362 {
2363         return ignoreTimeStop;
2364 }
2365
2366 void Object::setIgnoreTimeStop(bool val)
2367 {
2368         ignoreTimeStop = val;
2369 }
2370
2371 struct sprite *Object::getPortrait()
2372
2373         return portrait; 
2374 }
2375
2376 void Object::setPortrait(struct sprite *sprite) 
2377
2378         portrait = sprite; 
2379 }