diff options
Diffstat (limited to 'engines/glk/hugo/heobject.cpp')
-rw-r--r-- | engines/glk/hugo/heobject.cpp | 671 |
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 |