Initial commit
This commit is contained in:
670
engines/glk/hugo/heobject.cpp
Normal file
670
engines/glk/hugo/heobject.cpp
Normal file
@@ -0,0 +1,670 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/hugo/hugo.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Hugo {
|
||||
|
||||
#if defined (DEBUGGER)
|
||||
|
||||
int Hugo::CheckObjectRange(int obj) {
|
||||
if (runtime_warnings) {
|
||||
return CheckinRange((unsigned)obj, (unsigned)objects, "object");
|
||||
} else
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int Hugo::Child(int obj) {
|
||||
int c;
|
||||
|
||||
#if defined (DEBUGGER)
|
||||
if (!CheckObjectRange(obj)) return 0;
|
||||
#endif
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
defseg = objtable;
|
||||
|
||||
c = PeekWord(2 + obj*object_size + object_size - 4);
|
||||
|
||||
defseg = gameseg;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int Hugo::Children(int obj) {
|
||||
int count = 0;
|
||||
int nextobj;
|
||||
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
nextobj = Child(obj);
|
||||
while (nextobj)
|
||||
{count++;
|
||||
nextobj = Sibling(nextobj);}
|
||||
return count;
|
||||
}
|
||||
|
||||
int Hugo::Elder(int obj) {
|
||||
int lastobj;
|
||||
int p, cp;
|
||||
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
p = Parent(obj);
|
||||
cp = Child(p);
|
||||
|
||||
if (p==0 || cp==obj)
|
||||
return 0;
|
||||
|
||||
lastobj = cp;
|
||||
while (Sibling(lastobj) != obj)
|
||||
lastobj = Sibling(lastobj);
|
||||
|
||||
return lastobj;
|
||||
}
|
||||
|
||||
unsigned long Hugo::GetAttributes(int obj, int attribute_set) {
|
||||
unsigned long a;
|
||||
|
||||
defseg = objtable;
|
||||
|
||||
#if defined (DEBUGGER)
|
||||
if (!CheckObjectRange(obj)) return 0;
|
||||
#endif
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
a = (unsigned long)PeekWord(2 + obj*object_size + attribute_set*4)
|
||||
+ (unsigned long)PeekWord(2 + obj*object_size + attribute_set*4 + 2)*65536L;
|
||||
|
||||
defseg = gameseg;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
int Hugo::GetProp(int obj, int p, int n, char s) {
|
||||
char objonly, /* no verbroutine given in before, etc. */
|
||||
isadditive = 0, /* before, after, etc. */
|
||||
gotone = 0, /* when a match has been made */
|
||||
getpropaddress = 0; /* when getting &object.property */
|
||||
int i;
|
||||
int tempself,
|
||||
objtype, /* i.e., what we're matching to */
|
||||
flag = 0;
|
||||
int g = 0;
|
||||
int templocals[MAXLOCALS];
|
||||
int temp_stack_depth;
|
||||
char tempinexpr = inexpr;
|
||||
unsigned int pa, /* property address */
|
||||
offset = 0;
|
||||
long inprop, /* code position in complex property */
|
||||
returnaddr;
|
||||
#if defined (DEBUGGER)
|
||||
long orig_inprop;
|
||||
int tempdbnest;
|
||||
|
||||
/* Don't check a possible non-existent display object (-1) */
|
||||
if (obj!=-1 || display_object!=-1) CheckObjectRange(obj);
|
||||
#endif
|
||||
/* This way either -1 (the non-existent display object) or a too-high
|
||||
object will fail
|
||||
*/
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
/* The display object, which is automatically created by the compiler,
|
||||
did not exist pre-v2.4
|
||||
*/
|
||||
if ((obj==display_object) && game_version>=24)
|
||||
{
|
||||
/* There are no actual default "properties" per se on the
|
||||
display object--but reading them returns certain data about
|
||||
the current state of display affairs
|
||||
*/
|
||||
|
||||
/* no display.<prop> #2, etc. */
|
||||
if (n==1 && p<=pointer_y)
|
||||
{
|
||||
if (p==screenwidth)
|
||||
#if defined (GLK) && defined (ACTUAL_LINELENGTH)
|
||||
g = ACTUAL_LINELENGTH();
|
||||
#else
|
||||
g = SCREENWIDTH/FIXEDCHARWIDTH;
|
||||
#endif
|
||||
else if (p==screenheight)
|
||||
/* ACTUAL_SCREENHEIGHT can be set to a non-portable function if
|
||||
SCREENHEIGHT and SCREENHEIGHT have been set to large values in
|
||||
order to force the non-portable layer to handle wrapping and
|
||||
scrolling (as in the Glk port).
|
||||
*/
|
||||
#if defined (ACTUAL_SCREENHEIGHT)
|
||||
g = ACTUAL_SCREENHEIGHT();
|
||||
#else
|
||||
g = SCREENHEIGHT/FIXEDLINEHEIGHT;
|
||||
#endif
|
||||
else if (p==linelength)
|
||||
/* ACTUAL_LINELENGTH functions similarly to ACTUAL_SCREENWIDTH,
|
||||
above.
|
||||
*/
|
||||
#if defined (ACTUAL_LINELENGTH)
|
||||
g = ACTUAL_LINELENGTH();
|
||||
#else
|
||||
g = physical_windowwidth/FIXEDCHARWIDTH;
|
||||
#endif
|
||||
else if (p==windowlines)
|
||||
g = physical_windowheight/FIXEDLINEHEIGHT;
|
||||
else if (p==cursor_column)
|
||||
g = (currentpos+1+hugo_textwidth(pbuffer))/FIXEDCHARWIDTH;
|
||||
else if (p==cursor_row)
|
||||
g = currentline;
|
||||
else if (p==hasgraphics)
|
||||
g = hugo_hasgraphics();
|
||||
else if (p==title_caption)
|
||||
g = FindWord(game_title);
|
||||
else if (p==hasvideo)
|
||||
#if !defined (COMPILE_V25)
|
||||
g = hugo_hasvideo();
|
||||
#else
|
||||
g = 0;
|
||||
#endif
|
||||
else if (p==needs_repaint)
|
||||
g = display_needs_repaint;
|
||||
else if (p==pointer_x)
|
||||
g = display_pointer_x;
|
||||
else if (p==pointer_y)
|
||||
g = display_pointer_y;
|
||||
else
|
||||
g = 0;
|
||||
|
||||
return g;
|
||||
}
|
||||
}
|
||||
|
||||
/* To avoid prematurely getting an address in &obj.prop.prop */
|
||||
if (getaddress && MEM(codeptr)!=DECIMAL_T)
|
||||
getpropaddress = true;
|
||||
|
||||
tempself = var[self];
|
||||
if (!s) var[self] = obj;
|
||||
|
||||
temp_stack_depth = stack_depth;
|
||||
|
||||
|
||||
GetNextProp:
|
||||
|
||||
pa = PropAddr(obj, p, offset);
|
||||
|
||||
defseg = proptable;
|
||||
|
||||
/* If the object doesn't have property p, see if there's a
|
||||
default value.
|
||||
*/
|
||||
if (!pa)
|
||||
{
|
||||
if (offset) goto NoMorePropMatches;
|
||||
|
||||
if (getpropaddress) /* if an &value */
|
||||
g = 0;
|
||||
else
|
||||
g = PeekWord(p * 2 + 2);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
/* Property is a value... */
|
||||
if (Peek(pa+1) < PROP_ROUTINE)
|
||||
{
|
||||
if (getaddress || (int)Peek(pa+1) < n || n<=0)
|
||||
{
|
||||
#if defined (DEBUGGER)
|
||||
if (n!=1)
|
||||
CheckinRange(n, (int)Peek(pa+1), "property element");
|
||||
#endif
|
||||
g = 0;
|
||||
}
|
||||
else
|
||||
g = PeekWord(pa + n * 2);
|
||||
}
|
||||
|
||||
/* ...or a property routine */
|
||||
else
|
||||
{
|
||||
/* Check if this is an additive property */
|
||||
defseg = proptable;
|
||||
if (Peek(2 + Peek(0)*2 + p)&ADDITIVE_FLAG)
|
||||
isadditive = true;
|
||||
|
||||
/* If an &value, return the address of the
|
||||
property routine.
|
||||
*/
|
||||
if (getpropaddress)
|
||||
{
|
||||
g = PeekWord(pa+2);
|
||||
goto NoMorePropMatches;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined (DEBUGGER)
|
||||
if (debug_eval)
|
||||
{
|
||||
debug_eval_error = true;
|
||||
DebugMessageBox("Expression Error",
|
||||
"Property routine illegal in watch/assignment");
|
||||
defseg = gameseg;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/* If not a complex property such as
|
||||
before or after:
|
||||
*/
|
||||
if ((game_version>=22 && (Peek(2 + Peek(0)*2 + p)&COMPLEX_FLAG)==0) || (game_version<22 && p!=before && p!=after))
|
||||
{
|
||||
ret = 1;
|
||||
returnaddr = codeptr;
|
||||
|
||||
/* Check to see if this is a valid tail-recursive return... */
|
||||
if (tail_recursion==TAIL_RECURSION_PROPERTY && MEM(codeptr)==EOL_T)
|
||||
{
|
||||
PassLocals(0);
|
||||
tail_recursion_addr = (long)PeekWord(pa+2)*address_scale;
|
||||
return 0;
|
||||
}
|
||||
/* ...but if we're not immediately followed by and end-of-line marker,
|
||||
or another property value, cancel the pending tail-recursion
|
||||
*/
|
||||
else if (MEM(codeptr)!=DECIMAL_T)
|
||||
{
|
||||
tail_recursion = 0;
|
||||
}
|
||||
|
||||
for (i=0; i<MAXLOCALS; i++)
|
||||
templocals[i] = var[MAXGLOBALS+i];
|
||||
|
||||
PassLocals(0);
|
||||
|
||||
SetStackFrame(stack_depth+1, RUNROUTINE_BLOCK, 0, 0);
|
||||
#if defined (DEBUGGER)
|
||||
tempdbnest = dbnest;
|
||||
DebugRunRoutine((long)PeekWord(pa+2)*address_scale);
|
||||
dbnest = tempdbnest;
|
||||
#else
|
||||
RunRoutine((long)PeekWord(pa+2)*address_scale);
|
||||
#endif
|
||||
|
||||
retflag = 0;
|
||||
codeptr = returnaddr;
|
||||
g = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* Complex property: */
|
||||
else
|
||||
{
|
||||
for (i=0; i<MAXLOCALS; i++)
|
||||
templocals[i] = var[MAXGLOBALS+i];
|
||||
|
||||
inprop = (long)PeekWord(pa + 2)*address_scale;
|
||||
#ifdef DEBUGGER
|
||||
orig_inprop = inprop;
|
||||
#endif
|
||||
defseg = gameseg;
|
||||
while (Peek(inprop)!=CLOSE_BRACE_T)
|
||||
{
|
||||
returnaddr = codeptr;
|
||||
|
||||
codeptr = inprop;
|
||||
objonly = false;
|
||||
objtype = GetValue();
|
||||
inprop = codeptr;
|
||||
codeptr = returnaddr;
|
||||
|
||||
flag = 0;
|
||||
|
||||
/* If only an object (or other variable) is
|
||||
given, and no verbroutine
|
||||
*/
|
||||
if (Peek(inprop)==JUMP_T)
|
||||
{
|
||||
objonly = true;
|
||||
if (!gotone && obj==objtype) flag = 1;
|
||||
}
|
||||
|
||||
/* Otherwise, one or more verbroutines are
|
||||
specified
|
||||
*/
|
||||
else
|
||||
{
|
||||
while (Peek(inprop)!=JUMP_T)
|
||||
{
|
||||
if (PeekWord(inprop+1)==(unsigned int)var[verbroutine] ||
|
||||
|
||||
/* This is necessary because of the awkward way the pre-v2.2
|
||||
differentiated non-verbroutine blocks, i.e., with Parse
|
||||
*/
|
||||
((game_version<22) && PeekWord(inprop+1)==(unsigned int)parseaddr && !gotone))
|
||||
{
|
||||
if (obj==objtype) flag = 1;
|
||||
}
|
||||
inprop += 3;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag==1)
|
||||
{
|
||||
gotone = true;
|
||||
ret = 1;
|
||||
returnaddr = codeptr;
|
||||
|
||||
SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
|
||||
|
||||
PassLocals(0);
|
||||
#if defined (DEBUGGER)
|
||||
/* Prevent premature stopping */
|
||||
if (debugger_step_over && !debugger_finish)
|
||||
debugger_run = true;
|
||||
|
||||
if (IsBreakpoint(orig_inprop))
|
||||
complex_prop_breakpoint = true;
|
||||
|
||||
Common::sprintf_s(debug_line, "Calling: %s.%s", objectname[obj], propertyname[p]);
|
||||
trace_complex_prop_routine = true;
|
||||
|
||||
tempdbnest = dbnest;
|
||||
DebugRunRoutine(inprop+3);
|
||||
dbnest = tempdbnest;
|
||||
#else
|
||||
RunRoutine(inprop+3);
|
||||
#endif
|
||||
retflag = 0;
|
||||
codeptr = returnaddr;
|
||||
g = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* The following used to read "if (!flag || objonly..."
|
||||
meaning that any non-verbroutine related routines
|
||||
would fall through regardless of whether they returned
|
||||
true or false. I don't recall the rationale for this,
|
||||
and have therefore removed it.
|
||||
*/
|
||||
if (!flag || (objonly && !g) || ((game_version<22) && PeekWord(inprop-2)==(unsigned int)parseaddr))
|
||||
inprop = (long)PeekWord(inprop+1)*address_scale;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<MAXLOCALS; i++)
|
||||
var[MAXGLOBALS+i] = templocals[i];
|
||||
|
||||
if (isadditive && !g)
|
||||
{
|
||||
offset = pa + 4;
|
||||
gotone = false;
|
||||
goto GetNextProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NoMorePropMatches:
|
||||
|
||||
defseg = gameseg;
|
||||
|
||||
var[self] = tempself;
|
||||
inexpr = tempinexpr;
|
||||
stack_depth = temp_stack_depth;
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
int Hugo::GrandParent(int obj) {
|
||||
int nextobj;
|
||||
|
||||
#if defined (DEBUGGER)
|
||||
if (!CheckObjectRange(obj)) return 0;
|
||||
#endif
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
defseg = objtable;
|
||||
while ((nextobj = PeekWord(2 + obj*object_size + object_size-8)) != 0)
|
||||
obj = nextobj;
|
||||
defseg = gameseg;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Hugo::MoveObj(int obj, int p) {
|
||||
int oldparent, prevobj, s;
|
||||
unsigned int objaddr, parentaddr, lastobjaddr;
|
||||
|
||||
if (obj==p) return;
|
||||
if (obj<0 || obj>=objects) return;
|
||||
|
||||
oldparent = Parent(obj);
|
||||
/* if (oldparent==p) return; */
|
||||
|
||||
objaddr = 2 + obj*object_size;
|
||||
|
||||
/* First, detach the object from its old parent and siblings... */
|
||||
|
||||
prevobj = Elder(obj);
|
||||
s = Sibling(obj);
|
||||
defseg = objtable;
|
||||
if (prevobj) /* sibling */
|
||||
PokeWord(2 + prevobj*object_size + object_size-6, s);
|
||||
else /* child */
|
||||
PokeWord(2 + oldparent*object_size + object_size-4, s);
|
||||
|
||||
|
||||
/* Then move it to the new parent... */
|
||||
|
||||
defseg = objtable;
|
||||
PokeWord(objaddr + object_size-8, p); /* new parent */
|
||||
PokeWord(objaddr + object_size-6, 0); /* erase old sibling */
|
||||
|
||||
/* Only operate on the new parent if it isn't object 0 */
|
||||
if (p!=0)
|
||||
{
|
||||
|
||||
/* Object is sole child, or... */
|
||||
if (Child(p)==0)
|
||||
{
|
||||
parentaddr = 2 + p*object_size;
|
||||
defseg = objtable;
|
||||
PokeWord(parentaddr + object_size-4, obj);
|
||||
}
|
||||
|
||||
/* ...object is next sibling. */
|
||||
else
|
||||
{
|
||||
lastobjaddr = 2 + Youngest(p)*object_size;
|
||||
defseg = objtable;
|
||||
PokeWord(lastobjaddr + object_size-6, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *Hugo::Name(int obj) {
|
||||
int p;
|
||||
|
||||
p = GetProp(obj, 0, 1, 0);
|
||||
|
||||
if (p)
|
||||
return GetWord((unsigned int)p);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int Hugo::Parent(int obj) {
|
||||
int p;
|
||||
|
||||
#if defined (DEBUGGER)
|
||||
if (!CheckObjectRange(obj)) return 0;
|
||||
#endif
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
defseg = objtable;
|
||||
|
||||
p = PeekWord(2 + obj*object_size + object_size-8);
|
||||
|
||||
defseg = gameseg;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
unsigned int Hugo::PropAddr(int obj, int p, unsigned int offset) {
|
||||
unsigned char c;
|
||||
int proplen;
|
||||
unsigned int ptr;
|
||||
|
||||
#if defined (DEBUGGER)
|
||||
/* Don't check any non-existent display object (-1) */
|
||||
if (p!=-1) CheckinRange(p, properties, "property");
|
||||
CheckObjectRange(obj);
|
||||
#endif
|
||||
/* This way either -1 (the non-existent display object) or a too-high
|
||||
object will fail
|
||||
*/
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
defseg = objtable;
|
||||
|
||||
/* Position in the property table...
|
||||
|
||||
i.e., ptr = PeekWord(2 + obj*object_size + (object_size-2));
|
||||
*/
|
||||
ptr = PeekWord(object_size*(obj+1));
|
||||
|
||||
/* ...unless a position has already been given */
|
||||
if (offset) ptr = offset;
|
||||
|
||||
defseg = proptable;
|
||||
|
||||
c = Peek(ptr);
|
||||
while (c != PROP_END && c != (unsigned char)p)
|
||||
{
|
||||
proplen = Peek(ptr + 1);
|
||||
|
||||
/* Property routine address is 1 word */
|
||||
if (proplen==PROP_ROUTINE) proplen = 1;
|
||||
|
||||
ptr += proplen * 2 + 2;
|
||||
c = Peek(ptr);
|
||||
}
|
||||
|
||||
defseg = gameseg;
|
||||
|
||||
if (c==PROP_END)
|
||||
return 0;
|
||||
else
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Hugo::PutAttributes(int obj, unsigned long a, int attribute_set) {
|
||||
unsigned int lword, hword;
|
||||
|
||||
hword = (unsigned int)(a/65536L);
|
||||
lword = (unsigned int)(a%65536L);
|
||||
|
||||
defseg = objtable;
|
||||
|
||||
PokeWord(2 + obj*object_size + attribute_set*4, lword);
|
||||
PokeWord(2 + obj*object_size + attribute_set*4 + 2, hword);
|
||||
|
||||
defseg = gameseg;
|
||||
}
|
||||
|
||||
void Hugo::SetAttribute(int obj, int attr, int c) {
|
||||
unsigned long a, mask;
|
||||
|
||||
#if defined (DEBUGGER)
|
||||
if (!CheckObjectRange(obj)) return;
|
||||
#endif
|
||||
if (obj<0 || obj>=objects) return;
|
||||
|
||||
a = GetAttributes(obj, attr/32);
|
||||
|
||||
mask = 1L<<(long)(attr%32);
|
||||
|
||||
if (c==1)
|
||||
a = a | mask;
|
||||
else
|
||||
{
|
||||
if (a & mask)
|
||||
a = a ^ mask;
|
||||
}
|
||||
|
||||
PutAttributes(obj, a, attr/32);
|
||||
}
|
||||
|
||||
int Hugo::Sibling(int obj) {
|
||||
int s;
|
||||
|
||||
#if defined (DEBUGGER)
|
||||
if (!CheckObjectRange(obj)) return 0;
|
||||
#endif
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
defseg = objtable;
|
||||
|
||||
s = PeekWord(2+ obj*object_size + object_size-6);
|
||||
|
||||
defseg = gameseg;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int Hugo::TestAttribute(int obj, int attr, int nattr) {
|
||||
unsigned long a, mask, ta;
|
||||
|
||||
#if defined (DEBUGGER)
|
||||
if (!CheckObjectRange(obj)) return 0;
|
||||
#endif
|
||||
if (obj<0 || obj>=objects) return 0;
|
||||
|
||||
a = GetAttributes(obj, attr/32);
|
||||
|
||||
mask = 1L<<(attr%32);
|
||||
|
||||
ta = a & mask;
|
||||
if (ta) ta = 1;
|
||||
|
||||
if (nattr) ta = ta ^ 1;
|
||||
|
||||
return (int)ta;
|
||||
}
|
||||
|
||||
int Hugo::Youngest(int obj) {
|
||||
int nextobj;
|
||||
|
||||
if (Child(obj)==0) return 0;
|
||||
|
||||
nextobj = Child(obj);
|
||||
|
||||
while (Sibling(nextobj))
|
||||
nextobj = Sibling(nextobj);
|
||||
|
||||
return nextobj;
|
||||
}
|
||||
|
||||
} // End of namespace Hugo
|
||||
} // End of namespace Glk
|
||||
Reference in New Issue
Block a user