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 if (isvowel(name[0]))
165 log_continue("%s", getName());
166 } else if (getPluralName()) {
167 log_continue("some %s (%d)", getPluralName(), count);
169 if (endsWith(name, "s")
170 || endsWith(name, "sh"))
171 log_continue("some %ses (%d)", getName(), count);
173 log_continue("some %ss (%d)", getName(), count);
177 void ObjectType::describe(Object *obj)
179 if (hasDescribeHook()) {
180 runDescribeHook(obj);
184 describeType(obj->getCount());
187 bool ObjectType::isType(int classID)
189 return (classID == OBJECT_TYPE_ID);
192 int ObjectType::getType()
194 return OBJECT_TYPE_ID;
197 const char *ObjectType::getTag()
202 const char *ObjectType::getName()
207 struct sprite *ObjectType::getSprite()
212 enum layer ObjectType::getLayer()
217 bool ObjectType::isVisible()
222 int ObjectType::getSpeed()
227 int ObjectType::getRequiredActionPoints()
229 return required_action_points;
232 int ObjectType::getMaxHp()
237 //////////////////////////////////////////////////////////////////////////////
241 //////////////////////////////////////////////////////////////////////////////
242 #define hook_list_init(hl) do { list_init(&(hl)->list); (hl)->lock = 0; } while (0)
243 #define hook_list_add(hl,en) (list_add(&(hl)->list, (en)))
244 #define hook_list_lock(hl) (++(hl)->lock)
245 #define hook_list_unlock(hl) ((hl)->lock--)
246 #define hook_list_first(hl) ((hl)->list.next)
247 #define hook_list_end(hl) (&(hl)->list)
248 #define hook_list_empty(hl) (list_empty(&(hl)->list))
249 #define hook_list_for_each(hl,ptr) list_for_each(&(hl)->list, (ptr))
250 #define hook_list_locked(hl) ((hl)->lock)
251 #define hook_list_trylock(hl) (hook_list_locked(hl) ? 0 : hook_list_lock(hl))
252 #define hook_list_tryunlock(hl,lock) ((lock) ? hook_list_unlock(hl) : 0)
254 //////////////////////////////////////////////////////////////////////////////
258 //////////////////////////////////////////////////////////////////////////////
260 #define HEF_INVALID (1<<0)
261 #define HEF_DETECTED (1<<1)
262 #define HEF_STARTED (1<<2)
264 #define hook_entry_invalidate(he) ((he)->flags |= HEF_INVALID)
265 #define hook_entry_detected(he) ((he)->flags & HEF_DETECTED)
266 #define hook_entry_detect(he) ((he)->flags |= HEF_DETECTED)
267 #define hook_entry_gob(he) ((he)->gob->p)
268 #define hook_entry_started(he) ((he)->started)
269 #define hook_entry_set_started(he) ((he)->started=1)
271 hook_entry_t *hook_entry_new(struct effect *effect, struct gob *gob)
275 entry = (hook_entry_t*)calloc(1, sizeof(*entry));
277 list_init(&entry->list);
278 entry->effect = effect;
282 if (effect_will_expire(effect)) {
283 clock_alarm_set(&entry->expiration, effect->duration);
286 //dbg("hook_entry_new: %p\n", entry);
290 void hook_entry_del(hook_entry_t *entry)
292 //dbg("hook_entry_del: %p\n", entry);
294 gob_unref(entry->gob);
298 void hook_entry_save(hook_entry_t *entry, struct save *save)
300 // Note: saved effects are loaded in kern.c:kern_mk_char() & attached
301 // via restoreEffect().
303 save->enter(save, "(list\n");
304 save->write(save, "%s\n", entry->effect->tag);
306 gob_save(entry->gob, save);
308 save->write(save, "nil\n");
309 save->write(save, "%d\n", entry->flags);
310 clock_alarm_save(entry->expiration, save);
311 save->exit(save, ")\n");
314 static inline int hook_entry_is_invalid(hook_entry_t *entry)
316 return ((entry->flags & HEF_INVALID) ||
317 (effect_will_expire(entry->effect) &&
318 clock_alarm_is_expired(&entry->expiration)));
321 //////////////////////////////////////////////////////////////////////////////
323 // Object class methods
325 //////////////////////////////////////////////////////////////////////////////
327 void Object::init(int x, int y, struct place *place, class ObjectType * type)
336 void Object::init(class ObjectType * type)
341 bool Object::tryToRelocateToNewPlace(struct place *newplace,
343 struct closure *cutscene)
346 place_remove_object(getPlace(), this);
351 closure_exec(cutscene, NULL);
358 place_add_object(getPlace(), this);
365 // Trigger the topmost sense mechanism on a tile, using this as the subject
367 // @param tilePlace place the tile is in
368 // @param tileX coord of tile
369 // @param tileY coord of tile
371 void Object::triggerSense(struct place *tilePlace, int tileX, int tileY)
373 Object *mech = place_get_object(tilePlace, tileX, tileY, mech_layer);
376 && mech->getObjectType()->canSense()) {
377 mech->getObjectType()->sense(mech, this);
382 // Trigger the topmost step mechanism on a tile, using this as the subject
384 // @param tilePlace place the tile is in
385 // @param tileX coord of tile
386 // @param tileY coord of tile
388 void Object::triggerStep(struct place *tilePlace, int tileX, int tileY)
390 Object *mech = place_get_object(tilePlace, tileX, tileY, mech_layer);
393 && mech->getObjectType()->canStep()) {
394 mech->getObjectType()->step(mech, this);
399 // Run the step and sense triggers on tile entry using this as the subject,
402 // @param tilePlace place the tile is in
403 // @param tileX coord of tile
404 // @param tileY coord of tile
405 // @param flags relocation flags which specify what types of triggers to avoid
407 void Object::triggerOnTileEntry(struct place *tilePlace, int tileX, int tileY,
410 bool sense = !(flags & REL_NOSENSE);
411 bool step = !(flags & REL_NOSTEP);
414 triggerSense(tilePlace, tileX, tileY);
418 triggerStep(tilePlace, tileX, tileY);
423 // Run the sense trigger on tile exit using this as the subject, flags
426 // @param tilePlace place the tile is in
427 // @param tileX coord of tile
428 // @param tileY coord of tile
429 // @param flags relocation flags which specify what types of triggers to avoid
431 void Object::triggerOnTileExit(struct place *tilePlace, int tileX, int tileY,
434 bool sense = ! (flags & REL_NOSENSE);
437 triggerSense(tilePlace, tileX, tileY);
441 void Object::relocate(struct place *newplace, int newx, int newy, int flags,
442 struct closure *place_switch_hook)
446 struct place *foc_place;
448 struct place *oldPlace = getPlace(); // remember for tile exit
449 int oldX = getX(); // remember for tile exit
450 int oldY = getY(); // remember for tile exit
458 if (newplace == getPlace()) {
460 // Moving from one tile to another in the same place.
461 if (place_switch_hook) {
463 // A cut scene was specified (this happens for
464 // moongate entry, for example). Remove the
465 // object, play the cut scene, and put the
466 // object back down at the new location.
471 place_remove_object(getPlace(), this);
473 closure_exec(place_switch_hook, NULL);
478 place_add_object(newplace, this);
485 place_move_object(newplace, this, newx, newy);
492 // Place-to-place movement, where the object is on the
493 // map. This is a special case for character objects so
494 // use an overloadable method to implement it.
495 if (! tryToRelocateToNewPlace(newplace, newx, newy,
496 place_switch_hook)) {
500 // This object may no longer be on a map as a result of
501 // the above call. If so then finish processing.
504 // Run the exit triggers before returning
506 triggerOnTileExit(oldPlace, oldX, oldY, flags);
515 // Place-to-place movement, where the object is off-map in the
516 // old place. I assume by default it will be on-map in the new
517 // place and let changePlaceHook() fix things up if necessary.
519 setX(place_wrap_x(newplace, newx));
520 setY(place_wrap_y(newplace, newy));
521 place_add_object(getPlace(), this);
528 // Run the exit triggers.
530 triggerOnTileExit(oldPlace, oldX, oldY, flags);
535 // It's possible that changePlaceHook() removed this object from the
536 // map. This certainly happens when the player party moves from town to
537 // wilderness, for example. In this case I probably want to skip all of
538 // what follows. I ABSOLUTELY want to skip the call to updateView() at
539 // the end, and in fact I changed updateView() to assert if this object
540 // is not on the map.
544 // Attenuate movement sound based on distance from the camera's focal
546 volume = SOUND_MAX_VOLUME;
547 mapGetCameraFocus(&foc_place, &foc_x, &foc_y);
548 if (foc_place == getPlace()) {
549 distance = place_flying_distance(foc_place, foc_x, foc_y,
552 volume = (volume * (20 - distance))/20;
554 sound_play(get_movement_sound(), volume);
557 // If the camera is attached to this object then update it to focus on
558 // the object's new location.
559 if (isCameraAttached()) {
560 mapCenterCamera(getX(), getY());
565 // Run the entry triggers.
566 triggerOnTileEntry(getPlace(), getX(), getY(), flags);
569 void Object::setOnMap(bool val)
574 void Object::remove()
579 struct place *oldPlace = getPlace(); // remember for tile exit
580 int oldX = getX(); // remember for tile exit
581 int oldY = getY(); // remember for tile exit
585 place_remove_object(getPlace(), this);
587 // Run the exit triggers.
590 triggerOnTileExit(oldPlace, oldX, oldY, 0);
597 // Note: do NOT call setPlace(NULL) here. When the player party
598 // object is removed from the map it still needs to "know" what
599 // place the members are in.
606 void Object::paint(int sx, int sy)
608 struct sprite *sprite = getSprite();
610 int origFacing = sprite_get_facing(sprite);
611 sprite_set_facing(sprite, facing);
613 && isPlayerControlled()) {
615 sprite_frame + Session->time_stop_ticks,
618 sprite_paint(sprite, sprite_frame, sx, sy);
620 sprite_set_facing(sprite, origFacing);
624 void Object::describe()
626 assert(getObjectType()); // else implement this method in subclass
627 getObjectType()->describe(this);
629 log_continue(" (invisible)");
632 log_continue(" (submerged)");
636 void Object::examine()
638 assert(getObjectType()); // else implement this method in subclass
641 //todo: dont have examiner to pass in to ifc
642 if (getObjectType()->canXamine()) {
644 getObjectType()->xamine(this, this);
649 sound_t *Object::get_movement_sound()
654 class Object *Object::clone()
656 // gmcnutt: added support for an optional quantity field for placed
661 obj = getObjectType()->createInstance();
662 obj->setPlace(getPlace());
666 // FIXME: should assign the new object a unique script tag
671 //////////////////////////////////////////////////
673 bool Object::isType(int classID)
675 return (classID == OBJECT_ID);
677 int Object::getType()
688 Object::Object(class ObjectType * type)
701 for (i = 0; i < OBJ_NUM_HOOKS; i++) {
702 hook_list_init(&hooks[i]);
711 action_points = 0; /* FIXME: assumes no debt */
712 control_mode = CONTROL_MODE_AUTO;
713 camera_attached = false;
723 current_sprite = NULL;
728 pclass = PCLASS_NONE;
729 ttl = -1; // everlasting by default
731 facing = SPRITE_DEF_FACING;
732 ignoreTimeStop = false;
736 if (getObjectType() && ! getObjectType()->isVisible())
741 // Four is the typical max number of frames, so using more will not
742 // help (it won't hurt either, sprites.c will ensure the frame is
743 // within what the sprite will actually support).
744 sprite_frame = rand() % 4;
752 dbg("refcount=%d\n", refcount);
756 //dbg("destroying %d %08lx %s\n", refcount, this, getName());
759 session_rm(Session, handle);
772 mapDestroyView(getView());
780 // For each type of hook...
781 for (i = 0; i < OBJ_NUM_HOOKS; i++) {
783 // Shouldn't be destroying the object while one of its hook
785 assert(! hook_list_locked(&hooks[i]));
787 // This is a hack to workaround a bug due to a design flaw. A
788 // request has been logged to fix the design flaw [SF
789 // 1568398]. It will be a "deep" fix, and I expect it to add
790 // lots of new bugs, so I'm going to see if this relatively
791 // easy change will get us by a bit longer.
793 // The bug is this: we land here in the process of a
794 // session_del() call. Some of our effects have already been
795 // destroyed. In hookForEach() it will inspect some of these
796 // effects, not knowing that they are destroyed, and cause a
797 // crash. So instead of using hookForEach() I'm going to
798 // destroy the lists by hand right here without calling any
799 // hook removal closures or anything like that.
801 struct hook_list *hl;
803 lptr = hook_list_first(hl);
804 while (lptr != hook_list_end(hl)) {
805 hook_entry_t *he = outcast(lptr, hook_entry_t, list);
807 list_remove(&he->list);
823 struct place *Object::getPlace()
828 struct sprite *Object::getSprite()
831 return current_sprite;
833 return type->getSprite();
837 bool Object::isSelected()
842 enum layer Object::getLayer(void)
844 // subtle: ~Being runs, calls ~Object, calls hookForEach to delete all
845 // the hooks, but there's an invalid hook which gets it's rm closure
846 // invoked. In that closure it calls kern-obj-is-being, which lands us
847 // here instead of Being::getLayer() because of where we are in the
848 // destructor chain, and Being's have no object type... hopefully since
849 // the object is being destroyed it doesn't really matter what the
852 return getObjectType()->getLayer();
857 const char *Object::getName(void)
860 return type->getName();
864 class ObjectType *Object::getObjectType()
869 bool Object::isDestroyed()
875 void Object::setX(int x)
880 void Object::setY(int y)
885 void Object::changeX(int dx)
890 void Object::changeY(int dy)
895 void Object::setPlace(struct place *place)
900 void Object::select(bool val)
902 if (val == isSelected())
906 mapSetSelected(this);
907 } else if (isSelected()) {
908 mapSetSelected(NULL);
912 mapUpdateTile(getPlace(), getX(), getY());
916 void Object::destroy()
924 int Object::getLight()
932 if (getObjectType()->canExec())
933 getObjectType()->exec(this);
934 endTurn(); // warn: might destroy this!
935 Object::decrementTTL(this); // might destroy the object!
938 void Object::synchronize()
942 bool Object::isVisible()
944 //return getObjectType()->isVisible();
948 void Object::setVisible(bool val)
954 if (visible < 0 && getName()) {
955 printf("%s: %d\n", getName(), visible);
960 bool Object::isSubmerged()
965 void Object::setSubmerged(bool val)
970 bool Object::isShaded()
972 return isSubmerged();
975 void Object::setOpacity(bool val)
977 // If the opacity is changing then invalidate the view mask cache in
978 // the surrounding area.
979 if (val != opacity && isOnMap())
980 vmask_invalidate(getPlace(), getX(), getY(), 1, 1);
985 bool Object::isOpaque()
990 bool Object::joinPlayer()
995 int Object::getActivity()
1000 int Object::getActionPointsPerTurn()
1002 int baseAP = (int)(getSpeed() * session_get_time_accel());
1004 // If 'Quicken' is in effect then give player-controlled objects bonus
1005 // action points per turn.
1006 if (Quicken > 0 && isPlayerControlled()) {
1010 // If 'TimeStop' is in effect then give action points ONLY to
1011 // player-controlled objects.
1012 if (TimeStop && ! isPlayerControlled()) {
1019 void Object::applyEffect(closure_t *effect)
1021 closure_exec(effect, "p", this);
1028 void Object::sleep()
1032 sound_t *Object::getDamageSound()
1037 void Object::damage(int amount)
1039 // Paint the red "*" damage symbol over the character's icon on the map
1041 mapPaintDamage(getX(), getY());
1042 sound_play(getDamageSound(), SOUND_MAX_VOLUME);
1045 runHook(OBJ_HOOK_DAMAGE, 0);
1048 void Object::inflictDamage(int amount, class Character *attacker)
1053 int Object::getActionPoints()
1055 return action_points;
1058 void Object::decActionPoints(int points)
1060 setActionPoints(action_points - points);
1063 void Object::endTurn()
1065 if (action_points > 0)
1071 void Object::startTurn()
1073 setActionPoints(action_points + getActionPointsPerTurn());
1074 runHook(OBJ_HOOK_START_OF_TURN, 0);
1077 int Object::getSpeed()
1079 return getObjectType()->getSpeed();
1082 int Object::getRequiredActionPoints()
1084 return getObjectType()->getRequiredActionPoints();
1087 bool Object::isOnMap()
1092 bool Object::isDead()
1097 enum control_mode Object::getControlMode()
1099 return control_mode;
1102 void Object::setControlMode(enum control_mode mode)
1104 control_mode = mode;
1107 void Object::attachCamera(bool val)
1109 if (camera_attached == val)
1112 camera_attached = val;
1115 mapAttachCamera(this);
1117 mapDetachCamera(this);
1120 bool Object::isCameraAttached()
1122 return camera_attached;
1125 bool Object::isTurnEnded()
1127 return (getActionPoints() <= 0 ||
1132 bool Object::isPlayerPartyMember()
1137 bool Object::addToInventory(class Object *object)
1142 bool Object::hasInInventory(class ObjectType *object)
1147 void Object::heal(int amount)
1149 amount = min(amount, getMaxHp() - hp);
1153 bool Object::isPlayerControlled()
1158 int Object::getMaxHp()
1160 return getObjectType()->getMaxHp();
1168 bool Object::isCompanionOf(class Object *other)
1173 int Object::getPclass()
1178 void Object::setPclass(int val)
1183 int Object::getMovementCost(int pclass)
1185 if (pclass == PCLASS_NONE)
1188 struct mmode *mmode = getMovementMode();
1190 return PTABLE_IMPASSABLE;
1192 return ptable_get(session_ptable(), mmode->index, pclass);
1195 bool Object::isPassable(int pclass)
1197 return (getMovementCost(pclass) != PTABLE_IMPASSABLE);
1200 bool Object::putOnMap(struct place *new_place, int new_x, int new_y, int r,
1203 // --------------------------------------------------------------------
1204 // Put an object on a map. If possible, put it at (new_x, new_y). If
1205 // that's not possible then put it at some other (x, y) such that:
1207 // o (x, y) is with radius r of (new_x, new_y)
1209 // o The object can find a path from (x, y) to (new_x, new_y)
1211 // If no such (x, y) exists then return false without placing the
1213 // --------------------------------------------------------------------
1227 int x_offsets[] = { -1, 1, 0, 0 };
1228 int y_offsets[] = { 0, 0, -1, 1 };
1230 printf("Putting %s near (%d %d)\n", getName(), new_x, new_y);
1232 // --------------------------------------------------------------------
1233 // Although the caller specified a radius, internally I use a bounding
1234 // box. Assign the upper left corner in place coordinates. I don't
1235 // *think* I have to worry about wrapping coordinates because all the
1236 // place_* methods should do that internally.
1237 // --------------------------------------------------------------------
1239 rx = new_x - (r / 2);
1240 ry = new_y - (r / 2);
1242 // --------------------------------------------------------------------
1243 // Initialize the 'visited' table and the coordinate search queues. The
1244 // queues must be large enough to hold all the tiles in the bounding
1245 // box, plus an extra ring around it. The extra ring is for the case
1246 // where we enqueue the neighbors of tiles that are right on the edge
1247 // of the bounding box. We won't know the neighbors are bad until we
1248 // pop them off the queue and check them.
1249 // --------------------------------------------------------------------
1253 visited = (char*)calloc(sizeof(char), q_size);
1254 if (NULL == visited)
1257 q_x = (int*)calloc(sizeof(int), q_size);
1262 q_y = (int*)calloc(sizeof(int), q_size);
1267 queued = (char*)calloc(sizeof(char), q_size);
1271 // --------------------------------------------------------------------
1272 // Enqueue the preferred location to start the search.
1273 // --------------------------------------------------------------------
1275 #define INDEX(x,y) (((y)-ry) * r + ((x)-rx))
1279 q_x[q_tail] = new_x;
1280 q_y[q_tail] = new_y;
1281 index = INDEX(new_x, new_y);
1283 assert(index < q_size);
1287 // --------------------------------------------------------------------
1288 // Run through the search queue until it is exhausted or a safe
1289 // position has been found.
1290 // --------------------------------------------------------------------
1292 while (q_head != q_tail) {
1294 // ------------------------------------------------------------
1295 // Dequeue the next location to check.
1296 // ------------------------------------------------------------
1298 new_x = q_x[q_head];
1299 new_y = q_y[q_head];
1302 printf("Checking (%d,%d)...", new_x, new_y);
1304 // ------------------------------------------------------------
1305 // Has the location already been visited? (If not then mark it
1307 // ------------------------------------------------------------
1309 index = INDEX(new_x, new_y);
1311 assert(index < q_size);
1313 if (0 != visited[index]) {
1314 printf("already checked\n");
1319 // ------------------------------------------------------------
1320 // Is the location off the map or impassable?
1321 // ------------------------------------------------------------
1323 if (place_off_map(new_place, new_x, new_y) ||
1324 ! place_is_passable(new_place, new_x, new_y, this, flags)){
1328 // ------------------------------------------------------------
1329 // Is the location occupied or hazardous?
1330 // ------------------------------------------------------------
1332 if ((! (flags & PFLAG_IGNOREBEINGS) &&
1333 place_is_occupied(new_place, new_x, new_y)) ||
1334 (! (flags & PFLAG_IGNOREHAZARDS) &&
1335 place_is_hazardous(new_place, new_x, new_y))) {
1337 printf("occupied or hazardous\n");
1339 // ----------------------------------------------------
1340 // This place is not suitable, but its neighbors might
1341 // be. Put them on the queue.
1342 // ----------------------------------------------------
1344 for (i = 0; i < array_sz(x_offsets); i++) {
1350 neighbor_x = new_x + x_offsets[i];
1351 neighbor_y = new_y + y_offsets[i];
1353 // --------------------------------------------
1354 // Is the neighbor outside the search radius?
1355 // --------------------------------------------
1357 if (neighbor_x < rx ||
1359 neighbor_x >= (rx + r) ||
1360 neighbor_y >= (ry + r)) {
1364 // --------------------------------------------
1365 // Has the neighbor already been queued?
1366 // --------------------------------------------
1368 neighbor_index = INDEX(neighbor_x, neighbor_y);
1369 assert(neighbor_index >= 0);
1370 assert(neighbor_index < (q_size));
1372 if (queued[neighbor_index])
1375 // --------------------------------------------
1376 // Enqueue the neighbor
1377 // --------------------------------------------
1379 assert(q_tail < (q_size));
1381 q_x[q_tail] = neighbor_x;
1382 q_y[q_tail] = neighbor_y;
1384 queued[neighbor_index] = 1;
1390 // ------------------------------------------------------------
1391 // I've found a good spot, and I know that I can pathfind back
1392 // to the preferred location from here because of the manner in
1393 // which I found it.
1395 // Note: we don't want to activate step triggers while doing
1396 // this. One example of why this is bad is if we get here by
1397 // coming through a moongate on a town map. If the moongate is
1398 // in a state where it leads to itself we could end up
1399 // re-entering it with the relocate call below if we allowed
1401 // ------------------------------------------------------------
1404 relocate(new_place, new_x, new_y, REL_NOSTEP, NULL);
1410 // --------------------------------------------------------------------
1411 // Didn't find anyplace suitable. Return false. If the caller wants to
1412 // force placement I'll leave it to their discretion.
1413 // --------------------------------------------------------------------
1415 printf("NO PLACE FOUND!\n");
1430 struct mview *Object::getView()
1435 int Object::getVisionRadius()
1440 void Object::addView()
1442 if (NULL != getView()) {
1443 mapAddView(getView());
1448 void Object::rmView()
1450 if (NULL != getView()) {
1451 mapRmView(getView());
1455 void Object::updateView()
1459 if (NULL != getView()) {
1460 mapCenterView(getView(), getX(), getY());
1461 mapSetRadius(getView(), min(getVisionRadius(), MAX_VISION_RADIUS));
1466 void Object::setView(struct mview *new_view)
1471 void Object::changePlaceHook()
1484 bool Object::canWanderTo(int newx, int newy)
1488 enum MoveResult Object::move(int dx, int dy)
1490 return NotApplicable;
1493 void Object::save(struct save *save)
1495 // Create the object within a 'let' block
1496 save->enter(save, "(let ((kobj (kern-mk-obj %s %d\n",
1497 getObjectType()->getTag(),
1500 save->write(save, ")))\n");
1504 save->write(save, "(kern-tag '%s kobj)\n", tag);
1507 // Save the gob binding.
1509 save->enter(save, "(bind kobj\n");
1510 gob_save(getGob(), save);
1511 save->exit(save, ")\n");
1514 // Save time-to-live.
1515 if (getTTL() != -1) {
1516 save->write(save, "(kern-obj-set-ttl kobj %d)\n",
1520 // Set the custom sprite.
1521 if (current_sprite) {
1522 save->enter(save, "(kern-obj-set-sprite kobj\n");
1523 sprite_save(current_sprite, save);
1524 save->exit(save, ")\n");
1528 if (SPRITE_DEF_FACING != facing) {
1529 save->write(save, "(kern-obj-set-facing kobj %d)\n", facing);
1532 // Set the ignore-time-stop flag
1533 if (ignoreTimeStop) {
1534 save->write(save, "(kern-obj-set-ignore-time-stop kobj #t)\n");
1537 // Set the submerged flag
1539 save->write(save, "(kern-obj-set-submerged kobj #t)\n");
1542 // Close the 'let' block, returning kobj as the last thing evaluated.
1543 save->exit(save, "kobj)\n");
1548 #define VALID_HOOK_ID(id) ((id) >= 0 && (id) < OBJ_NUM_HOOKS)
1550 void Object::hookForEach(int hook_id,
1551 int (*cb)(struct hook_entry *entry, void *data),
1555 struct hook_list *hl;
1558 //dbg("hookForEach entry\n");
1560 assert(VALID_HOOK_ID(hook_id));
1561 hl = &hooks[hook_id];
1563 // Lock the hook list to prevent any removals or entry deletions while
1564 // we're running it.
1565 locked = hook_list_trylock(hl);
1567 elem = hook_list_first(hl);
1568 while (elem != hook_list_end(hl)) {
1570 hook_entry_t *entry;
1572 entry = outcast(elem, hook_entry_t, list);
1575 // Check if the entry is invalid. Invalid entries are entries
1576 // that somebody tried to remove while we had the hook list
1577 // locked. Since we have the lock, we can remove/delete them
1579 if (hook_entry_is_invalid(entry)) {
1581 //dbg("hookForEach: delete %p\n", entry);
1582 if (entry->effect->rm)
1583 closure_exec(entry->effect->rm, "lp",
1584 hook_entry_gob(entry),
1586 list_remove(&entry->list);
1587 hook_entry_del(entry);
1592 // Invoke the callback on the hook entry... this is the part
1593 // that does anything interesting. If it returns non-zero then
1594 // we skip running the rest of the hooks.
1595 if (cb(entry, data))
1602 hook_list_tryunlock(hl, locked);
1603 //dbg("hookForEach exit\n");
1607 static int object_start_effect(struct hook_entry *entry, void *data)
1609 if (entry->effect->restart
1610 && ! hook_entry_started(entry)) {
1611 hook_entry_set_started(entry);
1612 closure_exec(entry->effect->restart, "lp", hook_entry_gob(entry),
1618 void Object::start(void)
1622 // Don't start effects multiple times.
1629 for (i = 0; i < OBJ_NUM_HOOKS; i++) {
1630 hookForEach(i, object_start_effect, this);
1632 forceEffect = false;
1635 struct add_hook_hook_data {
1636 struct effect *effect;
1640 int object_run_add_hook_hook(hook_entry_t *entry, void *data)
1642 struct add_hook_hook_data *context;
1643 context = (struct add_hook_hook_data *)data;
1644 if (entry->effect->exec &&
1645 closure_exec(entry->effect->exec, "lp", hook_entry_gob(entry),
1647 context->reject = 1;
1648 return context->reject;
1651 int object_find_effect(hook_entry_t *entry, void *data)
1653 struct add_hook_hook_data *context;
1654 context = (struct add_hook_hook_data *)data;
1655 if (entry->effect == context->effect)
1656 context->reject = 1;
1657 return context->reject;
1660 bool Object::addEffect(struct effect *effect, struct gob *gob)
1662 hook_entry_t *entry;
1663 struct add_hook_hook_data data;
1664 int hook_id = effect->hook_id;
1666 assert(VALID_HOOK_ID(effect->hook_id));
1668 // Hack: NPC's don't go through a keystroke handler. For these,
1669 // substitute the start-of-turn-hook for the keystroke-hook.
1670 if (effect->hook_id == OBJ_HOOK_KEYSTROKE &&
1671 ! isPlayerControlled())
1672 hook_id = OBJ_HOOK_START_OF_TURN;
1674 // Use the same data structure to search for the effect and to check
1675 // for countereffects.
1676 data.effect = effect;
1679 // For non-cumulative effects Check if the effect is already applied.
1680 if (! effect->cumulative) {
1681 hookForEach(hook_id, object_find_effect, &data);
1686 // If we're starting up the object then "force" effects to be applied,
1687 // without checking for immunities. This works around the
1688 // script-kernel-script recursion that will otherwise occur, and which
1689 // will make summoning creatures with native effects not work from the
1691 if (! forceEffect) {
1693 // Run the add-hook entries to see if any of these will block
1694 // this new entry from being added. This is how immunities are
1695 // implemented, BTW.
1696 hookForEach(OBJ_HOOK_ADD_HOOK, object_run_add_hook_hook,
1704 // Run the "apply" procedure of the effect if it has one.
1705 if (effect->apply) {
1706 closure_exec(effect->apply, "lp", gob? gob->p : NULL, this);
1709 entry = hook_entry_new(effect, gob);
1710 hook_entry_set_started(entry);
1712 // Roll to see if the character detects the effect (it won't show up in
1714 if (dice_roll("1d20") > effect->detect_dc) {
1715 hook_entry_detect(entry);
1718 hook_list_add(&hooks[hook_id], &entry->list);
1720 // gmcnutt: I saw a crash on reload because we ran through this code
1721 // before the player party was created in the new session, and the
1722 // status window tried to access it because of this next call. I don't
1723 // think we need to be updating status for every object, anyway, only
1725 if (isPlayerControlled()) {
1732 void Object::restoreEffect(struct effect *effect, struct gob *gob, int flags,
1733 clock_alarm_t expiration)
1735 hook_entry_t *entry;
1737 assert(VALID_HOOK_ID(effect->hook_id));
1739 // Note: do NOT run the "apply" procedure of the effect here (already
1740 // tried this - causes script recursion while loading which as we know
1741 // aborts the load prematurely). Instead the "restart" procedure will
1742 // be run as part of our start() method, called on all objects near the
1743 // end of session_load().
1745 entry = hook_entry_new(effect, gob);
1746 entry->flags = flags;
1747 entry->expiration = expiration;
1748 hook_list_add(&hooks[effect->hook_id], &entry->list);
1752 struct object_run_hook_entry_data {
1758 static int object_run_hook_entry(struct hook_entry *entry, void *data)
1760 struct object_run_hook_entry_data *info;
1761 info = (struct object_run_hook_entry_data *)data;
1763 if (entry->effect->exec)
1764 return closure_execlpv(entry->effect->exec,
1765 hook_entry_gob(entry),
1772 void Object::runHook(int hook_id, const char *fmt, ...)
1774 struct object_run_hook_entry_data data;
1778 va_start(data.args, fmt);
1779 hookForEach(hook_id, object_run_hook_entry, &data);
1783 void Object::saveHooks(struct save *save)
1787 save->write(save, ";; hooks\n");
1788 save->enter(save, "(list\n");
1789 for (i = 0; i < OBJ_NUM_HOOKS; i++) {
1791 hook_list_for_each(&hooks[i], elem) {
1792 hook_entry_t *entry;
1793 entry = outcast(elem, hook_entry_t, list);
1794 hook_entry_save(entry, save);
1797 save->exit(save, ")\n");
1800 bool Object::removeEffect(struct effect *effect)
1803 struct hook_list *hl;
1804 int hook_id = effect->hook_id;
1806 assert(VALID_HOOK_ID(effect->hook_id));
1808 // Hack: NPC's don't go through a keystroke handler. For these,
1809 // substitute the start-of-turn-hook for the keystroke-hook.
1810 if (effect->hook_id == OBJ_HOOK_KEYSTROKE &&
1811 ! isPlayerControlled())
1812 hook_id = OBJ_HOOK_START_OF_TURN;
1814 hl = &hooks[hook_id];
1816 elem = hook_list_first(hl);
1817 while (elem != hook_list_end(hl)) {
1818 hook_entry_t *entry;
1820 entry = outcast(elem, hook_entry_t, list);
1823 if (hook_entry_is_invalid(entry))
1824 // Already pending removal.
1827 if (effect == entry->effect) {
1829 // If the hook list is locked we can't remove/delete
1830 // the entry, but if we mark it invalid then the
1831 // runHooks() method will eventually clean it up.
1832 if (hook_list_locked(hl)) {
1833 hook_entry_invalidate(entry);
1835 if (entry->effect->rm)
1836 closure_exec(entry->effect->rm, "lp",
1837 hook_entry_gob(entry),
1839 list_remove(&entry->list);
1840 hook_entry_del(entry);
1852 int Object::getCount()
1857 void Object::setCount(int c)
1862 bool ObjectType::isUsable()
1864 return (gifc_cap & GIFC_CAN_USE);
1867 bool ObjectType::isReadyable()
1869 return isType(ARMS_TYPE_ID);
1872 bool ObjectType::isMixable()
1874 return (gifc_cap & GIFC_CAN_MIX);
1877 bool ObjectType::isCastable()
1879 return (gifc_cap & GIFC_CAN_CAST);
1882 bool ObjectType::canExec()
1884 return (gifc_cap & GIFC_CAN_EXEC);
1887 bool ObjectType::canStep()
1889 return (gifc_cap & GIFC_CAN_STEP);
1892 bool ObjectType::canSense()
1894 return (gifc_cap & GIFC_CAN_SENSE);
1897 bool ObjectType::canXamine()
1899 return (gifc_cap & GIFC_CAN_XAMINE);
1902 bool ObjectType::canAttack()
1904 return (gifc_cap & GIFC_CAN_ATTACK);
1907 bool ObjectType::canEnter()
1909 return (gifc_cap & GIFC_CAN_ENTER);
1912 bool ObjectType::canGet()
1914 // Hack: arms types not converted over to use gifc's yet
1915 return (gifc_cap & GIFC_CAN_GET);
1918 bool ObjectType::canBuy()
1920 return (gifc_cap & GIFC_CAN_BUY);
1923 bool ObjectType::canSearch()
1925 return (gifc_cap & GIFC_CAN_SEARCH);
1928 bool ObjectType::canOpen()
1930 return (gifc_cap & GIFC_CAN_OPEN);
1933 bool ObjectType::canBump()
1935 return (gifc_cap & GIFC_CAN_BUMP);
1938 int ObjectType::open(Object *obj, Object *opener)
1940 return closure_exec(gifc, "ypp", "open", obj, opener);
1943 int ObjectType::bump(Object *obj, Object *bumper)
1945 return closure_exec(gifc, "ypp", "bump", obj, bumper);
1948 bool ObjectType::canHandle()
1950 return (gifc_cap & GIFC_CAN_HANDLE);
1953 int ObjectType::handle(Object *obj, Object *handler)
1955 return closure_exec(gifc, "ypp", "handle", obj, handler);
1958 bool ObjectType::canHitLocation()
1960 return (gifc_cap & GIFC_CAN_HIT_LOCATION);
1963 int ObjectType::hitLocation(Object *obj, Object *attacker, Object *target, struct place *place, int x, int y, int dam)
1965 return closure_exec(gifc, "yppppddd", "hit-loc", obj, attacker, target, place, x, y, dam);
1968 int ObjectType::step(Object *obj, Object *stepper)
1970 return closure_exec(gifc, "ypp", "step", obj, stepper);
1973 int ObjectType::sense(Object *obj, Object *stepper)
1975 return closure_exec(gifc, "ypp", "sense", obj, stepper);
1978 int ObjectType::xamine(Object *obj, Object *xaminer)
1980 return closure_exec(gifc, "ypp", "xamine", obj, xaminer);
1983 int ObjectType::attack(Object *obj, Object *stepper)
1985 return closure_exec(gifc, "ypp", "attack", obj, stepper);
1988 bool ObjectType::canOnAttack()
1990 return (gifc_cap & GIFC_CAN_ON_ATTACK);
1993 int ObjectType::onAttack(Object *obj, Object *stepper)
1995 return closure_exec(gifc, "yp", "on-attack", stepper);
1998 int ObjectType::enter(Object *obj, Object *stepper)
2000 return closure_exec(gifc, "ypp", "enter", obj, stepper);
2003 int ObjectType::exec(Object *obj)
2005 return closure_exec(gifc, "yp", "exec", obj);
2008 int ObjectType::use(Object *user)
2010 return closure_exec(gifc, "ypp", "use", this, user);
2013 int ObjectType::cast(Object *caster)
2015 return closure_exec(gifc, "yp", "cast", caster);
2018 int ObjectType::get(Object *obj, Object *getter)
2020 return closure_exec(gifc, "ypp", "get", obj, getter);
2023 int ObjectType::buy(Object *buyer, int q)
2025 return closure_exec(gifc, "ypd", "buy", buyer, q);
2028 int ObjectType::search(Object *obj, Object *searcher)
2030 return closure_exec(gifc, "ypp", "search", obj, searcher);
2033 closure_t *ObjectType::getGifc()
2038 void ObjectType::setGifc(closure_t *g, int cap)
2042 closure_unref(gifc);
2055 void ObjectType::setGob(struct gob *g)
2068 struct gob * ObjectType::getGob()
2073 bool ObjectType::hasDescribeHook()
2075 return (gifc_cap & GIFC_CAN_DESCRIBE);
2078 void ObjectType::runDescribeHook(Object *obj)
2080 closure_exec(gifc, "ypd", "describe", obj, obj->getCount());
2083 bool ObjectType::isQuestItem()
2085 return questItemFlag;
2088 void ObjectType::setQuestItemFlag(bool val)
2090 questItemFlag = val;
2093 struct mmode *ObjectType::getMovementMode()
2095 return movementMode;
2098 void ObjectType::setMovementMode(struct mmode *mmode)
2100 movementMode = mmode;
2103 /////////////////////////////////////////////////////////////////////////////////////////////
2106 bool Object::add(ObjectType *type, int amount)
2108 return false; // subclasses will overload
2111 bool Object::takeOut(ObjectType *type, int amount)
2113 return false; // subclasses will overload
2116 bool Object::addFood(int amount)
2118 return false; // subclasses will overload
2121 bool Object::addGold(int amount)
2123 return false; // subclasses will overload
2126 void Object::setGob(struct gob *g)
2131 struct gob * Object::getGob()
2136 void Object::setSprite(struct sprite *sprite)
2138 current_sprite = sprite;
2141 void Object::step(Object *stepper)
2143 if (! getObjectType() ||
2144 ! getObjectType()->canStep())
2147 getObjectType()->step(this, stepper);
2150 void Object::sense(Object *stepper)
2152 if (! getObjectType() ||
2153 ! getObjectType()->canSense())
2156 getObjectType()->sense(this, stepper);
2159 void Object::attack(Object *stepper)
2161 if (! getObjectType() ||
2162 ! getObjectType()->canAttack())
2165 getObjectType()->attack(this, stepper);
2168 void Object::onAttack(Object *user)
2170 if (! getObjectType() ||
2171 ! getObjectType()->canOnAttack())
2174 getObjectType()->onAttack(this, user);
2177 struct conv *Object::getConversation()
2182 void Object::setConversation(struct conv *val)
2197 bool Object::canEnter()
2199 return (getObjectType() && getObjectType()->canEnter());
2202 void Object::enter(Object *enterer)
2204 getObjectType()->enter(this, enterer);
2207 bool Object::canStep()
2209 return (getObjectType() && getObjectType()->canStep());
2212 bool Object::canSense()
2214 return (getObjectType() && getObjectType()->canSense());
2217 void Object::setLight(int val)
2224 bool Object::isTemporary()
2229 void Object::setTemporary(bool val)
2234 struct mmode *Object::getMovementMode()
2236 return getObjectType()->getMovementMode();
2239 void Object::setMovementMode(struct mmode *mmode)
2244 Object *Object::getSpeaker()
2249 void Object::resetActionPoints()
2254 void Object::setActionPoints(int amount)
2256 action_points = amount;
2257 if (isPlayerControlled()) {
2262 void obj_inc_ref(Object *obj)
2266 if (obj->getName() && ! strcmp(obj->getName(), "player party"
2267 /*"The Wanderer"*/)) {
2268 printf("obj_inc_ref: %d\n", obj->refcount);
2273 void obj_dec_ref(Object *obj)
2275 assert((obj)->refcount >= 0);
2278 if (obj->getName() && ! strcmp(obj->getName(), "player party"
2279 /*"The Wanderer"*/)) {
2280 printf("obj_dec_ref: %d\n", obj->refcount);
2283 if (! obj->refcount)
2287 int Object::getTTL(void) { return ttl; }
2289 bool Object::surreptitiouslyRemove()
2291 // crasher fix: check for player party; this may be called on boot if
2292 // the load file has (kern-obj-set-ttl kobj 0). That scenario happens
2293 // when an object's ttl is expired but the player is still in LOS, and
2294 // then the player saves the game.
2301 && getPlace()==player_party->getPlace()
2302 && (place_flying_distance(player_party->getPlace(),
2303 player_party->getX(),
2304 player_party->getY(),
2307 < player_party->getVisionRadius())
2308 && place_in_los(player_party->getPlace(),
2309 player_party->getX(),
2310 player_party->getY(),
2321 void Object::setTTL(class Object *obj, int val)
2325 obj->surreptitiouslyRemove(); // may destroy obj!
2329 void Object::decrementTTL(class Object *obj)
2331 // don't decrement if everlasting
2332 if (-1==obj->getTTL())
2335 if (0==obj->getTTL()) {
2336 obj->surreptitiouslyRemove(); // may destroy obj!
2340 obj->setTTL(obj, obj->getTTL() - 1); // may destroy obj!
2343 bool Object::isStationary()
2348 bool Object::setFacing(int val)
2350 if (!sprite_can_face(getSprite(), val))
2356 int Object::getFacing()
2361 bool Object::ignoresTimeStop()
2363 return ignoreTimeStop;
2366 void Object::setIgnoreTimeStop(bool val)
2368 ignoreTimeStop = val;
2371 struct sprite *Object::getPortrait()
2376 void Object::setPortrait(struct sprite *sprite)