Index: class.c ================================================================== --- class.c +++ class.c @@ -1056,10 +1056,17 @@ AddInst2(OP_NEXT,flowptr[flowdepth]); cl->codes[flowptr[flowdepth]]=peep=ptr; break; case OP_STRING: AddInst2(OP_STRING,pool_string(tokenstr)); + break; + case OP_MBEGIN: + FlowPush(OP_BEGIN); + AddInst(OP_TMARK); + AddInst(OP_IF); + FlowPush(OP_WHILE); + peep=++ptr; break; default: if(Tokenf(TF_ABNORMAL)) ParseError("Invalid instruction token\n"); if(compat && Tokenf(TF_COMPAT) && Tokenf(TF_EQUAL)) ++tokenv; AddInstF(tokenv,tokent); Index: class.doc ================================================================== --- class.doc +++ class.doc @@ -414,10 +414,12 @@ * String: A string in quotation marks. There are no string manipulation functions; the only thing that can be done with a string is to display it. * Sound: A named sound effect. Values of this type cannot be compared with anything, even other values of the same type. + +* Mark: There is only one value of this type. Some things are not their own types, and are other uses of numbers: * Null: A null class or null object is represented as zero. @@ -912,10 +914,13 @@ Unsigned divide in1 by in2 producing the quotient. ,/ ( in1 in2 -- out ) Signed divide in1 by in2 producing the quotient. +_ ( -- mark ) + Add a mark to the stack. + Animate ( flag start end delay -- ) ** Start or stop an animation for this object. This also sets the Image variable equal to the start value. The flag can be STOP to stop an animation, ONCE to play the animation once (and to queue a LASTIMAGE event if the animation isn't changed before that happens; note that @@ -1076,10 +1081,14 @@ EKS Hero Mesh they will not break replayability; the key to dismiss a non-quiz popup is not entered into the replay list, and the key to dismiss a quiz popup will be treated as a non-quiz input. IgnoreKey also causes the rest of the turn after the input phase to be skipped. +in ( x mark ... - bool ) + Check if x is equal to any of the values above the mark; equality is + tested the same as the "eq" instruction. + IntMove ( dir -- bool ) ** Similar to Move but do not initialize Inertia at all; use the current value of Inertia instead. ,IntMove ( obj dir -- bool ) ** @@ -1220,10 +1229,14 @@ NewX and NewY together. NewY ( oldy dir -- newy ) Advance the number in the direction as though it is a Y coordinate. +nin ( x mark ... - bool ) + Check if x is unequal to any of the values above the mark; this is the + same as "in lnot". + nip ( x y -- y ) o? ( any -- bool ) True if the value is an object (whether or not it has been garbage collected), or false for any other type. Error if it is a sound. @@ -1360,10 +1373,14 @@ Synchronize ( slot startimage -- ) ** Start a synchronized animation. Give the slot number of the animation, and the starting image number. The length and speed are defined in a global definition, and the animation is always a non-oscillating loop. +tmark ( any -- any true | false ) + If the input is a mark, then it is removed and the result is false. + Otherwise, the value remains and true is added above it on the stack. + Trace ( obj arg1 arg2 -- ) If tracing is enabled, sends the three values and some other information on stdout. If tracing is disabled, does nothing. This is intended to be used for debugging class codes. @@ -1426,10 +1443,14 @@ if [el if ...] [else ] then ( bool -- ) Takes a boolean value from the stack. If true, then is executed; otherwise it tries the other parts; "el" means to execute and take another boolean, and continue; "else" means always do this if none of the previous parts match. + +mbegin repeat ( -- any ) ( -- ) + The "mbegin" instruction is a shortcut for "begin tmark while", and it has + the same behaviour. === Substitution codes === %c Index: exec.c ================================================================== --- exec.c +++ exec.c @@ -1252,10 +1252,20 @@ } if(!i) return 0; if(current_key && !v_bool(msgvars.arg3) && !(keymask[i>>3]&(1<<(i&7)))) key_ignored=all_flushed=1; return code[i]; } + +static int v_in(void) { + int p=vstackptr; + Value v; + while(vstackptr-- && vstack[vstackptr].t!=TY_MARK); + if(!vstackptr) Throw("No mark"); + v=Pop(); + while(--p>vstackptr) if(v_equal(v,vstack[p])) return 1; + return 0; +} // Here is where the execution of a Free Hero Mesh bytecode subroutine is executed. #define NoIgnore() do{ changed=1; }while(0) #define GetVariableOf(a,b) (i=v_object(Pop()),i==VOIDLINK?NVALUE(0):b(objects[i]->a)) #define GetVariableOrAttributeOf(a,b) (t2=Pop(),t2.t==TY_CLASS?NVALUE(classes[t2.u]->a):(i=v_object(t2),i==VOIDLINK?NVALUE(0):b(objects[i]->a))) @@ -1427,10 +1437,11 @@ case OP_IGNOREKEY: if(current_key) key_ignored=all_flushed=1; break; case OP_IMAGE: StackReq(0,1); Push(NVALUE(o->image)); break; case OP_IMAGE_C: StackReq(1,1); Push(GetVariableOf(image,NVALUE)); break; case OP_IMAGE_E: NoIgnore(); StackReq(1,0); t1=Pop(); Numeric(t1); o->image=t1.u; break; case OP_IMAGE_EC: NoIgnore(); StackReq(2,0); t1=Pop(); Numeric(t1); i=v_object(Pop()); if(i!=VOIDLINK) objects[i]->image=t1.u; break; + case OP_IN: StackReq(2,1); i=v_in(); Push(NVALUE(i?1:0)); break; case OP_INERTIA: StackReq(0,1); Push(NVALUE(o->inertia)); break; case OP_INERTIA_C: StackReq(1,1); Push(GetVariableOf(inertia,NVALUE)); break; case OP_INERTIA_E: StackReq(1,0); t1=Pop(); Numeric(t1); o->inertia=t1.u; break; case OP_INERTIA_E16: StackReq(1,0); t1=Pop(); Numeric(t1); o->inertia=t1.u&0xFFFF; break; case OP_INERTIA_EC: StackReq(2,0); t1=Pop(); Numeric(t1); i=v_object(Pop()); if(i!=VOIDLINK) objects[i]->inertia=t1.u; break; @@ -1467,10 +1478,11 @@ case OP_LOSELEVEL: gameover=-1; Throw(0); break; case OP_LSH: StackReq(2,1); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); Push(NVALUE(t2.u&~31?0:t1.u<t1.u) Throw("Inventory overflow"); break; case OP_MOD: StackReq(2,1); t2=Pop(); DivideBy(t2); t1=Pop(); Numeric(t1); Push(NVALUE(t1.u%t2.u)); break; case OP_MOD_C: StackReq(2,1); t2=Pop(); DivideBy(t2); t1=Pop(); Numeric(t1); Push(NVALUE(t1.s%t2.s)); break; case OP_MOVE: NoIgnore(); StackReq(1,1); t1=Pop(); Numeric(t1); o->inertia=o->strength; Push(NVALUE(move_dir(obj,obj,t1.u))); break; case OP_MOVE_C: NoIgnore(); StackReq(2,1); t1=Pop(); Numeric(t1); i=v_object(Pop()); if(i==VOIDLINK) Push(NVALUE(0)); else { objects[i]->inertia=o->strength; Push(NVALUE(move_dir(obj,i,t1.u))); } break; @@ -1496,10 +1508,11 @@ case OP_NEG: StackReq(1,1); t1=Pop(); Numeric(t1); t1.s=-t1.s; Push(t1); break; case OP_NEWX: StackReq(2,1); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); Push(NVALUE(new_x(t1.u,t2.u))); break; case OP_NEWXY: StackReq(3,1); t3=Pop(); Numeric(t3); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); Push(NVALUE(new_x(t1.u,t3.u))); Push(NVALUE(new_y(t2.u,t3.u))); break; case OP_NEWY: StackReq(2,1); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); Push(NVALUE(new_y(t1.u,t2.u))); break; case OP_NEXT: StackReq(0,1); ptr=v_next(code,ptr); break; + case OP_NIN: StackReq(2,1); i=v_in(); Push(NVALUE(i?0:1)); break; case OP_NIP: StackReq(2,1); t1=Pop(); Pop(); Push(t1); break; case OP_OBJABOVE: StackReq(0,1); i=obj_above(obj); Push(OVALUE(i)); break; case OP_OBJABOVE_C: StackReq(1,1); i=obj_above(v_object(Pop())); Push(OVALUE(i)); break; case OP_OBJBELOW: StackReq(0,1); i=obj_below(obj); Push(OVALUE(i)); break; case OP_OBJBELOW_C: StackReq(1,1); i=obj_below(v_object(Pop())); Push(OVALUE(i)); break; @@ -1571,10 +1584,11 @@ case OP_TEMPERATURE_C: StackReq(1,1); Push(GetVariableOrAttributeOf(temperature,NVALUE)); break; case OP_TEMPERATURE_E: NoIgnore(); StackReq(1,0); t1=Pop(); Numeric(t1); o->temperature=t1.u; break; case OP_TEMPERATURE_E16: NoIgnore(); StackReq(1,0); t1=Pop(); Numeric(t1); o->temperature=t1.u&0xFFFF; break; case OP_TEMPERATURE_EC: NoIgnore(); StackReq(2,0); t1=Pop(); Numeric(t1); i=v_object(Pop()); if(i!=VOIDLINK) objects[i]->temperature=t1.u; break; case OP_TEMPERATURE_EC16: NoIgnore(); StackReq(2,0); t1=Pop(); Numeric(t1); i=v_object(Pop()); if(i!=VOIDLINK) objects[i]->temperature=t1.u; break; + case OP_TMARK: StackReq(1,2); t1=Pop(); if(t1.t==TY_MARK) { Push(NVALUE(0)); } else { Push(t1); Push(NVALUE(1)); } break; case OP_TRACE: StackReq(3,0); trace_stack(obj); break; case OP_TUCK: StackReq(2,3); t2=Pop(); t1=Pop(); Push(t2); Push(t1); Push(t2); break; case OP_USERSIGNAL: StackReq(0,1); if(o->oflags&OF_USERSIGNAL) Push(NVALUE(1)); else Push(NVALUE(0)); break; case OP_USERSIGNAL_C: StackReq(1,1); GetFlagOf(OF_USERSIGNAL); break; case OP_USERSIGNAL_E: NoIgnore(); StackReq(1,0); if(v_bool(Pop())) o->oflags|=OF_USERSIGNAL; else o->oflags&=~OF_USERSIGNAL; break; Index: function.c ================================================================== --- function.c +++ function.c @@ -164,10 +164,11 @@ [TY_MESSAGE]="message", [TY_LEVELSTRING]="string", [TY_STRING]="string", [TY_SOUND]="sound", [TY_USOUND]="sound", + [TY_MARK]="mark", }; int i; if(sqlite3_value_type(*argv)!=SQLITE_INTEGER) return; i=sqlite3_value_int64(*argv)>>32; sqlite3_result_text(cxt,i<0||i>TY_MAXTYPE?"object":n[i]?:"???",-1,SQLITE_STATIC); Index: game.c ================================================================== --- game.c +++ game.c @@ -291,10 +291,13 @@ draw_text(200,y,"",0xF0,0xF9); break; case TY_SOUND: case TY_USOUND: draw_text(200,y,"",0xF0,0xF6); break; + case TY_MARK: + draw_text(200,y,"",0xF0,0xF3); + break; default: snprintf(buf,80,"<%lu:%lu>",(long)v.u,(long)v.t); draw_text(200,y,buf,0xF0,0xFA); i=strlen(buf)*8+208; if(v.ugeneration==v.t) { Index: heromesh.h ================================================================== --- heromesh.h +++ heromesh.h @@ -13,10 +13,11 @@ #define TY_LEVELSTRING 3 #define TY_STRING 4 #define TY_SOUND 5 #define TY_USOUND 6 #define TY_FOR 7 +#define TY_MARK 8 #define TY_MAXTYPE 15 // The level file format requires type codes 0 to 3 to be as is; other codes may change. typedef struct { union { Index: instruc ================================================================== --- instruc +++ instruc @@ -245,10 +245,17 @@ VolumeAt WinLevel ,XDir XYDir ,YDir + +; Operations with marks +mark "_" +tmark +in +nin +-mbegin ; Specials *Function *Local *Label Index: instruc.h ================================================================== --- instruc.h +++ instruc.h @@ -360,17 +360,22 @@ #define OP_XDIR 32943 #define OP_XDIR_C 34991 #define OP_XYDIR 32944 #define OP_YDIR 32945 #define OP_YDIR_C 34993 -#define OP_FUNCTION 32946 -#define OP_LOCAL 32947 -#define OP_LABEL 32948 -#define OP_STRING 32949 -#define OP_INT16 32950 -#define OP_INT32 32951 -#define OP_DISPATCH 32952 +#define OP_MARK 32946 +#define OP_TMARK 32947 +#define OP_IN 32948 +#define OP_NIN 32949 +#define OP_MBEGIN 32950 +#define OP_FUNCTION 32951 +#define OP_LOCAL 32952 +#define OP_LABEL 32953 +#define OP_STRING 32954 +#define OP_INT16 32955 +#define OP_INT32 32956 +#define OP_DISPATCH 32957 #ifdef HEROMESH_CLASS static const Op_Names op_names[]={ {"*",8486936}, {"+",8421398}, {"-",8421399}, @@ -577,10 +582,11 @@ {"XYDir",8421552}, {"Xloc",8486969}, {"YDir",8487089}, {"YEEHAW",8389401}, {"Yloc",8486970}, +{"_",8421554}, {"again",8683533}, {"band",8421406}, {"begin",8683532}, {"bit",8683554}, {"bit0",8388609}, @@ -626,24 +632,27 @@ {"eq",8421415}, {"for",8683537}, {"ge",8486955}, {"gt",8486953}, {"if",8683528}, +{"in",8421556}, {"is",8421421}, {"land",8421411}, {"le",8486956}, {"lnot",8421414}, {"lor",8421412}, {"lsh",8421404}, {"lt",8486954}, {"lxor",8421413}, {"m?",8421425}, +{"mbegin",8683702}, {"mod",8486938}, {"n?",8421422}, {"ne",8421416}, {"neg",8421403}, {"next",8683538}, +{"nin",8421557}, {"nip",8421379}, {"o?",8421427}, {"oz?",8421428}, {"pick",8421383}, {"repeat",8683536}, @@ -651,11 +660,12 @@ {"rot",8421381}, {"rsh",8486941}, {"s?",8421426}, {"swap",8421378}, {"then",8683530}, +{"tmark",8421555}, {"tuck",8421380}, {"until",8683534}, {"while",8683535}, }; -#define N_OP_NAMES 285 +#define N_OP_NAMES 290 #endif