Index: class.c ================================================================== --- class.c +++ class.c @@ -1185,11 +1185,11 @@ #define InstFlag(x) (peepcodes[ptr-1]<0x0080) #define Inst8bit() (peepcodes[ptr-1]<0x0100) #define AbbrevOp(x,y) case x: if(Inst7bit()) ChangeInst(+=0x1000|((y)<<4)); else AddInstF(x,tokent); break #define FlowPush(x) do{ if(flowdepth==64) ParseError("Too much flow control nesting\n"); flowop[flowdepth]=x; flowptr[flowdepth++]=ptr; }while(0) -#define FlowPop(x) do{ if(!flowdepth || flowop[--flowdepth]!=(x)) ParseError("Flow control mismatch\n"); }while(0) +#define FlowPop(x) do{ if(!flowdepth || flowop[--flowdepth]!=(x)) ParseError("Flow control mismatch\n"); prflag=0; }while(0) static int parse_instructions(int cla,int ptr,Hash*hash,int compat) { int peep=ptr; int prflag=0; Class*cl=classes[cla]; int flowdepth=0; @@ -1357,10 +1357,15 @@ } else { AddInst(y); AddInst(0x1F00|(x&0x1F)); } break; + case OP_SUPER: + case OP_SUPER_C: + if(!ptr || cl->codes[0]!=OP_SUPER) ParseError("Use of Super in a class with no parent class\n"); + AddInst(tokenv); + break; default: if(Tokenf(TF_ABNORMAL)) ParseError("Invalid instruction token\n"); if(compat && Tokenf(TF_COMPAT) && Tokenf(TF_EQUAL)) ++tokenv; AddInstF(tokenv,tokent); } @@ -1422,10 +1427,11 @@ } if(ptr>=0xFFEF) ParseError("Out of code space\n"); } if(flowdepth) ParseError("Unterminated flow control blocks (%d levels)\n",flowdepth); cl->codes=realloc(cl->codes,ptr*sizeof(Uint16))?:cl->codes; + if(!ptr) cl->codes=0; return ptr; } static void dump_class(int cla,int endptr,const Hash*hash) { const Class*cl=classes[cla]; @@ -1682,10 +1688,49 @@ else if(x<0x1040) cl->misc5|=1L<<(x&0x1F); else if(x<0x1060) cl->misc6|=1L<<(x&0x1F); else if(x<0x1080) cl->misc7|=1L<<(x&0x1F); else cl->collisionLayers|=1<<(x&0x1F); } + +static void set_super_class(Class*cl,int ptr) { + Class*su=classes[tokenv&0x3FFF]; + int i; + if(ptr || cl->nmsg) ParseError("Cannot specify superclasses before program blocks\n"); + if(!su || !(su->cflags&CF_GROUP)) ParseError("Cannot inherit from non-abstract class\n"); + cl->codes=malloc(8); + if(!cl->codes) fatal("Allocation failed\n"); + cl->codes[0]=OP_SUPER; + cl->codes[1]=tokenv&0x3FFF; + cl->height=su->height; + cl->weight=su->weight; + cl->climb=su->climb; + cl->density=su->density; + cl->volume=su->volume; + cl->strength=su->strength; + cl->arrivals|=su->arrivals; + cl->departures|=su->departures; + cl->temperature=su->temperature; + cl->misc4|=su->misc4; + cl->misc5|=su->misc5; + cl->misc6|=su->misc6; + cl->misc7|=su->misc7; + cl->uservars+=su->uservars; + cl->oflags|=su->oflags; + for(i=0;i<4;i++) { + cl->sharp[i]=su->sharp[i]; + cl->hard[i]=su->hard[i]; + } + cl->cflags|=su->cflags&(CF_PLAYER|CF_INPUT|CF_COMPATIBLE|CF_QUIZ); + cl->shape=su->shape; + cl->shovable=su->shovable; + cl->collisionLayers|=su->collisionLayers; + if(cl->nmsg=su->nmsg) { + cl->messages=malloc(cl->nmsg*sizeof(Uint16)); + if(!cl->messages) fatal("Allocation failed\n"); + for(i=0;inmsg;i++) cl->messages[i]=(su->messages[i]==0xFFFF)?0xFFFF:0x0000; + } +} static void class_definition(int cla,sqlite3_stmt*vst) { Hash*hash=calloc(LOCAL_HASH_SIZE,sizeof(Hash)); Class*cl=classes[cla]; int ptr=0; @@ -1830,20 +1875,22 @@ case OP_VISUALONLY: cl->oflags|=OF_VISUALONLY; break; case OP_STEALTHY: cl->oflags|=OF_STEALTHY; break; case OP_USERSTATE: cl->oflags|=OF_USERSTATE; break; case OP_SHOVABLE: cl->shovable=0x55; break; case OP_USERFLAG: class_user_flag(cl); break; + case OP_ABSTRACT: cl->cflags|=CF_GROUP; break; + case 0x4000 ... 0x7FFF: set_super_class(cl,ptr); ptr=2; break; default: ParseError("Invalid directly inside of a class definition\n"); } } else if(Tokenf(TF_CLOSE)) { break; } else { ParseError("Invalid directly inside of a class definition\n"); } } end_label_stack(cl->codes,hash); - if(!cl->nimages) cl->oflags|=OF_INVISIBLE; + if(!cl->nimages && !(cl->cflags&CF_GROUP)) cl->oflags|=OF_INVISIBLE; if(main_options['C']) dump_class(cla,ptr,hash); if(main_options['H']) { for(i=0;icflags&CF_GROUP) { + if(cl->edithelp || cl->gamehelp || cl->nimages) ParseError("Invalid in abstract classes"); + } } static void load_class_numbers(void) { int i,n; long size=0; Index: class.doc ================================================================== --- class.doc +++ class.doc @@ -269,17 +269,28 @@ the initial value for variables of objects of this class. Outside of code blocks, named constants are not normally interchangeable with the numbers having the same value. + + Inherit the specified class, which must have been previously declared as + an abstract class. Flags and message codes are inherited unless they are + overridden. Multiple inheritance is not allowed. If inheritance is used, + it must be specified before any blocks with program instructions, and + any user-defined variables defined in the superclass are not accessible + by the code specific to this class; you can define another variable with + the same name but it will be a new variable with the same name. + Set a user flag bit in the definition of Misc4, Misc5, Misc6, Misc7, or CollisionLayers of this class. You must previously define this user flag as a global definition. Abstract - (Not implemented yet.) + Disallows creating objects of this class, but allows other classes to + subclass this one. The (Image), (DefaultImage), (Help), and (EditorHelp) + blocks are not allowed in abstract classes. (Arrivals InPlace) This is an abbreviation for (Arrivals 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0). @@ -1642,10 +1653,18 @@ Sound ( sound interruptflag -- ) Play a sound; if the interrupt flag is zero then it does not interrupt existing sounds but otherwise it does. It is not an error if the values of the arguments are not valid. +Super ( ? -- ? ) + Only allowed in a class which is a subclass of another class. This + causes the current code to stop being executed and to instead execute + the code of the class it is inheriting. + +,Super ( ? -- ? ) + Same as Super but return to this code after it is finished. + swap ( x y -- y x ) 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 Index: exec.c ================================================================== --- exec.c +++ exec.c @@ -422,10 +422,21 @@ static Uint32 obj_class_at(Uint32 c,Uint32 x,Uint32 y) { Uint32 i; if(x<1 || x>pfwidth || y<1 || y>pfheight) return VOIDLINK; i=playfield[x+y*64-65]; + if(c && (classes[c]->cflags&CF_GROUP)) { + Uint16 k; + while(i!=VOIDLINK) { + if(!(objects[i]->oflags&OF_DESTROYED)) { + k=objects[i]->class; + while(k!=c && classes[k]->codes && classes[k]->codes[0]==OP_SUPER) k=classes[k]->codes[1]; + } + i=objects[i]->up; + } + return VOIDLINK; + } while(i!=VOIDLINK) { if(objects[i]->class==c && !(objects[i]->oflags&OF_DESTROYED)) return i; i=objects[i]->up; } return VOIDLINK; @@ -565,17 +576,26 @@ Uint32 n; if(x.t==TY_NUMBER && !x.u) { return 0; } else if(x.t>TY_MAXTYPE && y.t==TY_CLASS) { n=v_object(x); + if(classes[y.u]->cflags&CF_GROUP) { + n=objects[n]->class; + while(n!=y.u && classes[n]->codes && classes[n]->codes[0]==OP_SUPER) n=classes[n]->codes[1]; + return (n==y.u)?1:0; + } return (objects[n]->class==y.u)?1:0; - //TODO: subclassing (using CF_GROUP) } else if((x.t>TY_MAXTYPE || x.t==TY_CLASS) && y.t==TY_NUMBER && !y.u) { return 1; } else if(x.t==TY_CLASS && y.t==TY_CLASS) { + if(classes[y.u]->cflags&CF_GROUP) { + n=x.u; + while(n!=y.u && classes[n]->codes && classes[n]->codes[0]==OP_SUPER) n=classes[n]->codes[1]; + return (n==y.u)?1:0; + } return (x.u==y.u)?1:0; - //TODO: subclassing (using CF_GROUP) + //TODO: CF_GROUP } else { Throw("Type mismatch"); } } @@ -2275,10 +2295,12 @@ case OP_STRENGTH_EC: NoIgnore(); StackReq(2,0); t1=Pop(); Numeric(t1); i=v_object(Pop()); if(i!=VOIDLINK) objects[i]->strength=t1.u; break; case OP_STRENGTH_EC16: NoIgnore(); StackReq(2,0); t1=Pop(); Numeric(t1); i=v_object(Pop()); if(i!=VOIDLINK) objects[i]->strength=t1.u&0xFFFF; break; case OP_STRING: StackReq(0,1); Push(UVALUE(code[ptr++],TY_STRING)); break; case OP_SOUND: StackReq(2,0); t2=Pop(); t1=Pop(); break; // Sound not implemented at this time case OP_SUB: StackReq(2,1); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); Push(NVALUE(t1.u-t2.u)); break; + case OP_SUPER: i=code[1]; code=classes[i]->codes; ptr=get_message_ptr(i,msgvars.msg); if(ptr==0xFFFF) break; break; + case OP_SUPER_C: i=code[1]; j=get_message_ptr(i,msgvars.msg); if(j!=0xFFFF) execute_program(classes[i]->codes,j,obj); break; case OP_SWAP: StackReq(2,2); t1=Pop(); t2=Pop(); Push(t1); Push(t2); break; case OP_SYNCHRONIZE: StackReq(2,0); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); animate_sync(obj,t1.u,t2.u); break; case OP_TARGET: StackReq(0,1); Push(NVALUE(v_target(obj)?1:0)); break; case OP_TARGET_C: StackReq(1,1); i=v_object(Pop()); Push(NVALUE(v_target(i)?1:0)); break; case OP_TEMPERATURE: StackReq(0,1); Push(NVALUE(o->temperature)); break; @@ -2397,10 +2419,11 @@ Object*o; Value v; if(s==2) t=0xFFFFFFFFULL; if(lastobj==VOIDLINK) return t; n=lastobj; + if(c && (classes[c]->cflags&CF_GROUP)) Throw("Broadcast for abstract classes is not implemented yet"); while(o=objects[n]) { if(!c || o->class==c) { v=send_message(from,n,msg,arg1,arg2,arg3); switch(s) { case 0: Index: heromesh.h ================================================================== --- heromesh.h +++ heromesh.h @@ -145,12 +145,12 @@ typedef struct { const char*name; const char*edithelp; // not present if CF_GROUP const char*gamehelp; // not present if CF_GROUP - Uint16*codes; // if this is CF_GROUP, then instead a zero-terminated list of classes - Uint16*messages; // use 0xFFFF if no such message block; not present if CF_GROUP + Uint16*codes; + Uint16*messages; // use 0xFFFF if no such message block Uint16*images; // high bit is set if available to editor; not present if CF_GROUP Sint32 height,weight,climb,density,volume,strength,arrivals,departures; Sint32 temperature,misc4,misc5,misc6,misc7; Uint16 uservars,oflags,nmsg; Uint16 sharp[4]; Index: instruc ================================================================== --- instruc +++ instruc @@ -295,10 +295,14 @@ -,=Pattern "P" -,=PatternS "P*" -Four -Eight -cut + +; Inheritance +-Abstract +,Super ; Specials *Function *Local *Label Index: instruc.h ================================================================== --- instruc.h +++ instruc.h @@ -427,18 +427,21 @@ #define OP_PATTERNS_E 37082 #define OP_PATTERNS_EC 39130 #define OP_FOUR 32987 #define OP_EIGHT 32988 #define OP_CUT 32989 -#define OP_FUNCTION 32990 -#define OP_LOCAL 32991 -#define OP_LABEL 32992 -#define OP_STRING 32993 -#define OP_INT16 32994 -#define OP_INT32 32995 -#define OP_DISPATCH 32996 -#define OP_USERFLAG 32997 +#define OP_ABSTRACT 32990 +#define OP_SUPER 32991 +#define OP_SUPER_C 35039 +#define OP_FUNCTION 32992 +#define OP_LOCAL 32993 +#define OP_LABEL 32994 +#define OP_STRING 32995 +#define OP_INT16 32996 +#define OP_INT32 32997 +#define OP_DISPATCH 32998 +#define OP_USERFLAG 32999 #ifdef HEROMESH_CLASS static const Op_Names op_names[]={ {"*",8486937}, {"+",8421399}, {"+Move",10584235}, @@ -447,10 +450,11 @@ {"-rot",8421382}, {".",10518528}, {"/",8486938}, {"ANHH",8389394}, {"ARRIVED",8389124}, +{"Abstract",8683742}, {"Animate",8421513}, {"AnimateDead",8421514}, {"Arg1",8552569}, {"Arg2",8552570}, {"Arg3",8552571}, @@ -646,10 +650,11 @@ {"Sharp",8618067}, {"Shovable",8618069}, {"Sound",8421567}, {"Stealthy",8618094}, {"Strength",9142352}, +{"Super",8487135}, {"Synchronize",8421568}, {"TAHTASHH",8389409}, {"THMP_thmp",8389405}, {"THWIT",8389384}, {"TICK",8389391}, @@ -768,7 +773,7 @@ {"tuck",8421380}, {"uniq",8421584}, {"until",8683534}, {"while",8683535}, }; -#define N_OP_NAMES 330 +#define N_OP_NAMES 332 #endif