muddle/src/object.c

150 lines
4.2 KiB
C

/*
* Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "object.h"
#include "story.h"
#include "portme.h"
#include "messages.h"
#include "memory.h"
uint32_t objectPointerGet(uint16_t objectId) {
uint32_t ptr;
if (objectId == 0) portDie(MSG_INT_OBJECT0_REF);
if ((storyVersion() <= 3) && (objectId > 255)) portDie(MSG_INT_OBJECT_BAD_ID);
ptr = storyObjectTableAddress();
ptr += 31 * 2; // Skip properties defaults table.
ptr += 9 * (objectId - 1); // Find object in table.
return ptr;
}
uint32_t objectPointerParentGet(uint32_t objectPointer) {
uint32_t result = 0;
uint32_t parent;
if (storyVersion() <= 3) {
parent = ZPEEK(objectPointer + 4);
result = parent ? objectPointerGet(parent) : 0;
} else {
portDie(MSG_UNIMPLEMENTED);
}
return result;
}
uint16_t objectPropertyDefaultGet(uint32_t propertyId) {
uint32_t values;
if (((storyVersion() <= 3) && (propertyId > 31)) || ((storyVersion() >= 4) && (propertyId > 63))) {
//***TODO*** Should we die here? This seems to work.
return 0;
}
values = storyObjectTableAddress() + ((propertyId - 1) * 2);
return ZPEEKW(values);
}
uint32_t objectPropertyGet(uint16_t objectId, uint32_t propertyId, uint8_t *size) {
uint32_t ptr = objectPointerGet(objectId);
uint16_t addr;
uint8_t info;
uint16_t num;
uint8_t s;
if (storyVersion() <= 3) {
ptr += 7; // Skip to properties address field.
addr = ZPEEKW(ptr);
ptr = addr;
ptr += ZPEEK(ptr) * 2 + 1; // Skip object name to start of properties.
while (1) {
info = ZPEEK(ptr++);
num = (info & 0x1f); // 5 bits for property ID.
s = ((info >> 5) & 0x7) + 1; // 3 bits for property size.
// These go in descending numeric order, and should fail the interpreter if missing.
// We use 0xFFFFFFFF internally to mean "first property".
if ((num == propertyId) || (propertyId == 0xFFFFFFFF)) { // found it?
if (size) *size = s;
return ptr;
} else if (num < propertyId) // we're past it.
break;
// Try the next property.
ptr += s;
}
} else {
portDie(MSG_UNIMPLEMENTED);
}
return 0;
}
uint16_t objectRelationGet(uint16_t objectId, uint8_t relationship) {
uint32_t objPtr = objectPointerGet(objectId);
uint16_t result = 0;
if (storyVersion() <= 3) {
result = ZPEEK(objPtr + relationship);
} else {
portDie(MSG_UNIMPLEMENTED);
}
return result;
}
void objectUnparent(uint16_t objectId) {
uint32_t objPtr = objectPointerGet(objectId);
uint32_t parentPtr = objectPointerParentGet(objPtr);
uint32_t ptr;
if (parentPtr != 0) {
if (storyVersion() <= 3) {
ptr = parentPtr + 6; // 4 to skip attrs, 2 to skip to child.
#ifdef DEBUGGING
printf("Checking %X (%d) for %d\n", (unsigned int)ptr, ZPEEK(ptr), objectId);
#endif
while (ZPEEK(ptr) != objectId) { // If not direct child, look through sibling list..
ptr = objectPointerGet(ZPEEK(ptr)) + 5;
#ifdef DEBUGGING
printf("Checking %X (%d) for %d\n", (unsigned int)ptr, ZPEEK(ptr), objectId);
#endif
}
ZPOKE(ptr, ZPEEK(objPtr + 5)); // obj sibling takes obj's place.
#ifdef DEBUGGING
printf("Setting %X to %d from %X\n", (unsigned int)ptr, ZPEEK(objPtr + 5), objPtr + 5);
#endif
} else {
portDie(MSG_UNIMPLEMENTED);
}
}
}