Index: class.doc ================================================================== --- class.doc +++ class.doc @@ -844,11 +844,11 @@ FlushClass ( class -- ) ** Resets the Arrived, Busy, Departed, Inertia, Moved, and UserSignal flags of all objects of the specified class to zero. If the class is -1, then all objects are flushed in this way, and during the input phase, it also - skips the other phases similarly to IgnoreKey if the class is -1. + skips the beginning and ending phases if the class is -1. FlushObj ( -- ) ** Resets this object's Arrived, Departed, Inertia, Moved, and UserSignal. ,FlushObj ( obj -- ) ** @@ -948,11 +948,12 @@ LoseLevel ( -- ) Ends all execution and results in loss of game. lsh ( in shift -- out ) - Left shift. + Left shift. If the shift amount is out of range 0-31, all bits are + shifted out, and the result is zero. lt ( in1 in2 -- bool ) Test if first input is less than second input (unsigned). ,lt ( in1 in2 -- bool ) @@ -1095,14 +1096,17 @@ rot ( x y z -- y z x ) -rot ( x y z -- z x y ) rsh ( in shift -- out ) - Logical right shift. + Logical right shift. If the shift amount is out of the range 0-31, + then all bits are shifted out and the result is zero. ,rsh ( in shift -- out ) - Arithmetic right shift. + Arithmetic right shift. If the shift amount is out of the range 0-31, + then all bits are shifted out; the result will be -1 if the input is + negative, or 0 otherwise. Self ( -- obj ) The reference to the current object. Send ( message arg1 arg2 -- value ) @@ -1482,6 +1486,8 @@ * Some bits are masked out of the return value from HIT and HITBY. * Many variables are limited to 16-bits. * Moving objects is not allowed during LASTIMAGE processing. + +* The way that the trigger phase works is different. Index: exec.c ================================================================== --- exec.c +++ exec.c @@ -613,11 +613,11 @@ Object*o; Object*p; Value v; if(n==VOIDLINK || (objects[n]->oflags&OF_DESTROYED)) return 0; o=objects[n]; - if(lastimage_processing && (classes[o->class]->cflags&CF_COMPATIBLE)) Throw("Can't move during animation processing"); + if(lastimage_processing) Throw("Can't move during animation processing"); if(x<1 || y<1 || x>pfwidth || y>pfheight) return 0; if(v_bool(send_message(from,n,MSG_MOVING,NVALUE(x),NVALUE(y),NVALUE(0)))) return 0; if(classes[o->class]->cflags&CF_PLAYER) { m=lastobj; while(m!=VOIDLINK) { @@ -831,11 +831,11 @@ printf(" : %u %u : %u %u\n",t1.t,t1.u,t2.t,t2.u); } static void flush_object(Uint32 n) { Object*o=objects[n]; - o->arrived=o->arrived2=o->departed=o->departed2=0; + o->arrived=o->departed=0; o->oflags&=~(OF_MOVED|OF_BUSY|OF_USERSIGNAL); o->inertia=0; } static void flush_class(Uint16 c) { @@ -844,11 +844,11 @@ if(lastobj==VOIDLINK) return; n=lastobj; while(o=objects[n]) { p=o->prev; if(!c || o->class==c) { - o->arrived=o->arrived2=o->departed=o->departed2=0; + o->arrived=o->departed=0; o->oflags&=~(OF_MOVED|OF_BUSY|OF_USERSIGNAL); o->inertia=0; } if(p==VOIDLINK) break; n=p; @@ -1064,11 +1064,11 @@ Value t1,t2; static Value t3,t4,t5; if(StackProtection()) Throw("Call stack overflow"); // Note about bit shifting: At least when running Hero Mesh in DOSBOX, out of range bit shifts produce zero. // I don't know if this is true on all computers that Hero Mesh runs on, though. (Some documents suggest that x86 doesn't work this way) - // The below code assumes that signed right shifting is available. + // The below code assumes that signed right shifting is available on the computer that Free Hero Mesh runs on. for(;;) switch(code[ptr++]) { case 0x0000 ... 0x00FF: StackReq(0,1); Push(NVALUE(code[ptr-1])); break; case 0x0100 ... 0x01FF: StackReq(0,1); Push(NVALUE(code[ptr-1]-0x200)); break; case 0x0200 ... 0x02FF: StackReq(0,1); Push(MVALUE(code[ptr-1]&255)); break; case 0x0300 ... 0x03FF: StackReq(0,1); Push(UVALUE(code[ptr-1]&255,TY_SOUND)); break; @@ -1492,13 +1492,35 @@ nobjects=0; free(objects); objects=0; gameover=0; } + +static Uint8 execute_animation(Uint8 clock,Uint32 obj) { + Object*o=objects[obj]; + Animation*a=o->anim; + if(!(a->step[a->lstep].flag&ANI_ONCE)) return clock; + if(a->ltime>=a->step[a->lstep].speed) { + a->ltime=0; + if(o->image==a->step[a->lstep].end) { + a->status&=~ANISTAT_LOGICAL; + if(classes[o->class]->cflags&CF_COMPATIBLE) lastimage_processing=1; + send_message(VOIDLINK,obj,MSG_LASTIMAGE,NVALUE(0),NVALUE(0),NVALUE(0)); + lastimage_processing=0; + } else { + if(a->step[a->lstep].start>a->step[a->lstep].end) --o->image; else ++o->image; + } + } + if(!(a->status&ANISTAT_LOGICAL) || !(a->step[a->lstep].flag&ANI_ONCE)) return clock; + if(clock>a->step[a->lstep].speed-a->ltime) return a->step[a->lstep].speed-a->ltime; + return clock; +} const char*execute_turn(int key) { - Uint32 m,n; + Uint8 busy,clock; + Uint32 m,n,turn; + Object*o; Value v; int i; if(!key) return 0; if(setjmp(my_env)) return my_error; if(quiz_text) { @@ -1515,14 +1537,14 @@ key_ignored=0; all_flushed=0; lastimage_processing=0; vstackptr=0; current_key=key; - for(n=0;ndistance=0; - objects[n]->oflags&=~(OF_KEYCLEARED|OF_DONE); - if(objects[n]->anim) objects[n]->anim->count=0; + for(n=0;ndistance=0; + o->oflags&=~(OF_KEYCLEARED|OF_DONE); + if(o->anim) o->anim->count=0; } // Input phase m=VOIDLINK; v=NVALUE(0); if(!quiz_obj.t) { @@ -1535,11 +1557,11 @@ } } else { n=quiz_obj.u; if(objects[n]->generation!=quiz_obj.t) n=VOIDLINK; quiz_obj=NVALUE(0); - //TODO + if(classes[objects[n]->class]->cflags&CF_COMPATIBLE) all_flushed=1; v=send_message(VOIDLINK,n,MSG_KEY,NVALUE(key),NVALUE(0),NVALUE(1)); } current_key=0; if(key_ignored) { quiz_obj=NVALUE(0); @@ -1546,19 +1568,97 @@ return changed?"Invalid use of IgnoreKey":0; } move_number++; // Beginning phase if(!all_flushed) broadcast(m,0,MSG_BEGIN_TURN,m==VOIDLINK?NVALUE(objects[m]->x):NVALUE(0),m==VOIDLINK?NVALUE(objects[m]->y):NVALUE(0),v,0); + turn=0; // Trigger phase - + trig: + busy=0; + clock=255; + for(i=0;i<64*pfheight;i++) { + n=playfield[i]; + while(n!=VOIDLINK) { + o=objects[n]; + m=o->up; + if(o->oflags&OF_DESTROYED) { + objtrash(n); + } else if(classes[o->class]->cflags&CF_COMPATIBLE) { + if(o->oflags&OF_MOVED) { + o->oflags&=~OF_MOVED; + send_message(VOIDLINK,n,MSG_MOVED,NVALUE(0),NVALUE(0),NVALUE(turn)); + busy=1; + } + if(o->departed) { + send_message(VOIDLINK,n,MSG_DEPARTED,NVALUE(0),NVALUE(0),NVALUE(turn)); + o->departed=0; + busy=1; + } + if(o->arrived) { + send_message(VOIDLINK,n,MSG_ARRIVED,NVALUE(0),NVALUE(0),NVALUE(turn)); + o->arrived=0; + busy=1; + } + if(o->anim && (o->anim->status&ANISTAT_LOGICAL)) clock=execute_animation(clock,n); + if(o->oflags&(OF_BUSY|OF_USERSIGNAL)) busy=1; + } else { + o->departed2=o->departed; + o->departed=0; + o->arrived2=o->arrived; + o->arrived=0; + if(o->oflags&OF_MOVED) o->oflags=(o->oflags|OF_MOVED2)&~OF_MOVED; + } + n=m; + } + } + n=lastobj; + while(n!=VOIDLINK) { + o=objects[n]; + if(!(classes[o->class]->cflags&CF_COMPATIBLE)) { + if(o->oflags&OF_MOVED2) send_message(VOIDLINK,n,MSG_MOVED,NVALUE(0),NVALUE(0),NVALUE(turn)),busy=1; + if(o->departed2) send_message(VOIDLINK,n,MSG_DEPARTED,NVALUE(o->departed2),NVALUE(0),NVALUE(turn)),busy=1; + if(o->arrived2) send_message(VOIDLINK,n,MSG_ARRIVED,NVALUE(o->arrived2),NVALUE(0),NVALUE(turn)),busy=1; + o->oflags&=~OF_MOVED2; + o->arrived2=o->departed2=0; + if(o->anim && (o->anim->status&ANISTAT_LOGICAL)) clock=execute_animation(clock,n); + if(o->oflags&(OF_BUSY|OF_USERSIGNAL)) busy=1; + } + n=o->prev; + } // Ending phase - - // Animation phase - + if(!busy && !all_flushed) { + n=lastobj; + while(n!=VOIDLINK) { + v=send_message(VOIDLINK,n,MSG_END_TURN,NVALUE(turn),NVALUE(0),NVALUE(0)); + if(v_bool(v) || objects[n]->arrived || objects[n]->departed) busy=1; + if(objects[n]->oflags&(OF_BUSY|OF_USERSIGNAL|OF_MOVED)) busy=1; + n=objects[n]->prev; + } + turn++; + if(!busy) all_flushed=1; + } + // Clock phase + if(!clock) clock=1; + n=lastobj; + while(n!=VOIDLINK) { + o=objects[n]; + if(o->oflags&(OF_BUSY|OF_USERSIGNAL|OF_MOVED)) busy=1; + if(o->arrived || o->departed) busy=1; + if(o->anim && (o->anim->status&ANISTAT_LOGICAL)) { + if(o->anim->step[o->anim->lstep].flag&ANI_ONCE) { + i=o->anim->ltime+clock; + o->anim->ltime=i>255?255:i; + busy=1; + } + } + n=o->prev; + } + if(busy) goto trig; // Cleanup phase for(n=0;noflags&OF_DESTROYED)) objtrash(n); if(generation_number<=TY_MAXTYPE) return "Too many generations of objects"; + // Finished return 0; } const char*init_level(void) { if(setjmp(my_env)) return my_error; Index: heromesh.h ================================================================== --- heromesh.h +++ heromesh.h @@ -119,10 +119,11 @@ #define OF_MOVED 0x0040 #define OF_DONE 0x0080 #define OF_KEYCLEARED 0x0100 #define OF_DESTROYED 0x0200 #define OF_BIZARRO 0x0400 +#define OF_MOVED2 0x0800 typedef struct { const char*name; const char*edithelp; // not present if CF_GROUP const char*gamehelp; // not present if CF_GROUP