OSDN Git Service

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