2 // nazghul - an old-school RPG engine
3 // Copyright (C) 2002, 2003 Gordon McNutt
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)
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
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
20 // gmcnutt@users.sourceforge.net
27 #include "character.h"
40 #include "combat.h" // for combat_get_state()
48 /*****************************************************************************/
51 // These GIFC_CAN_* bits need to match the script:
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)
72 ObjectType::ObjectType()
77 ObjectType::ObjectType(const char *tag, const char *sname, struct sprite *sprite_,
79 : sprite(sprite_), layer(layer_), speed(0), required_action_points(0),
80 max_hp(0), gifc(NULL), gifc_cap(0), gob(NULL), pluralName(NULL)
82 this->tag = strdup(tag);
86 this->name = strdup(sname);
93 ObjectType::~ObjectType()
107 void ObjectType::setPluralName(char *val)
112 pluralName=strdup(val);
117 char *ObjectType::getPluralName()
122 bool ObjectType::init(char *tag, char *name, enum layer layer,
123 struct sprite *sprite)
125 this->tag = strdup(tag);
126 this->name = strdup(name);
127 this->sprite = sprite;
129 return (this->tag != 0 && this->name != 0);
132 class Object *ObjectType::createInstance()
134 return new Object(this);
137 void ObjectType::setSprite(struct sprite *sprite)
139 this->sprite = sprite;
142 static int endsWith(const char *word, const char *end)
144 int wlen=strlen(word)-1;
145 int elen=strlen(end)-1;
151 if (word[wlen--]!=end[elen--])
158 void ObjectType::describeType(int count)
161 log_continue("%s", getName());
163 log_continue("%s(%d)", getName(), count);
167 void ObjectType::describe(Object *obj)
169 if (hasDescribeHook()) {
170 runDescribeHook(obj);
174 describeType(obj->getCount());
177 bool ObjectType::isType(int classID)
179 return (classID == OBJECT_TYPE_ID);
182 int ObjectType::getType()
184 return OBJECT_TYPE_ID;
187 const char *ObjectType::getTag()
192 const char *ObjectType::getName()
197 struct sprite *ObjectType::getSprite()
202 enum layer ObjectType::getLayer()
207 bool ObjectType::isVisible()
212 int ObjectType::getSpeed()
217 int ObjectType::getRequiredActionPoints()
219 return required_action_points;
222 int ObjectType::getMaxHp()
227 //////////////////////////////////////////////////////////////////////////////
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)
244 //////////////////////////////////////////////////////////////////////////////
248 //////////////////////////////////////////////////////////////////////////////
250 #define HEF_INVALID (1<<0)
251 #define HEF_DETECTED (1<<1)
252 #define HEF_STARTED (1<<2)
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)
261 hook_entry_t *hook_entry_new(struct effect *effect, struct gob *gob)
265 entry = (hook_entry_t*)calloc(1, sizeof(*entry));
267 list_init(&entry->list);
268 entry->effect = effect;
272 if (effect_will_expire(effect)) {
273 clock_alarm_set(&entry->expiration, effect->duration);
276 //dbg("hook_entry_new: %p\n", entry);
280 void hook_entry_del(hook_entry_t *entry)
282 //dbg("hook_entry_del: %p\n", entry);
284 gob_unref(entry->gob);
288 void hook_entry_save(hook_entry_t *entry, struct save *save)
290 // Note: saved effects are loaded in kern.c:kern_mk_char() & attached
291 // via restoreEffect().
293 save->enter(save, "(list\n");
294 save->write(save, "%s\n", entry->effect->tag);
296 gob_save(entry->gob, save);
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");
304 static inline int hook_entry_is_invalid(hook_entry_t *entry)
306 return ((entry->flags & HEF_INVALID) ||
307 (effect_will_expire(entry->effect) &&
308 clock_alarm_is_expired(&entry->expiration)));
311 //////////////////////////////////////////////////////////////////////////////
313 // Object class methods
315 //////////////////////////////////////////////////////////////////////////////
317 void Object::init(int x, int y, struct place *place, class ObjectType * type)
326 void Object::init(class ObjectType * type)
331 bool Object::tryToRelocateToNewPlace(struct place *newplace,
333 struct closure *cutscene)
336 place_remove_object(getPlace(), this);
341 closure_exec(cutscene, NULL);
348 place_add_object(getPlace(), this);
355 // Trigger the topmost sense mechanism on a tile, using this as the subject
357 // @param tilePlace place the tile is in
358 // @param tileX coord of tile
359 // @param tileY coord of tile
361 void Object::triggerSense(struct place *tilePlace, int tileX, int tileY)
363 Object *mech = place_get_object(tilePlace, tileX, tileY, mech_layer);
366 && mech->getObjectType()->canSense()) {
367 mech->getObjectType()->sense(mech, this);
372 // Trigger the topmost step mechanism on a tile, using this as the subject
374 // @param tilePlace place the tile is in
375 // @param tileX coord of tile
376 // @param tileY coord of tile
378 void Object::triggerStep(struct place *tilePlace, int tileX, int tileY)
380 Object *mech = place_get_object(tilePlace, tileX, tileY, mech_layer);
383 && mech->getObjectType()->canStep()) {
384 mech->getObjectType()->step(mech, this);
389 // Run the step and sense triggers on tile entry using this as the subject,
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
397 void Object::triggerOnTileEntry(struct place *tilePlace, int tileX, int tileY,
400 bool sense = !(flags & REL_NOSENSE);
401 bool step = !(flags & REL_NOSTEP);
404 triggerSense(tilePlace, tileX, tileY);
408 triggerStep(tilePlace, tileX, tileY);
413 // Run the sense trigger on tile exit using this as the subject, flags
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
421 void Object::triggerOnTileExit(struct place *tilePlace, int tileX, int tileY,
424 bool sense = ! (flags & REL_NOSENSE);
427 triggerSense(tilePlace, tileX, tileY);
431 void Object::relocate(struct place *newplace, int newx, int newy, int flags,
432 struct closure *place_switch_hook)
436 struct place *foc_place;
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
448 if (newplace == getPlace()) {
450 // Moving from one tile to another in the same place.
451 if (place_switch_hook) {
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.
461 place_remove_object(getPlace(), this);
463 closure_exec(place_switch_hook, NULL);
468 place_add_object(newplace, this);
475 place_move_object(newplace, this, newx, newy);
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)) {
490 // This object may no longer be on a map as a result of
491 // the above call. If so then finish processing.
494 // Run the exit triggers before returning
496 triggerOnTileExit(oldPlace, oldX, oldY, flags);
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.
509 setX(place_wrap_x(newplace, newx));
510 setY(place_wrap_y(newplace, newy));
511 place_add_object(getPlace(), this);
518 // Run the exit triggers.
520 triggerOnTileExit(oldPlace, oldX, oldY, flags);
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.
534 // Attenuate movement sound based on distance from the camera's focal
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,
542 volume = (volume * (20 - distance))/20;
544 sound_play(get_movement_sound(), volume);
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());
555 // Run the entry triggers.
556 triggerOnTileEntry(getPlace(), getX(), getY(), flags);
559 void Object::setOnMap(bool val)
564 void Object::remove()
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
575 place_remove_object(getPlace(), this);
577 // Run the exit triggers.
580 triggerOnTileExit(oldPlace, oldX, oldY, 0);
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.
596 void Object::paint(int sx, int sy)
598 struct sprite *sprite = getSprite();
600 int origFacing = sprite_get_facing(sprite);
601 sprite_set_facing(sprite, facing);
603 && isPlayerControlled()) {
605 sprite_frame + Session->time_stop_ticks,
608 sprite_paint(sprite, sprite_frame, sx, sy);
610 sprite_set_facing(sprite, origFacing);
614 void Object::describe()
616 assert(getObjectType()); // else implement this method in subclass
617 getObjectType()->describe(this);
619 log_continue("(ÉԲĻë)");
622 log_continue("(±£ÊÃ)");
626 void Object::examine()
628 assert(getObjectType()); // else implement this method in subclass
631 //todo: dont have examiner to pass in to ifc
632 if (getObjectType()->canXamine()) {
634 getObjectType()->xamine(this, this);
639 sound_t *Object::get_movement_sound()
644 class Object *Object::clone()
646 // gmcnutt: added support for an optional quantity field for placed
651 obj = getObjectType()->createInstance();
652 obj->setPlace(getPlace());
656 // FIXME: should assign the new object a unique script tag
661 //////////////////////////////////////////////////
663 bool Object::isType(int classID)
665 return (classID == OBJECT_ID);
667 int Object::getType()
678 Object::Object(class ObjectType * type)
691 for (i = 0; i < OBJ_NUM_HOOKS; i++) {
692 hook_list_init(&hooks[i]);
701 action_points = 0; /* FIXME: assumes no debt */
702 control_mode = CONTROL_MODE_AUTO;
703 camera_attached = false;
713 current_sprite = NULL;
718 pclass = PCLASS_NONE;
719 ttl = -1; // everlasting by default
721 facing = SPRITE_DEF_FACING;
722 ignoreTimeStop = false;
726 if (getObjectType() && ! getObjectType()->isVisible())
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;
742 dbg("refcount=%d\n", refcount);
746 //dbg("destroying %d %08lx %s\n", refcount, this, getName());
749 session_rm(Session, handle);
762 mapDestroyView(getView());
770 // For each type of hook...
771 for (i = 0; i < OBJ_NUM_HOOKS; i++) {
773 // Shouldn't be destroying the object while one of its hook
775 assert(! hook_list_locked(&hooks[i]));
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.
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.
791 struct hook_list *hl;
793 lptr = hook_list_first(hl);
794 while (lptr != hook_list_end(hl)) {
795 hook_entry_t *he = outcast(lptr, hook_entry_t, list);
797 list_remove(&he->list);
813 struct place *Object::getPlace()
818 struct sprite *Object::getSprite()
821 return current_sprite;
823 return type->getSprite();
827 bool Object::isSelected()
832 enum layer Object::getLayer(void)
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
842 return getObjectType()->getLayer();
847 const char *Object::getName(void)
850 return type->getName();
854 class ObjectType *Object::getObjectType()
859 bool Object::isDestroyed()
865 void Object::setX(int x)
870 void Object::setY(int y)
875 void Object::changeX(int dx)
880 void Object::changeY(int dy)
885 void Object::setPlace(struct place *place)
890 void Object::select(bool val)
892 if (val == isSelected())
896 mapSetSelected(this);
897 } else if (isSelected()) {
898 mapSetSelected(NULL);
902 mapUpdateTile(getPlace(), getX(), getY());
906 void Object::destroy()
914 int Object::getLight()
922 if (getObjectType()->canExec())
923 getObjectType()->exec(this);
924 endTurn(); // warn: might destroy this!
925 Object::decrementTTL(this); // might destroy the object!
928 void Object::synchronize()
932 bool Object::isVisible()
934 //return getObjectType()->isVisible();
938 void Object::setVisible(bool val)
944 if (visible < 0 && getName()) {
945 printf("%s: %d\n", getName(), visible);
950 bool Object::isSubmerged()
955 void Object::setSubmerged(bool val)
960 bool Object::isShaded()
962 return isSubmerged();
965 void Object::setOpacity(bool val)
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);
975 bool Object::isOpaque()
980 bool Object::joinPlayer()
985 int Object::getActivity()
990 int Object::getActionPointsPerTurn()
992 int baseAP = (int)(getSpeed() * session_get_time_accel());
994 // If 'Quicken' is in effect then give player-controlled objects bonus
995 // action points per turn.
996 if (Quicken > 0 && isPlayerControlled()) {
1000 // If 'TimeStop' is in effect then give action points ONLY to
1001 // player-controlled objects.
1002 if (TimeStop && ! isPlayerControlled()) {
1009 void Object::applyEffect(closure_t *effect)
1011 closure_exec(effect, "p", this);
1018 void Object::sleep()
1022 sound_t *Object::getDamageSound()
1027 void Object::damage(int amount)
1029 // Paint the red "*" damage symbol over the character's icon on the map
1031 mapPaintDamage(getX(), getY());
1032 sound_play(getDamageSound(), SOUND_MAX_VOLUME);
1035 runHook(OBJ_HOOK_DAMAGE, 0);
1038 void Object::inflictDamage(int amount, class Character *attacker)
1043 int Object::getActionPoints()
1045 return action_points;
1048 void Object::decActionPoints(int points)
1050 setActionPoints(action_points - points);
1053 void Object::endTurn()
1055 if (action_points > 0)
1061 void Object::startTurn()
1063 setActionPoints(action_points + getActionPointsPerTurn());
1064 runHook(OBJ_HOOK_START_OF_TURN, 0);
1067 int Object::getSpeed()
1069 return getObjectType()->getSpeed();
1072 int Object::getRequiredActionPoints()
1074 return getObjectType()->getRequiredActionPoints();
1077 bool Object::isOnMap()
1082 bool Object::isDead()
1087 enum control_mode Object::getControlMode()
1089 return control_mode;
1092 void Object::setControlMode(enum control_mode mode)
1094 control_mode = mode;
1097 void Object::attachCamera(bool val)
1099 if (camera_attached == val)
1102 camera_attached = val;
1105 mapAttachCamera(this);
1107 mapDetachCamera(this);
1110 bool Object::isCameraAttached()
1112 return camera_attached;
1115 bool Object::isTurnEnded()
1117 return (getActionPoints() <= 0 ||
1122 bool Object::isPlayerPartyMember()
1127 bool Object::addToInventory(class Object *object)
1132 bool Object::hasInInventory(class ObjectType *object)
1137 void Object::heal(int amount)
1139 amount = min(amount, getMaxHp() - hp);
1143 bool Object::isPlayerControlled()
1148 int Object::getMaxHp()
1150 return getObjectType()->getMaxHp();
1158 bool Object::isCompanionOf(class Object *other)
1163 int Object::getPclass()
1168 void Object::setPclass(int val)
1173 int Object::getMovementCost(int pclass)
1175 if (pclass == PCLASS_NONE)
1178 struct mmode *mmode = getMovementMode();
1180 return PTABLE_IMPASSABLE;
1182 return ptable_get(session_ptable(), mmode->index, pclass);
1185 bool Object::isPassable(int pclass)
1187 return (getMovementCost(pclass) != PTABLE_IMPASSABLE);
1190 bool Object::putOnMap(struct place *new_place, int new_x, int new_y, int r,
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:
1197 // o (x, y) is with radius r of (new_x, new_y)
1199 // o The object can find a path from (x, y) to (new_x, new_y)
1201 // If no such (x, y) exists then return false without placing the
1203 // --------------------------------------------------------------------
1217 int x_offsets[] = { -1, 1, 0, 0 };
1218 int y_offsets[] = { 0, 0, -1, 1 };
1220 printf("Putting %s near (%d %d)\n", getName(), new_x, new_y);
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 // --------------------------------------------------------------------
1229 rx = new_x - (r / 2);
1230 ry = new_y - (r / 2);
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 // --------------------------------------------------------------------
1243 visited = (char*)calloc(sizeof(char), q_size);
1244 if (NULL == visited)
1247 q_x = (int*)calloc(sizeof(int), q_size);
1252 q_y = (int*)calloc(sizeof(int), q_size);
1257 queued = (char*)calloc(sizeof(char), q_size);
1261 // --------------------------------------------------------------------
1262 // Enqueue the preferred location to start the search.
1263 // --------------------------------------------------------------------
1265 #define INDEX(x,y) (((y)-ry) * r + ((x)-rx))
1269 q_x[q_tail] = new_x;
1270 q_y[q_tail] = new_y;
1271 index = INDEX(new_x, new_y);
1273 assert(index < q_size);
1277 // --------------------------------------------------------------------
1278 // Run through the search queue until it is exhausted or a safe
1279 // position has been found.
1280 // --------------------------------------------------------------------
1282 while (q_head != q_tail) {
1284 // ------------------------------------------------------------
1285 // Dequeue the next location to check.
1286 // ------------------------------------------------------------
1288 new_x = q_x[q_head];
1289 new_y = q_y[q_head];
1292 printf("Checking (%d,%d)...", new_x, new_y);
1294 // ------------------------------------------------------------
1295 // Has the location already been visited? (If not then mark it
1297 // ------------------------------------------------------------
1299 index = INDEX(new_x, new_y);
1301 assert(index < q_size);
1303 if (0 != visited[index]) {
1304 printf("already checked\n");
1309 // ------------------------------------------------------------
1310 // Is the location off the map or impassable?
1311 // ------------------------------------------------------------
1313 if (place_off_map(new_place, new_x, new_y) ||
1314 ! place_is_passable(new_place, new_x, new_y, this, flags)){
1318 // ------------------------------------------------------------
1319 // Is the location occupied or hazardous?
1320 // ------------------------------------------------------------
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))) {
1327 printf("occupied or hazardous\n");
1329 // ----------------------------------------------------
1330 // This place is not suitable, but its neighbors might
1331 // be. Put them on the queue.
1332 // ----------------------------------------------------
1334 for (i = 0; i < array_sz(x_offsets); i++) {
1340 neighbor_x = new_x + x_offsets[i];
1341 neighbor_y = new_y + y_offsets[i];
1343 // --------------------------------------------
1344 // Is the neighbor outside the search radius?
1345 // --------------------------------------------
1347 if (neighbor_x < rx ||
1349 neighbor_x >= (rx + r) ||
1350 neighbor_y >= (ry + r)) {
1354 // --------------------------------------------
1355 // Has the neighbor already been queued?
1356 // --------------------------------------------
1358 neighbor_index = INDEX(neighbor_x, neighbor_y);
1359 assert(neighbor_index >= 0);
1360 assert(neighbor_index < (q_size));
1362 if (queued[neighbor_index])
1365 // --------------------------------------------
1366 // Enqueue the neighbor
1367 // --------------------------------------------
1369 assert(q_tail < (q_size));
1371 q_x[q_tail] = neighbor_x;
1372 q_y[q_tail] = neighbor_y;
1374 queued[neighbor_index] = 1;
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.
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
1391 // ------------------------------------------------------------
1394 relocate(new_place, new_x, new_y, REL_NOSTEP, NULL);
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 // --------------------------------------------------------------------
1405 printf("NO PLACE FOUND!\n");
1420 struct mview *Object::getView()
1425 int Object::getVisionRadius()
1430 void Object::addView()
1432 if (NULL != getView()) {
1433 mapAddView(getView());
1438 void Object::rmView()
1440 if (NULL != getView()) {
1441 mapRmView(getView());
1445 void Object::updateView()
1449 if (NULL != getView()) {
1450 mapCenterView(getView(), getX(), getY());
1451 mapSetRadius(getView(), min(getVisionRadius(), MAX_VISION_RADIUS));
1456 void Object::setView(struct mview *new_view)
1461 void Object::changePlaceHook()
1474 bool Object::canWanderTo(int newx, int newy)
1478 enum MoveResult Object::move(int dx, int dy)
1480 return NotApplicable;
1483 void Object::save(struct save *save)
1485 // Create the object within a 'let' block
1486 save->enter(save, "(let ((kobj (kern-mk-obj %s %d\n",
1487 getObjectType()->getTag(),
1490 save->write(save, ")))\n");
1494 save->write(save, "(kern-tag '%s kobj)\n", tag);
1497 // Save the gob binding.
1499 save->enter(save, "(bind kobj\n");
1500 gob_save(getGob(), save);
1501 save->exit(save, ")\n");
1504 // Save time-to-live.
1505 if (getTTL() != -1) {
1506 save->write(save, "(kern-obj-set-ttl kobj %d)\n",
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");
1518 if (SPRITE_DEF_FACING != facing) {
1519 save->write(save, "(kern-obj-set-facing kobj %d)\n", facing);
1522 // Set the ignore-time-stop flag
1523 if (ignoreTimeStop) {
1524 save->write(save, "(kern-obj-set-ignore-time-stop kobj #t)\n");
1527 // Set the submerged flag
1529 save->write(save, "(kern-obj-set-submerged kobj #t)\n");
1532 // Close the 'let' block, returning kobj as the last thing evaluated.
1533 save->exit(save, "kobj)\n");
1538 #define VALID_HOOK_ID(id) ((id) >= 0 && (id) < OBJ_NUM_HOOKS)
1540 void Object::hookForEach(int hook_id,
1541 int (*cb)(struct hook_entry *entry, void *data),
1545 struct hook_list *hl;
1548 //dbg("hookForEach entry\n");
1550 assert(VALID_HOOK_ID(hook_id));
1551 hl = &hooks[hook_id];
1553 // Lock the hook list to prevent any removals or entry deletions while
1554 // we're running it.
1555 locked = hook_list_trylock(hl);
1557 elem = hook_list_first(hl);
1558 while (elem != hook_list_end(hl)) {
1560 hook_entry_t *entry;
1562 entry = outcast(elem, hook_entry_t, list);
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
1569 if (hook_entry_is_invalid(entry)) {
1571 //dbg("hookForEach: delete %p\n", entry);
1572 if (entry->effect->rm)
1573 closure_exec(entry->effect->rm, "lp",
1574 hook_entry_gob(entry),
1576 list_remove(&entry->list);
1577 hook_entry_del(entry);
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))
1592 hook_list_tryunlock(hl, locked);
1593 //dbg("hookForEach exit\n");
1597 static int object_start_effect(struct hook_entry *entry, void *data)
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),
1608 void Object::start(void)
1612 // Don't start effects multiple times.
1619 for (i = 0; i < OBJ_NUM_HOOKS; i++) {
1620 hookForEach(i, object_start_effect, this);
1622 forceEffect = false;
1625 struct add_hook_hook_data {
1626 struct effect *effect;
1630 int object_run_add_hook_hook(hook_entry_t *entry, void *data)
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),
1637 context->reject = 1;
1638 return context->reject;
1641 int object_find_effect(hook_entry_t *entry, void *data)
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;
1650 bool Object::addEffect(struct effect *effect, struct gob *gob)
1652 hook_entry_t *entry;
1653 struct add_hook_hook_data data;
1654 int hook_id = effect->hook_id;
1656 assert(VALID_HOOK_ID(effect->hook_id));
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;
1664 // Use the same data structure to search for the effect and to check
1665 // for countereffects.
1666 data.effect = effect;
1669 // For non-cumulative effects Check if the effect is already applied.
1670 if (! effect->cumulative) {
1671 hookForEach(hook_id, object_find_effect, &data);
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
1681 if (! forceEffect) {
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,
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);
1699 entry = hook_entry_new(effect, gob);
1700 hook_entry_set_started(entry);
1702 // Roll to see if the character detects the effect (it won't show up in
1704 if (dice_roll("1d20") > effect->detect_dc) {
1705 hook_entry_detect(entry);
1708 hook_list_add(&hooks[hook_id], &entry->list);
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
1715 if (isPlayerControlled()) {
1722 void Object::restoreEffect(struct effect *effect, struct gob *gob, int flags,
1723 clock_alarm_t expiration)
1725 hook_entry_t *entry;
1727 assert(VALID_HOOK_ID(effect->hook_id));
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().
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);
1742 struct object_run_hook_entry_data {
1748 static int object_run_hook_entry(struct hook_entry *entry, void *data)
1750 struct object_run_hook_entry_data *info;
1751 info = (struct object_run_hook_entry_data *)data;
1753 if (entry->effect->exec)
1754 return closure_execlpv(entry->effect->exec,
1755 hook_entry_gob(entry),
1762 void Object::runHook(int hook_id, const char *fmt, ...)
1764 struct object_run_hook_entry_data data;
1768 va_start(data.args, fmt);
1769 hookForEach(hook_id, object_run_hook_entry, &data);
1773 void Object::saveHooks(struct save *save)
1777 save->write(save, ";; hooks\n");
1778 save->enter(save, "(list\n");
1779 for (i = 0; i < OBJ_NUM_HOOKS; i++) {
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);
1787 save->exit(save, ")\n");
1790 bool Object::removeEffect(struct effect *effect)
1793 struct hook_list *hl;
1794 int hook_id = effect->hook_id;
1796 assert(VALID_HOOK_ID(effect->hook_id));
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;
1804 hl = &hooks[hook_id];
1806 elem = hook_list_first(hl);
1807 while (elem != hook_list_end(hl)) {
1808 hook_entry_t *entry;
1810 entry = outcast(elem, hook_entry_t, list);
1813 if (hook_entry_is_invalid(entry))
1814 // Already pending removal.
1817 if (effect == entry->effect) {
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);
1825 if (entry->effect->rm)
1826 closure_exec(entry->effect->rm, "lp",
1827 hook_entry_gob(entry),
1829 list_remove(&entry->list);
1830 hook_entry_del(entry);
1842 int Object::getCount()
1847 void Object::setCount(int c)
1852 bool ObjectType::isUsable()
1854 return (gifc_cap & GIFC_CAN_USE);
1857 bool ObjectType::isReadyable()
1859 return isType(ARMS_TYPE_ID);
1862 bool ObjectType::isMixable()
1864 return (gifc_cap & GIFC_CAN_MIX);
1867 bool ObjectType::isCastable()
1869 return (gifc_cap & GIFC_CAN_CAST);
1872 bool ObjectType::canExec()
1874 return (gifc_cap & GIFC_CAN_EXEC);
1877 bool ObjectType::canStep()
1879 return (gifc_cap & GIFC_CAN_STEP);
1882 bool ObjectType::canSense()
1884 return (gifc_cap & GIFC_CAN_SENSE);
1887 bool ObjectType::canXamine()
1889 return (gifc_cap & GIFC_CAN_XAMINE);
1892 bool ObjectType::canAttack()
1894 return (gifc_cap & GIFC_CAN_ATTACK);
1897 bool ObjectType::canEnter()
1899 return (gifc_cap & GIFC_CAN_ENTER);
1902 bool ObjectType::canGet()
1904 // Hack: arms types not converted over to use gifc's yet
1905 return (gifc_cap & GIFC_CAN_GET);
1908 bool ObjectType::canBuy()
1910 return (gifc_cap & GIFC_CAN_BUY);
1913 bool ObjectType::canSearch()
1915 return (gifc_cap & GIFC_CAN_SEARCH);
1918 bool ObjectType::canOpen()
1920 return (gifc_cap & GIFC_CAN_OPEN);
1923 bool ObjectType::canBump()
1925 return (gifc_cap & GIFC_CAN_BUMP);
1928 int ObjectType::open(Object *obj, Object *opener)
1930 return closure_exec(gifc, "ypp", "open", obj, opener);
1933 int ObjectType::bump(Object *obj, Object *bumper)
1935 return closure_exec(gifc, "ypp", "bump", obj, bumper);
1938 bool ObjectType::canHandle()
1940 return (gifc_cap & GIFC_CAN_HANDLE);
1943 int ObjectType::handle(Object *obj, Object *handler)
1945 return closure_exec(gifc, "ypp", "handle", obj, handler);
1948 bool ObjectType::canHitLocation()
1950 return (gifc_cap & GIFC_CAN_HIT_LOCATION);
1953 int ObjectType::hitLocation(Object *obj, Object *attacker, Object *target, struct place *place, int x, int y, int dam)
1955 return closure_exec(gifc, "yppppddd", "hit-loc", obj, attacker, target, place, x, y, dam);
1958 int ObjectType::step(Object *obj, Object *stepper)
1960 return closure_exec(gifc, "ypp", "step", obj, stepper);
1963 int ObjectType::sense(Object *obj, Object *stepper)
1965 return closure_exec(gifc, "ypp", "sense", obj, stepper);
1968 int ObjectType::xamine(Object *obj, Object *xaminer)
1970 return closure_exec(gifc, "ypp", "xamine", obj, xaminer);
1973 int ObjectType::attack(Object *obj, Object *stepper)
1975 return closure_exec(gifc, "ypp", "attack", obj, stepper);
1978 bool ObjectType::canOnAttack()
1980 return (gifc_cap & GIFC_CAN_ON_ATTACK);
1983 int ObjectType::onAttack(Object *obj, Object *stepper)
1985 return closure_exec(gifc, "yp", "on-attack", stepper);
1988 int ObjectType::enter(Object *obj, Object *stepper)
1990 return closure_exec(gifc, "ypp", "enter", obj, stepper);
1993 int ObjectType::exec(Object *obj)
1995 return closure_exec(gifc, "yp", "exec", obj);
1998 int ObjectType::use(Object *user)
2000 return closure_exec(gifc, "ypp", "use", this, user);
2003 int ObjectType::cast(Object *caster)
2005 return closure_exec(gifc, "yp", "cast", caster);
2008 int ObjectType::get(Object *obj, Object *getter)
2010 return closure_exec(gifc, "ypp", "get", obj, getter);
2013 int ObjectType::buy(Object *buyer, int q)
2015 return closure_exec(gifc, "ypd", "buy", buyer, q);
2018 int ObjectType::search(Object *obj, Object *searcher)
2020 return closure_exec(gifc, "ypp", "search", obj, searcher);
2023 closure_t *ObjectType::getGifc()
2028 void ObjectType::setGifc(closure_t *g, int cap)
2032 closure_unref(gifc);
2045 void ObjectType::setGob(struct gob *g)
2058 struct gob * ObjectType::getGob()
2063 bool ObjectType::hasDescribeHook()
2065 return (gifc_cap & GIFC_CAN_DESCRIBE);
2068 void ObjectType::runDescribeHook(Object *obj)
2070 closure_exec(gifc, "ypd", "describe", obj, obj->getCount());
2073 bool ObjectType::isQuestItem()
2075 return questItemFlag;
2078 void ObjectType::setQuestItemFlag(bool val)
2080 questItemFlag = val;
2083 struct mmode *ObjectType::getMovementMode()
2085 return movementMode;
2088 void ObjectType::setMovementMode(struct mmode *mmode)
2090 movementMode = mmode;
2093 /////////////////////////////////////////////////////////////////////////////////////////////
2096 bool Object::add(ObjectType *type, int amount)
2098 return false; // subclasses will overload
2101 bool Object::takeOut(ObjectType *type, int amount)
2103 return false; // subclasses will overload
2106 bool Object::addFood(int amount)
2108 return false; // subclasses will overload
2111 bool Object::addGold(int amount)
2113 return false; // subclasses will overload
2116 void Object::setGob(struct gob *g)
2121 struct gob * Object::getGob()
2126 void Object::setSprite(struct sprite *sprite)
2128 current_sprite = sprite;
2131 void Object::step(Object *stepper)
2133 if (! getObjectType() ||
2134 ! getObjectType()->canStep())
2137 getObjectType()->step(this, stepper);
2140 void Object::sense(Object *stepper)
2142 if (! getObjectType() ||
2143 ! getObjectType()->canSense())
2146 getObjectType()->sense(this, stepper);
2149 void Object::attack(Object *stepper)
2151 if (! getObjectType() ||
2152 ! getObjectType()->canAttack())
2155 getObjectType()->attack(this, stepper);
2158 void Object::onAttack(Object *user)
2160 if (! getObjectType() ||
2161 ! getObjectType()->canOnAttack())
2164 getObjectType()->onAttack(this, user);
2167 struct conv *Object::getConversation()
2172 void Object::setConversation(struct conv *val)
2187 bool Object::canEnter()
2189 return (getObjectType() && getObjectType()->canEnter());
2192 void Object::enter(Object *enterer)
2194 getObjectType()->enter(this, enterer);
2197 bool Object::canStep()
2199 return (getObjectType() && getObjectType()->canStep());
2202 bool Object::canSense()
2204 return (getObjectType() && getObjectType()->canSense());
2207 void Object::setLight(int val)
2214 bool Object::isTemporary()
2219 void Object::setTemporary(bool val)
2224 struct mmode *Object::getMovementMode()
2226 return getObjectType()->getMovementMode();
2229 void Object::setMovementMode(struct mmode *mmode)
2234 Object *Object::getSpeaker()
2239 void Object::resetActionPoints()
2244 void Object::setActionPoints(int amount)
2246 action_points = amount;
2247 if (isPlayerControlled()) {
2252 void obj_inc_ref(Object *obj)
2256 if (obj->getName() && ! strcmp(obj->getName(), "player party"
2257 /*"The Wanderer"*/)) {
2258 printf("obj_inc_ref: %d\n", obj->refcount);
2263 void obj_dec_ref(Object *obj)
2265 assert((obj)->refcount >= 0);
2268 if (obj->getName() && ! strcmp(obj->getName(), "player party"
2269 /*"The Wanderer"*/)) {
2270 printf("obj_dec_ref: %d\n", obj->refcount);
2273 if (! obj->refcount)
2277 int Object::getTTL(void) { return ttl; }
2279 bool Object::surreptitiouslyRemove()
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.
2291 && getPlace()==player_party->getPlace()
2292 && (place_flying_distance(player_party->getPlace(),
2293 player_party->getX(),
2294 player_party->getY(),
2297 < player_party->getVisionRadius())
2298 && place_in_los(player_party->getPlace(),
2299 player_party->getX(),
2300 player_party->getY(),
2311 void Object::setTTL(class Object *obj, int val)
2315 obj->surreptitiouslyRemove(); // may destroy obj!
2319 void Object::decrementTTL(class Object *obj)
2321 // don't decrement if everlasting
2322 if (-1==obj->getTTL())
2325 if (0==obj->getTTL()) {
2326 obj->surreptitiouslyRemove(); // may destroy obj!
2330 obj->setTTL(obj, obj->getTTL() - 1); // may destroy obj!
2333 bool Object::isStationary()
2338 bool Object::setFacing(int val)
2340 if (!sprite_can_face(getSprite(), val))
2346 int Object::getFacing()
2351 bool Object::ignoresTimeStop()
2353 return ignoreTimeStop;
2356 void Object::setIgnoreTimeStop(bool val)
2358 ignoreTimeStop = val;
2361 struct sprite *Object::getPortrait()
2366 void Object::setPortrait(struct sprite *sprite)