aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/hugo/heobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/hugo/heobject.cpp')
-rw-r--r--engines/glk/hugo/heobject.cpp671
1 files changed, 671 insertions, 0 deletions
diff --git a/engines/glk/hugo/heobject.cpp b/engines/glk/hugo/heobject.cpp
new file mode 100644
index 0000000000..cc1416d98d
--- /dev/null
+++ b/engines/glk/hugo/heobject.cpp
@@ -0,0 +1,671 @@
+/* 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/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;
+
+ sprintf(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);
+ }
+ }
+}
+
+char *Hugo::Name(int obj) {
+ int p;
+
+ p = GetProp(obj, 0, 1, 0);
+
+ if (p)
+ return GetWord((unsigned int)p);
+ else
+ return 0;
+}
+
+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