Index: bindings.doc ================================================================== --- bindings.doc +++ bindings.doc @@ -57,10 +57,13 @@ Rewind that many moves. '= ' Restart and replay that many moves. +'^T' + Display the level title popup. + '^o' List objects at the specified coordinates. === Editor commands === Index: exec.c ================================================================== --- exec.c +++ exec.c @@ -29,10 +29,11 @@ Uint32 playfield[64*64]; // bottom-most object per cell Uint8 pfwidth,pfheight; Sint8 gameover,key_ignored; Uint8 generation_number_inc; Uint32 move_number; +unsigned char*quiz_text; typedef struct { Uint16 msg; Uint32 from; Value arg1,arg2,arg3; @@ -911,10 +912,124 @@ static inline Value v_send_self(Uint32 from,Value msg,Value arg1,Value arg2,Value arg3) { if(msg.t!=TY_MESSAGE) Throw("Type mismatch"); return send_message(from,from,msg.u,arg1,arg2,arg3); } + +static void v_set_popup(Uint32 from,int argc) { + const unsigned char*t; + const unsigned char*u; + sqlite3_str*s; + Value v; + int argi=1; + if(argc>32 || argc<0) Throw("Too many arguments"); + vstackptr-=argc; + v=Pop(); + if(quiz_text) return; + if(argc) { + argc++; + if(v.t!=TY_STRING && v.t!=TY_LEVELSTRING) Throw("Type mismatch"); + s=sqlite3_str_new(userdb); + t=value_string_ptr(v); + while(*t) { + if(u=strchr(t,'%')) { + sqlite3_str_append(s,t,u-t); + t=u+2; + switch(u[1]) { + case 0: + t=u+1; + break; + case 'c': + if(argi==argc) break; + v=vstack[vstackptr+argi++]; + if(v.t==TY_NUMBER) { + sqlite3_str_appendchar(s,1,31); + sqlite3_str_appendchar(s,1,v.u&255?:255); + } + break; + case 'd': + if(argi==argc) break; + v=vstack[vstackptr+argi++]; + if(v.t==TY_NUMBER) sqlite3_str_appendf(s,"%d",(signed int)v.s); + break; + case 'i': + if(argi>=argc-1) break; + if(vstack[vstackptr+argi].t==TY_CLASS && vstack[vstackptr+argi+1].t==TY_NUMBER) { + Class*c=classes[vstack[vstackptr+argi].u]; + int n=vstack[vstackptr+argi+1].u&255; + if(nnimages) sqlite3_str_appendf(s,"\x0E\x07:%d\\",c->images[n]&0x7FFF); + } + argi+=2; + break; + case 's': + if(argi==argc) break; + v=vstack[vstackptr+argi++]; + switch(v.t) { + case TY_STRING: case TY_LEVELSTRING: + sqlite3_str_appendf(s,"%s",value_string_ptr(v)); + break; + case TY_NUMBER: + sqlite3_str_appendf(s,"%llu",(sqlite3_int64)v.u); + break; + case TY_CLASS: + sqlite3_str_appendf(s,"%s",classes[v.u]->name); + break; + default: + Throw("Type mismatch"); + } + break; + case 'u': + if(argi==argc) break; + v=vstack[vstackptr+argi++]; + if(v.t==TY_NUMBER) sqlite3_str_appendf(s,"%u",(unsigned int)v.u); + break; + case 'x': + if(argi==argc) break; + v=vstack[vstackptr+argi++]; + if(v.t==TY_NUMBER) sqlite3_str_appendf(s,"%x",(unsigned int)v.u); + break; + case 'X': + if(argi==argc) break; + v=vstack[vstackptr+argi++]; + if(v.t==TY_NUMBER) sqlite3_str_appendf(s,"%X",(unsigned int)v.u); + break; + case '%': + sqlite3_str_appendchar(s,1,'%'); + break; + } + } else { + sqlite3_str_appendall(s,t); + break; + } + } + sqlite3_str_appendchar(s,1,0); + quiz_text=sqlite3_str_finish(s); + } else { + switch(v.t) { + case TY_STRING: case TY_LEVELSTRING: + quiz_text=sqlite3_mprintf("%s",value_string_ptr(v)); + break; + case TY_NUMBER: + quiz_text=sqlite3_mprintf("%llu",(sqlite3_int64)v.u); + break; + case TY_CLASS: + quiz_text=sqlite3_mprintf("%s",classes[v.u]->name); + break; + default: + Throw("Type mismatch"); + } + } + if(!quiz_text) fatal("Allocation failed\n"); + t=quiz_text; + while(*t) { + if(*t==16) { + quiz_obj.t=objects[from]->generation?:1; + quiz_obj.u=from; + } + if(*t==31 && t[1]) t+=2; else t+=1; + } +} // 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))) @@ -1156,10 +1271,12 @@ case OP_OBJDIR_C: StackReq(2,1); t2=Pop(); Numeric(t2); i=obj_dir(v_object(Pop()),t2.u); Push(OVALUE(i)); break; case OP_OBJLAYERAT: StackReq(3,1); t3=Pop(); Numeric(t3); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); i=obj_layer_at(t1.u,t2.u,t3.u); Push(OVALUE(i)); break; case OP_OBJTOPAT: StackReq(2,1); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); i=obj_top_at(t1.u,t2.u); Push(OVALUE(i)); break; case OP_PLAYER: StackReq(0,1); if(classes[o->class]->cflags&CF_PLAYER) Push(NVALUE(1)); else Push(NVALUE(0)); break; case OP_PLAYER_C: StackReq(1,1); GetClassFlagOf(CF_PLAYER); break; + case OP_POPUP: StackReq(1,0); v_set_popup(obj,0); break; + case OP_POPUPARGS: i=code[ptr++]; StackReq(i+1,0); v_set_popup(obj,i); break; case OP_RET: return; case OP_ROT: StackReq(3,3); t3=Pop(); t2=Pop(); t1=Pop(); Push(t2); Push(t3); Push(t1); break; case OP_ROTBACK: StackReq(3,3); t3=Pop(); t2=Pop(); t1=Pop(); Push(t3); Push(t1); Push(t2); break; case OP_RSH: StackReq(2,1); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); Push(NVALUE(t2.u&~31?0:t1.u>>t2.u)); break; case OP_RSH_C: StackReq(2,1); t2=Pop(); Numeric(t2); t1=Pop(); Numeric(t1); Push(NVALUE(t2.u&~31?(t1.s<0?-1:0):t1.s>>t2.u)); break; @@ -1196,10 +1313,11 @@ case OP_STRENGTH_C: StackReq(1,1); Push(GetVariableOrAttributeOf(strength,NVALUE)); break; case OP_STRENGTH_E: NoIgnore(); StackReq(1,0); t1=Pop(); Numeric(t1); o->strength=t1.u; break; case OP_STRENGTH_E16: NoIgnore(); StackReq(1,0); t1=Pop(); Numeric(t1); o->strength=t1.u&0xFFFF; break; 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_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_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_TEMPERATURE: StackReq(0,1); Push(NVALUE(o->temperature)); break; @@ -1339,10 +1457,14 @@ void annihilate(void) { Uint32 i; for(i=0;i<64*64;i++) playfield[i]=VOIDLINK; firstobj=lastobj=VOIDLINK; + if(quiz_text) { + sqlite3_free(quiz_text); + quiz_text=0; + } if(!objects) return; for(i=0;ianim); free(objects[i]); } @@ -1393,10 +1515,15 @@ 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); + // Trigger phase + + // Ending phase + + // Animation phase // Cleanup phase for(n=0;noflags&OF_DESTROYED)) objtrash(n); if(generation_number<=TY_MAXTYPE) return "Too many generations of objects"; return 0; Index: game.c ================================================================== --- game.c +++ game.c @@ -73,10 +73,11 @@ y=y/picture_size+1; if(x>0 && y>0 && x<=pfwidth && y<=pfheight) snprintf(buf,8,"(%2d,%2d)",x,y); else strcpy(buf," "); draw_text(0,40,buf,0xF0,0xF1); SDL_UnlockSurface(screen); + if(quiz_text) draw_popup(quiz_text); SDL_Flip(screen); set_cursor(XC_arrow); } static void continue_animation(void) { @@ -170,15 +171,12 @@ break; case TY_MESSAGE: snprintf(buf,255,"%s%s",v.u<256?"":"#",v.u<256?standard_message_names[v.u]:messages[v.u-256]); draw_text(200,y,buf,0xF0,0xFD); break; - case TY_LEVELSTRING: - - break; - case TY_STRING: - + case TY_LEVELSTRING: case TY_STRING: + draw_text(200,y,"",0xF0,0xF9); break; case TY_SOUND: case TY_USOUND: draw_text(200,y,"",0xF0,0xF6); break; default: @@ -255,10 +253,11 @@ SDL_FillRect(screen,&r,0xF0); SDL_LockSurface(screen); exam_value("Self:",0,OVALUE(n)); exam_value("Class:",1,CVALUE(o->class)); exam_value("Image:",2,NVALUE(o->image)); + if(classes[o->class]->cflags&CF_QUIZ) goto quiz; exam_value("Dir:",3,NVALUE(o->dir)); exam_value("Misc1:",4,o->misc1); exam_value("Misc2:",5,o->misc2); exam_value("Misc3:",6,o->misc3); exam_value("Misc4:",7,o->misc4); @@ -283,10 +282,11 @@ exam_hardsharp("Sharpness:",26,o->sharp); while(sqlite3_step(st)==SQLITE_ROW) { i=sqlite3_column_int(st,1); exam_value(sqlite3_column_text(st,0),i+28,o->uservars[i]); } + quiz: sqlite3_reset(st); SDL_UnlockSurface(screen); SDL_Flip(screen); while(SDL_WaitEvent(&ev)) switch(ev.type) { case SDL_KEYDOWN: @@ -343,12 +343,12 @@ draw_text(0,0,buf,0xF7,0xF0); n=t; for(i=0;idown; for(i=0;ih/8 && n!=VOIDLINK;i++) { o=objects[n]; - snprintf(buf,255," %8d: %-14.14s %3d %s",n,classes[o->class]->name,o->image,dirs[o->dir&7]); - draw_text(24,r.y,buf,0xF1,o->generation?0xFF:0xF8); + snprintf(buf,255," %8d: %-14.14s %3d %s",n,classes[o->class]->name,o->image,classes[o->class]->cflags&CF_QUIZ?"":dirs[o->dir&7]); + draw_text(24,r.y,buf,0xF1,o->generation?(classes[o->class]->cflags&CF_PLAYER?0xFE:0xFF):0xF8); n=o->down; r.y+=8; } SDL_UnlockSurface(screen); SDL_Flip(screen); @@ -397,10 +397,13 @@ return 0; case '^E': // Edit return -2; case '^Q': // Quit return -1; + case '^T': // Show title + modal_draw_popup(level_title); + return prev; case '^o': // List objects list_objects_at(number-65); return prev; case 'go': // Select level begin_level(number); @@ -466,11 +469,11 @@ break; case SDL_MOUSEMOTION: show_mouse_xy(&ev); break; case SDL_USEREVENT: - if(!gameover) continue_animation(); + if(!gameover && !quiz_text) continue_animation(); timerflag=0; break; case SDL_MOUSEBUTTONDOWN: if(ev.button.xpixels; + Uint16 pitch=screen->pitch; + int xx,yy; + const unsigned char*f=fontdata+(v<<3); + if(li->h>8) y+=(li->h-8)/2; + if(y+8>=screen->h || x+8>=screen->w) return; + p+=y*pitch+x; + for(yy=0;yy<8;yy++) { + for(xx=0;xx<8;xx++) p[xx]=(*f<h>picture_size) y+=(li->h-picture_size)/2; + if(y+picture_size>=screen->h || x+picture_size>=screen->w) return; + SDL_UnlockSurface(screen); + if(p==t+1 && *t==7) { + // This case is used for %i substitutions + draw_picture(x,y,n); + } else if(p) { + for(i=1;i<0x4000;i++) if(classes[i]) { + if(strlen(classes[i]->name)==p-t && !memcmp(t,classes[i]->name,p-t)) { + if(n>=0 && nnimages) draw_picture(x,y,classes[i]->images[n]&0x7FFF); + break; + } + } + } + SDL_LockSurface(screen); +} + +static void pop_quiz(int x,int y,PopLine*li,Uint8 c,Uint8 v) { + +} + +void draw_popup(const unsigned char*txt) { + static colo[8]={1,144,188,173,83,98,218,15}; + static PopLine li[64]; + SDL_Rect r; + int bx,by,x,y,c; + int ln=0; // line number + int lh=8; // height of current line + int th=0; // total height + int lw=0; // width of current line + int tw=0; // total width + const unsigned char*p=txt; + li[0].w=li[0].h=li[0].a=0; + // Figure out size + while(ln<64 && *p) switch(*p++) { + case 1 ... 8: + // needing doing nothing yet + break; + case 10: + th+=lh; + li[ln].h=lh; + li[ln].w=lw; + if(twscreen->w-32) tw=screen->w-32; + if(th>screen->h-32) th=screen->h-32; + bx=(screen->w-tw-8)/2; + by=(screen->h-th-8)/2; + // Draw box + r.x=bx; r.y=by; r.w=tw+16; r.h=th+16; + SDL_FillRect(screen,&r,0xF7); + r.w=tw+16; r.h=1; SDL_FillRect(screen,&r,0x0F); + r.w=1; r.h=th+16; SDL_FillRect(screen,&r,0x0F); + r.x++; r.y++; + r.w=tw+14; r.h=1; SDL_FillRect(screen,&r,0x0D); + r.w=1; r.h=th+14; SDL_FillRect(screen,&r,0x0D); + r.x++; r.y++; + r.w=tw+12; r.h=1; SDL_FillRect(screen,&r,0x0B); + r.w=1; r.h=th+12; SDL_FillRect(screen,&r,0x0B); + r.x=bx+tw+15; r.y=by; + r.w=1; r.h=th+16; SDL_FillRect(screen,&r,0x02); + r.x--; r.y++; r.h-=2; SDL_FillRect(screen,&r,0x05); + r.x--; r.y++; r.h-=2; SDL_FillRect(screen,&r,0x08); + r.x=bx; r.y=by+th+15; + r.w=tw+16; r.h=1; SDL_FillRect(screen,&r,0x02); + r.x++; r.y--; r.w-=2; SDL_FillRect(screen,&r,0x05); + r.x++; r.y--; r.w-=2; SDL_FillRect(screen,&r,0x08); + // Draw text + bx+=8; by+=8; tw-=8; th-=8; + x=bx; y=by; + c=colo[0]; + ln=0; + SDL_LockSurface(screen); + while(*p && ln<64 && y+li[ln].hh) switch(*p++) { + case 1 ... 8: + c=colo[p[-1]-1]; + break; + case 10: + y+=li[ln++].h; + if(ln<64) x=li[ln].a?bx+(tw-li[ln].w)/2:bx; + break; + case 11: + // do nothing + break; + case 12: + if(x==bx) x+=(tw-li[ln].w)/2; + break; + case 14: + pop_image(x,y,li+ln,p); + x+=picture_size; + p=strchr(p,'\\')?:""; + if(*p) p++; + break; + case 15: + y+=li[ln++].h; + pop_bar(bx,y,li+ln,tw); + y+=8; + if(ln<64) x=li[ln].a?bx+(tw-li[ln].w)/2:bx; + break; + case 16: + pop_quiz(x,y,li+ln,c,*p); + if(*p) p++; + x+=24; + break; + case 31: + pop_char(x,y,li+ln,c,*p); + if(*p) p++; + x+=8; + default: + pop_char(x,y,li+ln,c,p[-1]); + x+=8; + break; + } + SDL_UnlockSurface(screen); +} + +int modal_draw_popup(const unsigned char*txt) { + SDL_Event ev; + SDL_Rect r; + r.x=r.y=0; + r.w=screen->w; + r.h=4; + SDL_FillRect(screen,&r,0xFE); + set_cursor(XC_iron_cross); + redraw: + draw_popup(txt); + SDL_Flip(screen); + while(SDL_WaitEvent(&ev)) switch(ev.type) { + case SDL_QUIT: + SDL_PushEvent(&ev); + return -1; + case SDL_KEYDOWN: + switch(ev.key.keysym.sym) { + case SDLK_RETURN: case SDLK_KP_ENTER: case SDLK_ESCAPE: + return 0; + } + break; + case SDL_MOUSEBUTTONDOWN: + return 0; + case SDL_VIDEOEXPOSE: + goto redraw; + } + return -1; +} +