Index: class.c ================================================================== --- class.c +++ class.c @@ -43,10 +43,11 @@ Uint16 functions[0x4000]; int max_animation=32; Sint32 max_volume=10000; Uint8 back_color=1; char**stringpool; +AnimationSlot anim_slot[8]; #define HASH_SIZE 8888 #define LOCAL_HASH_SIZE 5555 typedef struct { Uint16 id; @@ -1621,10 +1622,26 @@ case OP_ANIMATE: nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(tokenv<1 || tokenv>256) ParseError("Number of max animation steps out of range\n"); max_animation=tokenv; + nxttok(); + if(tokent!=TF_CLOSE) ParseError("Expected close parenthesis\n"); + break; + case OP_SYNCHRONIZE: + nxttok(); + if(tokent!=TF_INT) ParseError("Number expected\n"); + i=tokenv; + if(i&~7) ParseError("Animation slot number out of range\n"); + nxttok(); + if(tokent!=TF_INT) ParseError("Number expected\n"); + if(tokenv<1 || tokenv>255) ParseError("Length of synchronized animation too long\n"); + anim_slot[i].length=tokenv; + nxttok(); + if(tokent!=TF_INT) ParseError("Number expected\n"); + if(tokenv<1 || tokenv>255) ParseError("Synchronized animation speed out of range\n"); + anim_slot[i].speed=tokenv; nxttok(); if(tokent!=TF_CLOSE) ParseError("Expected close parenthesis\n"); break; case OP_VOLUME: nxttok(); Index: exec.c ================================================================== --- exec.c +++ exec.c @@ -171,14 +171,111 @@ } static inline Uint8 resolve_dir(Uint32 n,Uint32 d) { return d<8?d:(objects[n]->dir+d)&7; } + +/* + Working of animation: There are two separate animations, being logical + and visual animations. New animations replace the current logical + animation; if there isn't one, but there is a visual animation, then a + new logical animation is added at the end. The logical animation is + always at the same step or ahead of the visual animation. + + - lstep - Step number of the logical animation; this cannot equal or + exceed max_animation. Only a single logical step is present at once. + + - vstep - Step number of the visual animation; this cannot equal or + exceed max_animation. The tail of the visual animation is the same as + the head of the logical animation, and the buffer is circular. + + - status - Contains bit flags. If the ANISTAT_LOGICAL flag is set, then + it means a logical animation is active. If the ANISTAT_VISUAL flag is + set, then it means a visual animation is active. ANISTAT_SYNCHRONIZED + is set only for synchronized animations, which are visual only. + + - ltime - Amount of logical time passed + + - vtime - Number of centiseconds elapsed. + + - vimage - If the status field has the ANISTAT_VISUAL bit set, then this + will decide the picture to display rather than using the object's image. + + - count - Number of logical animation steps so far this turn. If it is + equal to max_animation then no more animations can be added this turn. + + Synchronized animations only use vstep, status, and vimage. +*/ + +static Animation*animalloc(void) { + Animation*a=malloc(sizeof(Animation)+max_animation*sizeof(AnimationStep)); + if(!a) fatal("Allocation failed\n"); + a->lstep=a->vstep=a->ltime=a->vtime=a->status=a->count=0; + return a; +} static void animate(Uint32 n,Uint32 f,Uint32 a0,Uint32 a1,Uint32 t) { + Animation*an=objects[n]->anim; + objects[n]->image=a0; + f&=0x0A; + if(!an) an=objects[n]->anim=animalloc(); + if(an->status&ANISTAT_SYNCHRONIZED) an->status=0; + if(an->count==max_animation) f=ANI_STOP; + if(f&(ANI_ONCE|ANI_LOOP)) { + switch(an->status) { + case 0: + an->vtime=an->lstep=an->vstep=0; + an->vimage=a0; + break; + case ANISTAT_LOGICAL: + an->vstep=an->lstep; + an->vtime=0; + an->vimage=a0; + break; + case ANISTAT_VISUAL: + an->lstep++; + if(an->lstep>=max_animation) an->lstep=0; + if(an->lstep==an->vstep && max_animation>1) { + an->vstep++; + if(an->vstep==max_animation) an->vstep=0; + an->vimage=an->step[an->vstep].start; + an->vtime=0; + } + break; + case ANISTAT_VISUAL|ANISTAT_LOGICAL: + if(an->lstep==an->vstep) { + an->vimage=a0; + an->vtime=0; + } + break; + } + an->step[an->lstep].flag=f; + an->step[an->lstep].start=a0; + an->step[an->lstep].end=a1; + an->step[an->lstep].speed=t; + an->ltime=0; + an->status=ANISTAT_VISUAL|ANISTAT_LOGICAL; + an->count++; + } else if(an->lstep==an->vstep) { + an->status=0; + } else if(an->status&ANISTAT_LOGICAL) { + an->lstep=(an->lstep?:max_animation)-1; + an->status&=~ANISTAT_LOGICAL; + } +} + +static void animate_sync(Uint32 n,Uint32 sl,Uint32 a0) { + Animation*an=objects[n]->anim; objects[n]->image=a0; - //TODO + sl&=7; + if(!an) an=objects[n]->anim=animalloc(); + an->status=ANISTAT_VISUAL|ANISTAT_SYNCHRONIZED; + an->vimage=a0+anim_slot[sl].frame; + an->lstep=an->vstep=0; + an->step->flag=ANI_LOOP|ANI_SYNC; + an->step->start=a0; + an->step->slot=sl; } static Uint32 obj_above(Uint32 i) { Object*o; if(i==VOIDLINK) return VOIDLINK; @@ -945,10 +1042,11 @@ case OP_STRENGTH_EC: NoIgnore(); StackReq(2,0); t1=Pop(); Numeric(t1); i=v_object(Pop()); if(i!=VOIDLINK) o->strength=t1.u; break; case OP_STRENGTH_EC16: NoIgnore(); StackReq(2,0); t1=Pop(); Numeric(t1); i=v_object(Pop()); if(i!=VOIDLINK) o->strength=t1.u; 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_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_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; @@ -1099,10 +1197,11 @@ 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; } current_key=0; if(key_ignored) return changed?"Invalid use of IgnoreKey":0; move_number++; Index: game.c ================================================================== --- game.c +++ game.c @@ -74,10 +74,55 @@ draw_text(0,40,buf,0xF0,0xF1); SDL_UnlockSurface(screen); SDL_Flip(screen); set_cursor(XC_arrow); } + +static void continue_animation(void) { + Uint32 n=firstobj; + Object*o; + Animation*a; + int i; + for(i=0;i<8;i++) if(anim_slot[i].length && ++anim_slot[i].vtime==anim_slot[i].speed && ++anim_slot[i].frame==anim_slot[i].length) anim_slot[i].frame=0; + while(n!=VOIDLINK) { + o=objects[n]; + if((a=o->anim) && (a->status&ANISTAT_VISUAL)) { + i=a->vstep; + if(a->step[i].flag&ANI_SYNC) { + i=anim_slot[a->step[i].slot].frame+a->step[i].start; + if(i!=a->vimage) { + a->vimage=i; + draw_cell(o->x,o->y); + } + } else if(++a->vtime>=a->step[i].speed) { + a->vtime=0; + if(a->vimage==a->step[i].end) { + if(a->step[i].flag&ANI_ONCE) { + if(a->vstep==a->lstep) { + a->status&=~ANISTAT_VISUAL; + } else { + if(++a->vstep==max_animation) a->vstep=0; + a->vimage=a->step[a->vstep].start; + } + } else if(a->step[i].flag&ANI_OSC) { + a->step[i].end=a->step[i].start; + a->step[i].start=a->vimage; + goto advance; + } else { + a->vimage=a->step[i].start; + } + } else { + advance: + if(a->step[i].end>=a->step[i].start) ++a->vimage; else --a->vimage; + } + draw_cell(o->x,o->y); + } + } + n=o->next; + } + SDL_Flip(screen); +} static void show_mouse_xy(SDL_Event*ev) { char buf[32]; int x,y; x=(ev->motion.x-left_margin)/picture_size+1; @@ -98,10 +143,11 @@ gameover=-1; screen_message(t); } else { gameover=0; } + timerflag=0; } static inline void exam_value(const char*t,int y,Value v) { char buf[256]; int i; @@ -400,11 +446,11 @@ break; case SDL_MOUSEMOTION: show_mouse_xy(&ev); break; case SDL_USEREVENT: - //TODO: animation + if(!gameover) continue_animation(); timerflag=0; break; case SDL_MOUSEBUTTONDOWN: if(ev.button.x64 || y<1 || y>64) return; SDL_FillRect(screen,&dst,back_color); o=playfield[y*64+x-65]; while(o!=VOIDLINK) { if(main_options['e'] || !(objects[o]->oflags&OF_INVISIBLE)) { c=classes[objects[o]->class]; - if(objects[o]->imagenimages) - draw_picture((x-1)*picture_size+left_margin,(y-1)*picture_size,c->images[objects[o]->image]&0x7FFF); + if(objects[o]->anim && (objects[o]->anim->status&ANISTAT_VISUAL)) i=objects[o]->anim->vimage; else i=objects[o]->image; + if(inimages) draw_picture((x-1)*picture_size+left_margin,(y-1)*picture_size,c->images[i]&0x7FFF); } o=objects[o]->up; } }