1004 lines
18 KiB
C
1004 lines
18 KiB
C
|
/* object.c - Object manipulation opcodes
|
||
|
* Copyright (c) 1995-1997 Stefan Jokisch
|
||
|
*
|
||
|
* This file is part of Frotz.
|
||
|
*
|
||
|
* Frotz 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.
|
||
|
*
|
||
|
* Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||
|
*/
|
||
|
|
||
|
#include "frotz.h"
|
||
|
|
||
|
#define MAX_OBJECT 2000
|
||
|
|
||
|
#define O1_PARENT 4
|
||
|
#define O1_SIBLING 5
|
||
|
#define O1_CHILD 6
|
||
|
#define O1_PROPERTY_OFFSET 7
|
||
|
#define O1_SIZE 9
|
||
|
|
||
|
#define O4_PARENT 6
|
||
|
#define O4_SIBLING 8
|
||
|
#define O4_CHILD 10
|
||
|
#define O4_PROPERTY_OFFSET 12
|
||
|
#define O4_SIZE 14
|
||
|
|
||
|
/*
|
||
|
* object_address
|
||
|
*
|
||
|
* Calculate the address of an object.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
static zword object_address (zword obj)
|
||
|
{
|
||
|
/* zchar obj_num[10]; */
|
||
|
|
||
|
/* Check object number */
|
||
|
|
||
|
if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
|
||
|
print_string("@Attempt to address illegal object ");
|
||
|
print_num(obj);
|
||
|
print_string(". This is normally fatal.");
|
||
|
new_line();
|
||
|
runtime_error (ERR_ILL_OBJ);
|
||
|
}
|
||
|
|
||
|
/* Return object address */
|
||
|
|
||
|
if (h_version <= V3)
|
||
|
return h_objects + ((obj - 1) * O1_SIZE + 62);
|
||
|
else
|
||
|
return h_objects + ((obj - 1) * O4_SIZE + 126);
|
||
|
|
||
|
}/* object_address */
|
||
|
|
||
|
/*
|
||
|
* object_name
|
||
|
*
|
||
|
* Return the address of the given object's name.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
zword object_name (zword object)
|
||
|
{
|
||
|
zword obj_addr;
|
||
|
zword name_addr;
|
||
|
|
||
|
obj_addr = object_address (object);
|
||
|
|
||
|
/* The object name address is found at the start of the properties */
|
||
|
|
||
|
if (h_version <= V3)
|
||
|
obj_addr += O1_PROPERTY_OFFSET;
|
||
|
else
|
||
|
obj_addr += O4_PROPERTY_OFFSET;
|
||
|
|
||
|
LOW_WORD (obj_addr, name_addr)
|
||
|
|
||
|
return name_addr;
|
||
|
|
||
|
}/* object_name */
|
||
|
|
||
|
/*
|
||
|
* first_property
|
||
|
*
|
||
|
* Calculate the start address of the property list associated with
|
||
|
* an object.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
static zword first_property (zword obj)
|
||
|
{
|
||
|
zword prop_addr;
|
||
|
zbyte size;
|
||
|
|
||
|
/* Fetch address of object name */
|
||
|
|
||
|
prop_addr = object_name (obj);
|
||
|
|
||
|
/* Get length of object name */
|
||
|
|
||
|
LOW_BYTE (prop_addr, size)
|
||
|
|
||
|
/* Add name length to pointer */
|
||
|
|
||
|
return prop_addr + 1 + 2 * size;
|
||
|
|
||
|
}/* first_property */
|
||
|
|
||
|
/*
|
||
|
* next_property
|
||
|
*
|
||
|
* Calculate the address of the next property in a property list.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
static zword next_property (zword prop_addr)
|
||
|
{
|
||
|
zbyte value;
|
||
|
|
||
|
/* Load the current property id */
|
||
|
|
||
|
LOW_BYTE (prop_addr, value)
|
||
|
prop_addr++;
|
||
|
|
||
|
/* Calculate the length of this property */
|
||
|
|
||
|
if (h_version <= V3)
|
||
|
value >>= 5;
|
||
|
else if (!(value & 0x80))
|
||
|
value >>= 6;
|
||
|
else {
|
||
|
|
||
|
LOW_BYTE (prop_addr, value)
|
||
|
value &= 0x3f;
|
||
|
|
||
|
if (value == 0) value = 64; /* demanded by Spec 1.0 */
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Add property length to current property pointer */
|
||
|
|
||
|
return prop_addr + value + 1;
|
||
|
|
||
|
}/* next_property */
|
||
|
|
||
|
/*
|
||
|
* unlink_object
|
||
|
*
|
||
|
* Unlink an object from its parent and siblings.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
static void unlink_object (zword object)
|
||
|
{
|
||
|
zword obj_addr;
|
||
|
zword parent_addr;
|
||
|
zword sibling_addr;
|
||
|
|
||
|
if (object == 0) {
|
||
|
runtime_error (ERR_REMOVE_OBJECT_0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
obj_addr = object_address (object);
|
||
|
|
||
|
if (h_version <= V3) {
|
||
|
|
||
|
zbyte parent;
|
||
|
zbyte younger_sibling;
|
||
|
zbyte older_sibling;
|
||
|
zbyte zero = 0;
|
||
|
|
||
|
/* Get parent of object, and return if no parent */
|
||
|
|
||
|
obj_addr += O1_PARENT;
|
||
|
LOW_BYTE (obj_addr, parent)
|
||
|
if (!parent)
|
||
|
return;
|
||
|
|
||
|
/* Get (older) sibling of object and set both parent and sibling
|
||
|
pointers to 0 */
|
||
|
|
||
|
SET_BYTE (obj_addr, zero)
|
||
|
obj_addr += O1_SIBLING - O1_PARENT;
|
||
|
LOW_BYTE (obj_addr, older_sibling)
|
||
|
SET_BYTE (obj_addr, zero)
|
||
|
|
||
|
/* Get first child of parent (the youngest sibling of the object) */
|
||
|
|
||
|
parent_addr = object_address (parent) + O1_CHILD;
|
||
|
LOW_BYTE (parent_addr, younger_sibling)
|
||
|
|
||
|
/* Remove object from the list of siblings */
|
||
|
|
||
|
if (younger_sibling == object)
|
||
|
SET_BYTE (parent_addr, older_sibling)
|
||
|
else {
|
||
|
do {
|
||
|
sibling_addr = object_address (younger_sibling) + O1_SIBLING;
|
||
|
LOW_BYTE (sibling_addr, younger_sibling)
|
||
|
} while (younger_sibling != object);
|
||
|
SET_BYTE (sibling_addr, older_sibling)
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
zword parent;
|
||
|
zword younger_sibling;
|
||
|
zword older_sibling;
|
||
|
zword zero = 0;
|
||
|
|
||
|
/* Get parent of object, and return if no parent */
|
||
|
|
||
|
obj_addr += O4_PARENT;
|
||
|
LOW_WORD (obj_addr, parent)
|
||
|
if (!parent)
|
||
|
return;
|
||
|
|
||
|
/* Get (older) sibling of object and set both parent and sibling
|
||
|
pointers to 0 */
|
||
|
|
||
|
SET_WORD (obj_addr, zero)
|
||
|
obj_addr += O4_SIBLING - O4_PARENT;
|
||
|
LOW_WORD (obj_addr, older_sibling)
|
||
|
SET_WORD (obj_addr, zero)
|
||
|
|
||
|
/* Get first child of parent (the youngest sibling of the object) */
|
||
|
|
||
|
parent_addr = object_address (parent) + O4_CHILD;
|
||
|
LOW_WORD (parent_addr, younger_sibling)
|
||
|
|
||
|
/* Remove object from the list of siblings */
|
||
|
|
||
|
if (younger_sibling == object)
|
||
|
SET_WORD (parent_addr, older_sibling)
|
||
|
else {
|
||
|
do {
|
||
|
sibling_addr = object_address (younger_sibling) + O4_SIBLING;
|
||
|
LOW_WORD (sibling_addr, younger_sibling)
|
||
|
} while (younger_sibling != object);
|
||
|
SET_WORD (sibling_addr, older_sibling)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}/* unlink_object */
|
||
|
|
||
|
/*
|
||
|
* z_clear_attr, clear an object attribute.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
* zargs[1] = number of attribute to be cleared
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_clear_attr (void)
|
||
|
{
|
||
|
zword obj_addr;
|
||
|
zbyte value;
|
||
|
|
||
|
if (story_id == SHERLOCK)
|
||
|
if (zargs[1] == 48)
|
||
|
return;
|
||
|
|
||
|
if (zargs[1] > ((h_version <= V3) ? 31 : 47))
|
||
|
runtime_error (ERR_ILL_ATTR);
|
||
|
|
||
|
/* If we are monitoring attribute assignment display a short note */
|
||
|
|
||
|
if (f_setup.attribute_assignment) {
|
||
|
stream_mssg_on ();
|
||
|
print_string ("@clear_attr ");
|
||
|
print_object (zargs[0]);
|
||
|
print_string (" ");
|
||
|
print_num (zargs[1]);
|
||
|
stream_mssg_off ();
|
||
|
}
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_CLEAR_ATTR_0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Get attribute address */
|
||
|
|
||
|
obj_addr = object_address (zargs[0]) + zargs[1] / 8;
|
||
|
|
||
|
/* Clear attribute bit */
|
||
|
|
||
|
LOW_BYTE (obj_addr, value)
|
||
|
value &= ~(0x80 >> (zargs[1] & 7));
|
||
|
SET_BYTE (obj_addr, value)
|
||
|
|
||
|
}/* z_clear_attr */
|
||
|
|
||
|
/*
|
||
|
* z_jin, branch if the first object is inside the second.
|
||
|
*
|
||
|
* zargs[0] = first object
|
||
|
* zargs[1] = second object
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_jin (void)
|
||
|
{
|
||
|
zword obj_addr;
|
||
|
|
||
|
/* If we are monitoring object locating display a short note */
|
||
|
|
||
|
if (f_setup.object_locating) {
|
||
|
stream_mssg_on ();
|
||
|
print_string ("@jin ");
|
||
|
print_object (zargs[0]);
|
||
|
print_string (" ");
|
||
|
print_object (zargs[1]);
|
||
|
stream_mssg_off ();
|
||
|
}
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_JIN_0);
|
||
|
branch (0 == zargs[1]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
obj_addr = object_address (zargs[0]);
|
||
|
|
||
|
if (h_version <= V3) {
|
||
|
|
||
|
zbyte parent;
|
||
|
|
||
|
/* Get parent id from object */
|
||
|
|
||
|
obj_addr += O1_PARENT;
|
||
|
LOW_BYTE (obj_addr, parent)
|
||
|
|
||
|
/* Branch if the parent is obj2 */
|
||
|
|
||
|
branch (parent == zargs[1]);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
zword parent;
|
||
|
|
||
|
/* Get parent id from object */
|
||
|
|
||
|
obj_addr += O4_PARENT;
|
||
|
LOW_WORD (obj_addr, parent)
|
||
|
|
||
|
/* Branch if the parent is obj2 */
|
||
|
|
||
|
branch (parent == zargs[1]);
|
||
|
|
||
|
}
|
||
|
|
||
|
}/* z_jin */
|
||
|
|
||
|
/*
|
||
|
* z_get_child, store the child of an object.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_get_child (void)
|
||
|
{
|
||
|
zword obj_addr;
|
||
|
|
||
|
/* If we are monitoring object locating display a short note */
|
||
|
|
||
|
if (f_setup.object_locating) {
|
||
|
stream_mssg_on ();
|
||
|
print_string ("@get_child ");
|
||
|
print_object (zargs[0]);
|
||
|
stream_mssg_off ();
|
||
|
}
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_GET_CHILD_0);
|
||
|
store (0);
|
||
|
branch (FALSE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
obj_addr = object_address (zargs[0]);
|
||
|
|
||
|
if (h_version <= V3) {
|
||
|
|
||
|
zbyte child;
|
||
|
|
||
|
/* Get child id from object */
|
||
|
|
||
|
obj_addr += O1_CHILD;
|
||
|
LOW_BYTE (obj_addr, child)
|
||
|
|
||
|
/* Store child id and branch */
|
||
|
|
||
|
store (child);
|
||
|
branch (child);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
zword child;
|
||
|
|
||
|
/* Get child id from object */
|
||
|
|
||
|
obj_addr += O4_CHILD;
|
||
|
LOW_WORD (obj_addr, child)
|
||
|
|
||
|
/* Store child id and branch */
|
||
|
|
||
|
store (child);
|
||
|
branch (child);
|
||
|
|
||
|
}
|
||
|
|
||
|
}/* z_get_child */
|
||
|
|
||
|
/*
|
||
|
* z_get_next_prop, store the number of the first or next property.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
* zargs[1] = address of current property (0 gets the first property)
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_get_next_prop (void)
|
||
|
{
|
||
|
zword prop_addr;
|
||
|
zbyte value;
|
||
|
zbyte mask;
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_GET_NEXT_PROP_0);
|
||
|
store (0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Property id is in bottom five (six) bits */
|
||
|
|
||
|
mask = (h_version <= V3) ? 0x1f : 0x3f;
|
||
|
|
||
|
/* Load address of first property */
|
||
|
|
||
|
prop_addr = first_property (zargs[0]);
|
||
|
|
||
|
if (zargs[1] != 0) {
|
||
|
|
||
|
/* Scan down the property list */
|
||
|
|
||
|
do {
|
||
|
LOW_BYTE (prop_addr, value)
|
||
|
prop_addr = next_property (prop_addr);
|
||
|
} while ((value & mask) > zargs[1]);
|
||
|
|
||
|
/* Exit if the property does not exist */
|
||
|
|
||
|
if ((value & mask) != zargs[1])
|
||
|
runtime_error (ERR_NO_PROP);
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Return the property id */
|
||
|
|
||
|
LOW_BYTE (prop_addr, value)
|
||
|
store ((zword) (value & mask));
|
||
|
|
||
|
}/* z_get_next_prop */
|
||
|
|
||
|
/*
|
||
|
* z_get_parent, store the parent of an object.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_get_parent (void)
|
||
|
{
|
||
|
zword obj_addr;
|
||
|
|
||
|
/* If we are monitoring object locating display a short note */
|
||
|
|
||
|
if (f_setup.object_locating) {
|
||
|
stream_mssg_on ();
|
||
|
print_string ("@get_parent ");
|
||
|
print_object (zargs[0]);
|
||
|
stream_mssg_off ();
|
||
|
}
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_GET_PARENT_0);
|
||
|
store (0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
obj_addr = object_address (zargs[0]);
|
||
|
|
||
|
if (h_version <= V3) {
|
||
|
|
||
|
zbyte parent;
|
||
|
|
||
|
/* Get parent id from object */
|
||
|
|
||
|
obj_addr += O1_PARENT;
|
||
|
LOW_BYTE (obj_addr, parent)
|
||
|
|
||
|
/* Store parent */
|
||
|
|
||
|
store (parent);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
zword parent;
|
||
|
|
||
|
/* Get parent id from object */
|
||
|
|
||
|
obj_addr += O4_PARENT;
|
||
|
LOW_WORD (obj_addr, parent)
|
||
|
|
||
|
/* Store parent */
|
||
|
|
||
|
store (parent);
|
||
|
|
||
|
}
|
||
|
|
||
|
}/* z_get_parent */
|
||
|
|
||
|
/*
|
||
|
* z_get_prop, store the value of an object property.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
* zargs[1] = number of property to be examined
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_get_prop (void)
|
||
|
{
|
||
|
zword prop_addr;
|
||
|
zword wprop_val;
|
||
|
zbyte bprop_val;
|
||
|
zbyte value;
|
||
|
zbyte mask;
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_GET_PROP_0);
|
||
|
store (0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Property id is in bottom five (six) bits */
|
||
|
|
||
|
mask = (h_version <= V3) ? 0x1f : 0x3f;
|
||
|
|
||
|
/* Load address of first property */
|
||
|
|
||
|
prop_addr = first_property (zargs[0]);
|
||
|
|
||
|
/* Scan down the property list */
|
||
|
|
||
|
for (;;) {
|
||
|
LOW_BYTE (prop_addr, value)
|
||
|
if ((value & mask) <= zargs[1])
|
||
|
break;
|
||
|
prop_addr = next_property (prop_addr);
|
||
|
}
|
||
|
|
||
|
if ((value & mask) == zargs[1]) { /* property found */
|
||
|
|
||
|
/* Load property (byte or word sized) */
|
||
|
|
||
|
prop_addr++;
|
||
|
|
||
|
if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
|
||
|
|
||
|
LOW_BYTE (prop_addr, bprop_val)
|
||
|
wprop_val = bprop_val;
|
||
|
|
||
|
} else LOW_WORD (prop_addr, wprop_val)
|
||
|
|
||
|
} else { /* property not found */
|
||
|
|
||
|
/* Load default value */
|
||
|
|
||
|
prop_addr = h_objects + 2 * (zargs[1] - 1);
|
||
|
LOW_WORD (prop_addr, wprop_val)
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Store the property value */
|
||
|
|
||
|
store (wprop_val);
|
||
|
|
||
|
}/* z_get_prop */
|
||
|
|
||
|
/*
|
||
|
* z_get_prop_addr, store the address of an object property.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
* zargs[1] = number of property to be examined
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_get_prop_addr (void)
|
||
|
{
|
||
|
zword prop_addr;
|
||
|
zbyte value;
|
||
|
zbyte mask;
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_GET_PROP_ADDR_0);
|
||
|
store (0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (story_id == BEYOND_ZORK)
|
||
|
if (zargs[0] > MAX_OBJECT)
|
||
|
{ store (0); return; }
|
||
|
|
||
|
/* Property id is in bottom five (six) bits */
|
||
|
|
||
|
mask = (h_version <= V3) ? 0x1f : 0x3f;
|
||
|
|
||
|
/* Load address of first property */
|
||
|
|
||
|
prop_addr = first_property (zargs[0]);
|
||
|
|
||
|
/* Scan down the property list */
|
||
|
|
||
|
for (;;) {
|
||
|
LOW_BYTE (prop_addr, value)
|
||
|
if ((value & mask) <= zargs[1])
|
||
|
break;
|
||
|
prop_addr = next_property (prop_addr);
|
||
|
}
|
||
|
|
||
|
/* Calculate the property address or return zero */
|
||
|
|
||
|
if ((value & mask) == zargs[1]) {
|
||
|
|
||
|
if (h_version >= V4 && (value & 0x80))
|
||
|
prop_addr++;
|
||
|
store ((zword) (prop_addr + 1));
|
||
|
|
||
|
} else store (0);
|
||
|
|
||
|
}/* z_get_prop_addr */
|
||
|
|
||
|
/*
|
||
|
* z_get_prop_len, store the length of an object property.
|
||
|
*
|
||
|
* zargs[0] = address of property to be examined
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_get_prop_len (void)
|
||
|
{
|
||
|
zword addr;
|
||
|
zbyte value;
|
||
|
|
||
|
/* Back up the property pointer to the property id */
|
||
|
|
||
|
addr = zargs[0] - 1;
|
||
|
LOW_BYTE (addr, value)
|
||
|
|
||
|
/* Calculate length of property */
|
||
|
|
||
|
if (h_version <= V3)
|
||
|
value = (value >> 5) + 1;
|
||
|
else if (!(value & 0x80))
|
||
|
value = (value >> 6) + 1;
|
||
|
else {
|
||
|
|
||
|
value &= 0x3f;
|
||
|
|
||
|
if (value == 0) value = 64; /* demanded by Spec 1.0 */
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Store length of property */
|
||
|
|
||
|
store (value);
|
||
|
|
||
|
}/* z_get_prop_len */
|
||
|
|
||
|
/*
|
||
|
* z_get_sibling, store the sibling of an object.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_get_sibling (void)
|
||
|
{
|
||
|
zword obj_addr;
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_GET_SIBLING_0);
|
||
|
store (0);
|
||
|
branch (FALSE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
obj_addr = object_address (zargs[0]);
|
||
|
|
||
|
if (h_version <= V3) {
|
||
|
|
||
|
zbyte sibling;
|
||
|
|
||
|
/* Get sibling id from object */
|
||
|
|
||
|
obj_addr += O1_SIBLING;
|
||
|
LOW_BYTE (obj_addr, sibling)
|
||
|
|
||
|
/* Store sibling and branch */
|
||
|
|
||
|
store (sibling);
|
||
|
branch (sibling);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
zword sibling;
|
||
|
|
||
|
/* Get sibling id from object */
|
||
|
|
||
|
obj_addr += O4_SIBLING;
|
||
|
LOW_WORD (obj_addr, sibling)
|
||
|
|
||
|
/* Store sibling and branch */
|
||
|
|
||
|
store (sibling);
|
||
|
branch (sibling);
|
||
|
|
||
|
}
|
||
|
|
||
|
}/* z_get_sibling */
|
||
|
|
||
|
/*
|
||
|
* z_insert_obj, make an object the first child of another object.
|
||
|
*
|
||
|
* zargs[0] = object to be moved
|
||
|
* zargs[1] = destination object
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_insert_obj (void)
|
||
|
{
|
||
|
zword obj1 = zargs[0];
|
||
|
zword obj2 = zargs[1];
|
||
|
zword obj1_addr;
|
||
|
zword obj2_addr;
|
||
|
|
||
|
/* If we are monitoring object movements display a short note */
|
||
|
|
||
|
if (f_setup.object_movement) {
|
||
|
stream_mssg_on ();
|
||
|
print_string ("@move_obj ");
|
||
|
print_object (obj1);
|
||
|
print_string (" ");
|
||
|
print_object (obj2);
|
||
|
stream_mssg_off ();
|
||
|
}
|
||
|
|
||
|
if (obj1 == 0) {
|
||
|
runtime_error (ERR_MOVE_OBJECT_0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (obj2 == 0) {
|
||
|
runtime_error (ERR_MOVE_OBJECT_TO_0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Get addresses of both objects */
|
||
|
|
||
|
obj1_addr = object_address (obj1);
|
||
|
obj2_addr = object_address (obj2);
|
||
|
|
||
|
/* Remove object 1 from current parent */
|
||
|
|
||
|
unlink_object (obj1);
|
||
|
|
||
|
/* Make object 1 first child of object 2 */
|
||
|
|
||
|
if (h_version <= V3) {
|
||
|
|
||
|
zbyte child;
|
||
|
|
||
|
obj1_addr += O1_PARENT;
|
||
|
SET_BYTE (obj1_addr, obj2)
|
||
|
obj2_addr += O1_CHILD;
|
||
|
LOW_BYTE (obj2_addr, child)
|
||
|
SET_BYTE (obj2_addr, obj1)
|
||
|
obj1_addr += O1_SIBLING - O1_PARENT;
|
||
|
SET_BYTE (obj1_addr, child)
|
||
|
|
||
|
} else {
|
||
|
|
||
|
zword child;
|
||
|
|
||
|
obj1_addr += O4_PARENT;
|
||
|
SET_WORD (obj1_addr, obj2)
|
||
|
obj2_addr += O4_CHILD;
|
||
|
LOW_WORD (obj2_addr, child)
|
||
|
SET_WORD (obj2_addr, obj1)
|
||
|
obj1_addr += O4_SIBLING - O4_PARENT;
|
||
|
SET_WORD (obj1_addr, child)
|
||
|
|
||
|
}
|
||
|
|
||
|
}/* z_insert_obj */
|
||
|
|
||
|
/*
|
||
|
* z_put_prop, set the value of an object property.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
* zargs[1] = number of property to set
|
||
|
* zargs[2] = value to set property to
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_put_prop (void)
|
||
|
{
|
||
|
zword prop_addr;
|
||
|
zword value;
|
||
|
zbyte mask;
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_PUT_PROP_0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Property id is in bottom five or six bits */
|
||
|
|
||
|
mask = (h_version <= V3) ? 0x1f : 0x3f;
|
||
|
|
||
|
/* Load address of first property */
|
||
|
|
||
|
prop_addr = first_property (zargs[0]);
|
||
|
|
||
|
/* Scan down the property list */
|
||
|
|
||
|
for (;;) {
|
||
|
LOW_BYTE (prop_addr, value)
|
||
|
if ((value & mask) <= zargs[1])
|
||
|
break;
|
||
|
prop_addr = next_property (prop_addr);
|
||
|
}
|
||
|
|
||
|
/* Exit if the property does not exist */
|
||
|
|
||
|
if ((value & mask) != zargs[1])
|
||
|
runtime_error (ERR_NO_PROP);
|
||
|
|
||
|
/* Store the new property value (byte or word sized) */
|
||
|
|
||
|
prop_addr++;
|
||
|
|
||
|
if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
|
||
|
zbyte v = zargs[2];
|
||
|
SET_BYTE (prop_addr, v)
|
||
|
} else {
|
||
|
zword v = zargs[2];
|
||
|
SET_WORD (prop_addr, v)
|
||
|
}
|
||
|
|
||
|
}/* z_put_prop */
|
||
|
|
||
|
/*
|
||
|
* z_remove_obj, unlink an object from its parent and siblings.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_remove_obj (void)
|
||
|
{
|
||
|
|
||
|
/* If we are monitoring object movements display a short note */
|
||
|
|
||
|
if (f_setup.object_movement) {
|
||
|
stream_mssg_on ();
|
||
|
print_string ("@remove_obj ");
|
||
|
print_object (zargs[0]);
|
||
|
stream_mssg_off ();
|
||
|
}
|
||
|
|
||
|
/* Call unlink_object to do the job */
|
||
|
|
||
|
unlink_object (zargs[0]);
|
||
|
|
||
|
}/* z_remove_obj */
|
||
|
|
||
|
/*
|
||
|
* z_set_attr, set an object attribute.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
* zargs[1] = number of attribute to set
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_set_attr (void)
|
||
|
{
|
||
|
zword obj_addr;
|
||
|
zbyte value;
|
||
|
|
||
|
if (story_id == SHERLOCK)
|
||
|
if (zargs[1] == 48)
|
||
|
return;
|
||
|
|
||
|
if (zargs[1] > ((h_version <= V3) ? 31 : 47))
|
||
|
runtime_error (ERR_ILL_ATTR);
|
||
|
|
||
|
/* If we are monitoring attribute assignment display a short note */
|
||
|
|
||
|
if (f_setup.attribute_assignment) {
|
||
|
stream_mssg_on ();
|
||
|
print_string ("@set_attr ");
|
||
|
print_object (zargs[0]);
|
||
|
print_string (" ");
|
||
|
print_num (zargs[1]);
|
||
|
stream_mssg_off ();
|
||
|
}
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_SET_ATTR_0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Get attribute address */
|
||
|
|
||
|
obj_addr = object_address (zargs[0]) + zargs[1] / 8;
|
||
|
|
||
|
/* Load attribute byte */
|
||
|
|
||
|
LOW_BYTE (obj_addr, value)
|
||
|
|
||
|
/* Set attribute bit */
|
||
|
|
||
|
value |= 0x80 >> (zargs[1] & 7);
|
||
|
|
||
|
/* Store attribute byte */
|
||
|
|
||
|
SET_BYTE (obj_addr, value)
|
||
|
|
||
|
}/* z_set_attr */
|
||
|
|
||
|
/*
|
||
|
* z_test_attr, branch if an object attribute is set.
|
||
|
*
|
||
|
* zargs[0] = object
|
||
|
* zargs[1] = number of attribute to test
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void z_test_attr (void)
|
||
|
{
|
||
|
zword obj_addr;
|
||
|
zbyte value;
|
||
|
|
||
|
if (zargs[1] > ((h_version <= V3) ? 31 : 47))
|
||
|
runtime_error (ERR_ILL_ATTR);
|
||
|
|
||
|
/* If we are monitoring attribute testing display a short note */
|
||
|
|
||
|
if (f_setup.attribute_testing) {
|
||
|
stream_mssg_on ();
|
||
|
print_string ("@test_attr ");
|
||
|
print_object (zargs[0]);
|
||
|
print_string (" ");
|
||
|
print_num (zargs[1]);
|
||
|
stream_mssg_off ();
|
||
|
}
|
||
|
|
||
|
if (zargs[0] == 0) {
|
||
|
runtime_error (ERR_TEST_ATTR_0);
|
||
|
branch (FALSE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Get attribute address */
|
||
|
|
||
|
obj_addr = object_address (zargs[0]) + zargs[1] / 8;
|
||
|
|
||
|
/* Load attribute byte */
|
||
|
|
||
|
LOW_BYTE (obj_addr, value)
|
||
|
|
||
|
/* Test attribute */
|
||
|
|
||
|
branch (value & (0x80 >> (zargs[1] & 7)));
|
||
|
|
||
|
}/* z_test_attr */
|