aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/adrift/scobjcts.cpp
diff options
context:
space:
mode:
authorPaul Gilbert2019-09-02 20:57:19 -0700
committerPaul Gilbert2019-09-25 20:13:26 -0700
commit60c860c6a6c37b95ab8265d658cbcc144d51153b (patch)
treee21dac197fcd374bb8d348873c91230141e1fddf /engines/glk/adrift/scobjcts.cpp
parentc893c5a60b8298aa99ae1a47dea51c6f04c419bc (diff)
downloadscummvm-rg350-60c860c6a6c37b95ab8265d658cbcc144d51153b.tar.gz
scummvm-rg350-60c860c6a6c37b95ab8265d658cbcc144d51153b.tar.bz2
scummvm-rg350-60c860c6a6c37b95ab8265d658cbcc144d51153b.zip
GLK: ADRIFT: Skeleton sub-engine commit
Diffstat (limited to 'engines/glk/adrift/scobjcts.cpp')
-rw-r--r--engines/glk/adrift/scobjcts.cpp1036
1 files changed, 1036 insertions, 0 deletions
diff --git a/engines/glk/adrift/scobjcts.cpp b/engines/glk/adrift/scobjcts.cpp
new file mode 100644
index 0000000000..9bef9952e3
--- /dev/null
+++ b/engines/glk/adrift/scobjcts.cpp
@@ -0,0 +1,1036 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/adrift/scare.h"
+#include "glk/adrift/scprotos.h"
+#include "glk/adrift/scgamest.h"
+
+namespace Glk {
+namespace Adrift {
+
+/* Assorted definitions and constants. */
+static const sc_char NUL = '\0';
+
+/* Trace flag, set before running. */
+static sc_bool obj_trace = FALSE;
+
+
+/*
+ * obj_is_static()
+ * obj_is_surface()
+ * obj_is_container()
+ *
+ * Convenience functions to return TRUE for given object attributes.
+ */
+sc_bool
+obj_is_static (sc_gameref_t game, sc_int object)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ sc_bool bstatic;
+
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Static";
+ bstatic = prop_get_boolean (bundle, "B<-sis", vt_key);
+ return bstatic;
+}
+
+sc_bool
+obj_is_container (sc_gameref_t game, sc_int object)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ sc_bool is_container;
+
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Container";
+ is_container = prop_get_boolean (bundle, "B<-sis", vt_key);
+ return is_container;
+}
+
+sc_bool
+obj_is_surface (sc_gameref_t game, sc_int object)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ sc_bool is_surface;
+
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Surface";
+ is_surface = prop_get_boolean (bundle, "B<-sis", vt_key);
+ return is_surface;
+}
+
+
+/*
+ * obj_container_object()
+ *
+ * Return the index of the n'th container object found.
+ */
+sc_int
+obj_container_object (sc_gameref_t game, sc_int n)
+{
+ sc_int object, count;
+
+ /* Progress through objects until n containers found. */
+ count = n;
+ for (object = 0; object < gs_object_count (game) && count >= 0; object++)
+ {
+ if (obj_is_container (game, object))
+ count--;
+ }
+ return object - 1;
+}
+
+
+/*
+ * obj_container_index()
+ *
+ * Return index such that obj_container_object(index) == objnum.
+ */
+sc_int
+obj_container_index (sc_gameref_t game, sc_int objnum)
+{
+ sc_int object, count;
+
+ /* Progress through objects up to objnum. */
+ count = 0;
+ for (object = 0; object < objnum; object++)
+ {
+ if (obj_is_container (game, object))
+ count++;
+ }
+ return count;
+}
+
+
+/*
+ * obj_surface_object()
+ *
+ * Return the index of the n'th surface object found.
+ */
+sc_int
+obj_surface_object (sc_gameref_t game, sc_int n)
+{
+ sc_int object, count;
+
+ /* Progress through objects until n surfaces found. */
+ count = n;
+ for (object = 0; object < gs_object_count (game) && count >= 0; object++)
+ {
+ if (obj_is_surface (game, object))
+ count--;
+ }
+ return object - 1;
+}
+
+
+/*
+ * obj_surface_index()
+ *
+ * Return index such that obj_surface_object(index) == objnum.
+ */
+sc_int
+obj_surface_index (sc_gameref_t game, sc_int objnum)
+{
+ sc_int object, count;
+
+ /* Progress through objects up to objnum. */
+ count = 0;
+ for (object = 0; object < objnum; object++)
+ {
+ if (obj_is_surface (game, object))
+ count++;
+ }
+ return count;
+}
+
+
+/*
+ * obj_stateful_object()
+ *
+ * Return the index of the n'th openable or statussed object found.
+ */
+sc_int
+obj_stateful_object (sc_gameref_t game, sc_int n)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_int object, count;
+
+ /* Progress through objects until n matches found. */
+ count = n;
+ for (object = 0; object < gs_object_count (game) && count >= 0; object++)
+ {
+ sc_vartype_t vt_key[3];
+ sc_bool is_openable, is_statussed;
+
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Openable";
+ is_openable = prop_get_integer (bundle, "I<-sis", vt_key) != 0;
+ vt_key[2].string = "CurrentState";
+ is_statussed = prop_get_integer (bundle, "I<-sis", vt_key) != 0;
+ if (is_openable || is_statussed)
+ count--;
+ }
+ return object - 1;
+}
+
+
+/*
+ * obj_stateful_index()
+ *
+ * Return index such that obj_stateful_object(index) == objnum.
+ */
+sc_int
+obj_stateful_index (sc_gameref_t game, sc_int objnum)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_int object, count;
+
+ /* Progress through objects up to objnum. */
+ count = 0;
+ for (object = 0; object < objnum; object++)
+ {
+ sc_vartype_t vt_key[3];
+ sc_bool is_openable, is_statussed;
+
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Openable";
+ is_openable = prop_get_integer (bundle, "I<-sis", vt_key) != 0;
+ vt_key[2].string = "CurrentState";
+ is_statussed = prop_get_integer (bundle, "I<-sis", vt_key) != 0;
+ if (is_openable || is_statussed)
+ count++;
+ }
+ return count;
+}
+
+
+/*
+ * obj_state_name()
+ *
+ * Return the string name of the state of a given stateful object. The
+ * string is malloc'ed, and needs to be freed by the caller. Returns NULL
+ * if no valid state string found.
+ */
+sc_char *
+obj_state_name (sc_gameref_t game, sc_int objnum)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ const sc_char *states;
+ sc_int length, state, count, first, last;
+ sc_char *string;
+
+ /* Get the list of state strings for the object. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = objnum;
+ vt_key[2].string = "States";
+ states = prop_get_string (bundle, "S<-sis", vt_key);
+
+ /* Find the start of the element for the current state. */
+ state = gs_object_state (game, objnum);
+ length = strlen (states);
+ for (first = 0, count = state; first < length && count > 1; first++)
+ {
+ if (states[first] == '|')
+ count--;
+ }
+ if (count != 1)
+ return NULL;
+
+ /* Find the end of the state string. */
+ for (last = first; last < length; last++)
+ {
+ if (states[last] == '|')
+ break;
+ }
+
+ /* Allocate and take a copy of the state string. */
+ string = (sc_char *)sc_malloc (last - first + 1);
+ memcpy (string, states + first, last - first);
+ string[last - first] = NUL;
+
+ return string;
+}
+
+
+/*
+ * obj_dynamic_object()
+ *
+ * Return the index of the n'th non-static object found.
+ */
+sc_int
+obj_dynamic_object (sc_gameref_t game, sc_int n)
+{
+ sc_int object, count;
+
+ /* Progress through objects until n matches found. */
+ count = n;
+ for (object = 0; object < gs_object_count (game) && count >= 0; object++)
+ {
+ if (!obj_is_static (game, object))
+ count--;
+ }
+ return object - 1;
+}
+
+
+/*
+ * obj_wearable_object()
+ *
+ * Return the index of the n'th wearable object found.
+ */
+sc_int
+obj_wearable_object (sc_gameref_t game, sc_int n)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_int object, count;
+
+ /* Progress through objects until n matches found. */
+ count = n;
+ for (object = 0; object < gs_object_count (game) && count >= 0; object++)
+ {
+ if (!obj_is_static (game, object))
+ {
+ sc_vartype_t vt_key[3];
+
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Wearable";
+ if (prop_get_boolean (bundle, "B<-sis", vt_key))
+ count--;
+ }
+ }
+ return object - 1;
+}
+
+
+/*
+ * Size is held in the ten's digit of SizeWeight, and weight in the units.
+ * Size and weight are multipliers -- the relative size and weight of objects
+ * rises by a factor of three for each incremental multiplier. These factors
+ * are also used for the maximum size of object that can fit in a container,
+ * and the number of these that fit.
+ */
+enum
+{ OBJ_DIMENSION_DIVISOR = 10,
+ OBJ_DIMENSION_MULTIPLE = 3
+};
+
+/*
+ * obj_get_size()
+ * obj_get_weight()
+ *
+ * Return the relative size and weight of an object. For containers, the
+ * weight includes the weight of each contained object.
+ *
+ * TODO It's possible to have static objects in the player inventory, moved
+ * by events -- how should these be handled, as they have no SizeWeight?
+ */
+sc_int
+obj_get_size (sc_gameref_t game, sc_int object)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ sc_int size, count;
+
+ /* TODO For now, give static objects no size. */
+ if (obj_is_static (game, object))
+ return 0;
+
+ /* Size is the 'tens' component of SizeWeight. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "SizeWeight";
+ count = prop_get_integer (bundle, "I<-sis", vt_key) / OBJ_DIMENSION_DIVISOR;
+
+ /*
+ * Calculate base object size. Unlike weights below, we take this as simply
+ * being the maximum size; that is, when a container carries other objects
+ * its weight increases by the sum of objects carried, but its size remains
+ * constant.
+ */
+ size = 1;
+ for (; count > 0; count--)
+ size *= OBJ_DIMENSION_MULTIPLE;
+
+ if (obj_trace)
+ sc_trace ("Object: object %ld is size %ld\n", object, size);
+
+ /* Return total size. */
+ return size;
+}
+
+sc_int
+obj_get_weight (sc_gameref_t game, sc_int object)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ sc_int weight, count;
+
+ /* TODO For now, give static objects no weight. */
+ if (obj_is_static (game, object))
+ return 0;
+
+ /* Weight is the 'units' component of SizeWeight. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "SizeWeight";
+ count = prop_get_integer (bundle, "I<-sis", vt_key) % OBJ_DIMENSION_DIVISOR;
+
+ /* Calculate base object weight. */
+ weight = 1;
+ for (; count > 0; count--)
+ weight *= OBJ_DIMENSION_MULTIPLE;
+
+ /* If this is a container or a surface, add weights of parented objects. */
+ if (obj_is_container (game, object) || obj_is_surface (game, object))
+ {
+ sc_int other;
+
+ /* Find and add contained or surface objects. */
+ for (other = 0; other < gs_object_count (game); other++)
+ {
+ if ((gs_object_position (game, other) == OBJ_IN_OBJECT
+ || gs_object_position (game, other) == OBJ_ON_OBJECT)
+ && gs_object_parent (game, other) == object)
+ {
+ weight += obj_get_weight (game, other);
+ }
+ }
+ }
+
+ if (obj_trace)
+ sc_trace ("Object: object %ld is weight %ld\n", object, weight);
+
+ /* Return total weight. */
+ return weight;
+}
+
+
+/*
+ * obj_convert_player_limit()
+ * obj_get_player_size_limit()
+ * obj_get_player_weight_limit()
+ *
+ * Return the limits set on the sizes and weights a player can handle. Not
+ * really object-related except that they deal with sizing multiples.
+ */
+static sc_int
+obj_convert_player_limit (sc_int value)
+{
+ sc_int retval, index_;
+
+ /* 'Tens' of value multiplied by 3 to the power 'units' of value. */
+ retval = value / OBJ_DIMENSION_DIVISOR;
+ for (index_ = 0; index_ < value % OBJ_DIMENSION_DIVISOR; index_++)
+ retval *= OBJ_DIMENSION_MULTIPLE;
+
+ return retval;
+}
+
+sc_int
+obj_get_player_size_limit (sc_gameref_t game)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[2];
+ sc_int max_size;
+
+ vt_key[0].string = "Globals";
+ vt_key[1].string = "MaxSize";
+ max_size = prop_get_integer (bundle, "I<-ss", vt_key);
+
+ return obj_convert_player_limit (max_size);
+}
+
+sc_int
+obj_get_player_weight_limit (sc_gameref_t game)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[2];
+ sc_int max_weight;
+
+ vt_key[0].string = "Globals";
+ vt_key[1].string = "MaxWt";
+ max_weight = prop_get_integer (bundle, "I<-ss", vt_key);
+
+ return obj_convert_player_limit (max_weight);
+}
+
+
+/*
+ * obj_get_container_maxsize()
+ * obj_get_container_capacity()
+ *
+ * Return the maximum size of an object that can be placed in a container,
+ * and the number that will fit.
+ */
+sc_int
+obj_get_container_maxsize (sc_gameref_t game, sc_int object)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ sc_int maxsize, count;
+
+ /* Maxsize is found from the 'units' component of Capacity. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Capacity";
+ count = prop_get_integer (bundle, "I<-sis", vt_key) % OBJ_DIMENSION_DIVISOR;
+
+ /* Calculate and return maximum size. */
+ maxsize = 1;
+ for (; count > 0; count--)
+ maxsize *= OBJ_DIMENSION_MULTIPLE;
+
+ if (obj_trace)
+ sc_trace ("Object: object %ld has max size %ld\n", object, maxsize);
+
+ return maxsize;
+}
+
+sc_int
+obj_get_container_capacity (sc_gameref_t game, sc_int object)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ sc_int capacity;
+
+ /* The count of objects is in the 'tens' component of Capacity. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Capacity";
+ capacity = prop_get_integer (bundle, "I<-sis", vt_key)
+ / OBJ_DIMENSION_DIVISOR;
+
+ if (obj_trace)
+ sc_trace ("Object: object %ld has capacity %ld\n", object, capacity);
+
+ return capacity;
+}
+
+
+/* Sit/lie bit mask enumerations. */
+enum
+{ OBJ_STANDABLE_MASK = 1 << 0,
+ OBJ_LIEABLE_MASK = 1 << 1
+};
+
+/*
+ * obj_standable_object()
+ *
+ * Return the index of the n'th standable object found.
+ */
+sc_int
+obj_standable_object (sc_gameref_t game, sc_int n)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_int object, count;
+
+ /* Progress through objects until n standable found. */
+ count = n;
+ for (object = 0; object < gs_object_count (game) && count >= 0; object++)
+ {
+ sc_vartype_t vt_key[3];
+ sc_int sit_lie_flags;
+
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "SitLie";
+ sit_lie_flags = prop_get_integer (bundle, "I<-sis", vt_key);
+ if (sit_lie_flags & OBJ_STANDABLE_MASK)
+ count--;
+ }
+ return object - 1;
+}
+
+
+/*
+ * obj_lieable_object()
+ *
+ * Return the index of the n'th lieable object found.
+ */
+sc_int
+obj_lieable_object (sc_gameref_t game, sc_int n)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_int object, count;
+
+ /* Progress through objects until n lieable found. */
+ count = n;
+ for (object = 0; object < gs_object_count (game) && count >= 0; object++)
+ {
+ sc_vartype_t vt_key[3];
+ sc_int sit_lie_flags;
+
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "SitLie";
+ sit_lie_flags = prop_get_integer (bundle, "I<-sis", vt_key);
+ if (sit_lie_flags & OBJ_LIEABLE_MASK)
+ count--;
+ }
+ return object - 1;
+}
+
+
+/*
+ * obj_appears_plural()
+ *
+ * Return TRUE if the object appears to be plural. Adrift makes a guess at
+ * this to produce "... is on.." or "... are on...". It's not clear how it
+ * does it, but it looks something like: singular if prefix is "a" or "an"
+ * or ""; plural if prefix is "the" or "some" and short name ends with 's'
+ * that is not preceded by 'u'.
+ */
+sc_bool
+obj_appears_plural (sc_gameref_t game, sc_int object)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ const sc_char *prefix, *name;
+
+ /* Check prefix for "a", "an", or empty. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Prefix";
+ prefix = prop_get_string (bundle, "S<-sis", vt_key);
+
+ if (!(sc_strempty (prefix)
+ || sc_compare_word (prefix, "a", 1)
+ || sc_compare_word (prefix, "an", 2)))
+ {
+ sc_int length;
+
+ /* Check name for ending in 's', but not 'us'. */
+ vt_key[2].string = "Short";
+ name = prop_get_string (bundle, "S<-sis", vt_key);
+ length = strlen (name);
+
+ if (!sc_strempty (name)
+ && sc_tolower (name[length - 1]) == 's'
+ && (length < 2 || sc_tolower (name[length - 2]) != 'u'))
+ return TRUE;
+ }
+
+ /* Doesn't look plural. */
+ return FALSE;
+}
+
+
+/*
+ * obj_directly_in_room_internal()
+ * obj_directly_in_room()
+ *
+ * Return TRUE if a given object is currently on the floor of a given room.
+ */
+static sc_bool
+obj_directly_in_room_internal (sc_gameref_t game, sc_int object, sc_int room)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+
+ /* See if the object is static or dynamic. */
+ if (obj_is_static (game, object))
+ {
+ sc_vartype_t vt_key[5];
+ sc_int type;
+
+ /* Static object moved to player or room by event? */
+ if (!gs_object_static_unmoved (game, object))
+ {
+ if (gs_object_position (game, object) == OBJ_HELD_PLAYER)
+ return FALSE;
+ else
+ return gs_object_position (game, object) - 1 == room;
+ }
+
+ /* Check and return the room list for the object. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Where";
+ vt_key[3].string = "Type";
+ type = prop_get_integer (bundle, "I<-siss", vt_key);
+ switch (type)
+ {
+ case ROOMLIST_ALL_ROOMS:
+ return TRUE;
+ case ROOMLIST_NO_ROOMS:
+ case ROOMLIST_NPC_PART:
+ return FALSE;
+
+ case ROOMLIST_ONE_ROOM:
+ vt_key[3].string = "Room";
+ return prop_get_integer (bundle, "I<-siss", vt_key) == room + 1;
+
+ case ROOMLIST_SOME_ROOMS:
+ vt_key[3].string = "Rooms";
+ vt_key[4].integer = room + 1;
+ return prop_get_boolean (bundle, "B<-sissi", vt_key);
+
+ default:
+ sc_fatal ("obj_directly_in_room_internal:"
+ " invalid type, %ld\n", type);
+ return FALSE;
+ }
+ }
+ else
+ return gs_object_position (game, object) == room + 1;
+}
+
+sc_bool
+obj_directly_in_room (sc_gameref_t game, sc_int object, sc_int room)
+{
+ sc_bool result;
+
+ /* Check, trace result, and return. */
+ result = obj_directly_in_room_internal (game, object, room);
+
+ if (obj_trace)
+ {
+ sc_trace ("Object: checking for object %ld directly in room %ld, %s\n",
+ object, room, result ? "true" : "false");
+ }
+
+ return result;
+}
+
+
+/*
+ * obj_indirectly_in_room_internal()
+ * obj_indirectly_in_room()
+ *
+ * Return TRUE if a given object is currently in a given room, either
+ * directly, on an object indirectly, in an open object indirectly, or
+ * carried by an NPC in the room.
+ */
+static sc_bool
+obj_indirectly_in_room_internal (sc_gameref_t game, sc_int object, sc_int room)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+
+ /* See if the object is static or dynamic. */
+ if (obj_is_static (game, object))
+ {
+ sc_vartype_t vt_key[5];
+ sc_int type;
+
+ /* Static object moved to player or room by event? */
+ if (!gs_object_static_unmoved (game, object))
+ {
+ if (gs_object_position (game, object) == OBJ_HELD_PLAYER)
+ return gs_player_in_room (game, room);
+ else
+ return gs_object_position (game, object) - 1 == room;
+ }
+
+ /* Check and return the room list for the object. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Where";
+ vt_key[3].string = "Type";
+ type = prop_get_integer (bundle, "I<-siss", vt_key);
+ switch (type)
+ {
+ case ROOMLIST_ALL_ROOMS:
+ return TRUE;
+ case ROOMLIST_NO_ROOMS:
+ return FALSE;
+
+ case ROOMLIST_ONE_ROOM:
+ vt_key[3].string = "Room";
+ return prop_get_integer (bundle, "I<-siss", vt_key) == room + 1;
+
+ case ROOMLIST_SOME_ROOMS:
+ vt_key[3].string = "Rooms";
+ vt_key[4].integer = room + 1;
+ return prop_get_boolean (bundle, "B<-sissi", vt_key);
+
+ case ROOMLIST_NPC_PART:
+ {
+ sc_int npc;
+
+ vt_key[2].string = "Parent";
+ npc = prop_get_integer (bundle, "I<-sis", vt_key);
+ if (npc == 0)
+ return gs_player_in_room (game, room);
+ else
+ return npc_in_room (game, npc - 1, room);
+ }
+
+ default:
+ sc_fatal ("obj_indirectly_in_room_internal:"
+ " invalid type, %ld\n", type);
+ return FALSE;
+ }
+ }
+ else
+ {
+ sc_int parent, position;
+
+ /* Get dynamic object's parent and position. */
+ parent = gs_object_parent (game, object);
+ position = gs_object_position (game, object);
+
+ /* Decide depending on positioning. */
+ switch (position)
+ {
+ case OBJ_HIDDEN: /* Hidden. */
+ return FALSE;
+
+ case OBJ_HELD_PLAYER: /* Held by player. */
+ case OBJ_WORN_PLAYER: /* Worn by player. */
+ return gs_player_in_room (game, room);
+
+ case OBJ_HELD_NPC: /* Held by NPC. */
+ case OBJ_WORN_NPC: /* Worn by NPC. */
+ return npc_in_room (game, parent, room);
+
+ case OBJ_IN_OBJECT: /* In another object. */
+ {
+ sc_int openness;
+
+ openness = gs_object_openness (game, parent);
+ switch (openness)
+ {
+ case OBJ_WONTCLOSE:
+ case OBJ_OPEN:
+ return obj_indirectly_in_room (game, parent, room);
+ default:
+ return FALSE;
+ }
+ }
+
+ case OBJ_ON_OBJECT: /* On another object. */
+ return obj_indirectly_in_room (game, parent, room);
+
+ default: /* Within a room. */
+ if (position > gs_room_count (game) + 1)
+ {
+ sc_error ("sc_object_indirectly_in_room:"
+ " position out of bounds, %ld\n", position);
+ }
+ return position - 1 == room;
+ }
+ }
+}
+
+sc_bool
+obj_indirectly_in_room (sc_gameref_t game,
+ sc_int object, sc_int room)
+{
+ sc_bool result;
+
+ /* Check, trace result, and return. */
+ result = obj_indirectly_in_room_internal (game, object, room);
+
+ if (obj_trace)
+ {
+ sc_trace ("Object: checking for object %ld indirectly in room %ld, %s\n",
+ object, room, result ? "true" : "false");
+ }
+
+ return result;
+}
+
+
+/*
+ * obj_indirectly_held_by_player_internal()
+ * obj_indirectly_held_by_player()
+ *
+ * Return TRUE if a given object is currently held by the player, either
+ * directly, on an object indirectly, or in an open object indirectly.
+ */
+static sc_bool
+obj_indirectly_held_by_player_internal (sc_gameref_t game,
+ sc_int object)
+{
+ /* See if the object is static or dynamic. */
+ if (obj_is_static (game, object))
+ {
+ /* Static object moved to player or room by event? */
+ if (!gs_object_static_unmoved (game, object))
+ {
+ if (gs_object_position (game, object) == OBJ_HELD_PLAYER)
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ /* An unmoved static object is not held by the player. */
+ return FALSE;
+ }
+ else
+ {
+ sc_int parent, position;
+
+ /* Get dynamic object's parent and position. */
+ parent = gs_object_parent (game, object);
+ position = gs_object_position (game, object);
+
+ /* Decide depending on positioning. */
+ switch (position)
+ {
+ case OBJ_HIDDEN: /* Hidden. */
+ return FALSE;
+
+ case OBJ_HELD_PLAYER: /* Held by player. */
+ case OBJ_WORN_PLAYER: /* Worn by player. */
+ return TRUE;
+
+ case OBJ_HELD_NPC: /* Held by NPC. */
+ case OBJ_WORN_NPC: /* Worn by NPC. */
+ return FALSE;
+
+ case OBJ_IN_OBJECT: /* In another object. */
+ {
+ sc_int openness;
+
+ openness = gs_object_openness (game, parent);
+ switch (openness)
+ {
+ case OBJ_WONTCLOSE:
+ case OBJ_OPEN:
+ return obj_indirectly_held_by_player (game, parent);
+ default:
+ return FALSE;
+ }
+ }
+
+ case OBJ_ON_OBJECT: /* On another object. */
+ return obj_indirectly_held_by_player (game, parent);
+
+ default: /* Within a room. */
+ return FALSE;
+ }
+ }
+}
+
+sc_bool
+obj_indirectly_held_by_player (sc_gameref_t game, sc_int object)
+{
+ sc_bool result;
+
+ /* Check, trace result, and return. */
+ result = obj_indirectly_held_by_player_internal (game, object);
+
+ if (obj_trace)
+ {
+ sc_trace ("Object: checking for object %ld indirectly"
+ " held by player, %s\n", object, result ? "true" : "false");
+ }
+
+ return result;
+}
+
+
+/*
+ * sc_obj_shows_initial_description()
+ *
+ * Return TRUE if this object should be listed as room content.
+ */
+sc_bool
+obj_shows_initial_description (sc_gameref_t game, sc_int object)
+{
+ const sc_prop_setref_t bundle = gs_get_bundle (game);
+ sc_vartype_t vt_key[3];
+ sc_int onlywhennotmoved;
+
+ /* Get only when moved property. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "OnlyWhenNotMoved";
+ onlywhennotmoved = prop_get_integer (bundle, "I<-sis", vt_key);
+
+ /* Combine this with game in mysterious ways. */
+ switch (onlywhennotmoved)
+ {
+ case 0:
+ return TRUE;
+
+ case 1:
+ return gs_object_unmoved (game, object);
+
+ case 2:
+ {
+ sc_int initialposition;
+
+ if (gs_object_unmoved (game, object))
+ return TRUE;
+
+ vt_key[2].string = "InitialPosition";
+ initialposition = prop_get_integer (bundle, "I<-sis", vt_key) - 3;
+ return gs_object_position (game, object) == initialposition;
+ }
+ }
+
+ /* What you talkin' 'bout, Willis? */
+ return FALSE;
+}
+
+
+/*
+ * obj_turn_update()
+ * obj_setup_initial()
+ *
+ * Set initial values for object states, and update after a turn.
+ */
+void
+obj_turn_update (sc_gameref_t game)
+{
+ sc_int index_;
+
+ /* Update object seen flag to current state. */
+ for (index_ = 0; index_ < gs_object_count (game); index_++)
+ {
+ if (!gs_object_seen (game, index_)
+ && obj_indirectly_in_room (game, index_, gs_playerroom (game)))
+ gs_set_object_seen (game, index_, TRUE);
+ }
+}
+
+void
+obj_setup_initial (sc_gameref_t game)
+{
+ /* Set initial seen states for objects. */
+ obj_turn_update (game);
+}
+
+
+/*
+ * obj_debug_trace()
+ *
+ * Set object tracing on/off.
+ */
+void
+obj_debug_trace (sc_bool flag)
+{
+ obj_trace = flag;
+}
+
+} // End of namespace Adrift
+} // End of namespace Glk