Trivial Solutions Corp. Happy Programmers Make Us Happy The LoseThos 64-bit PC Operating System File:/LT/Demo/GameStarters/FPS.CPZ This, and all LoseThos files, are public domain. Do whatever you like. AcctRegSetDefaultEntry("LT/FPS","F8 best_score=9999;\r\n"); AcctRegExecuteBranch("LT/FPS"); //Set snap to 4 and width to 4 //if you edit this map. //Don't forget to change the //starting position. #define MAN_START_X 0 #define MAN_START_Y 4.5 <1>/* Graphics Not Rendered in HTML */ #define MONSTER_SCALE 2.0 <2>/* Graphics Not Rendered in HTML */ <3>/* Graphics Not Rendered in HTML */ <4>/* Graphics Not Rendered in HTML */ #define PLANT_SCALE 2.0 <5>/* Graphics Not Rendered in HTML */ #define SCREEN_SCALE 512 #define PLOT_GRID_WIDTH 24 #define PLOT_GRID_HEIGHT 24 #define MAN_HEIGHT 125 #define MAP_SCALE 4 I8 map_width,map_height; U1 *map=NULL, *panels_processed_bitmap=NULL; I8 man_xx,man_yy; F8 man_theta; F8 t0,tf; #define NUM_MONSTERS 16 I8 monsters_left; class monster { I8 x,y; BoolI1 dead,pad[15]; } monsters[NUM_MONSTERS]; U0 FPSTransform(GrBitMap *base,I8 *x,I8 *y,I8 *z) { I8 zz; GrRot(base->r,x,y,z); zz=SCREEN_SCALE/3+*z; if (zz<1) zz=1; *x=SCREEN_SCALE/2* *x/zz; *y=SCREEN_SCALE/2* (*y+MAN_HEIGHT)/zz; *x+=base->x; *y+=base->y; *z+=base->z; } #define LOS_SCALE 4 BoolI8 LOSPlot(U0 *dummy,I8 x,I8 y,I8 z) { nounusedwarn dummy,z; if (!map[(y/LOS_SCALE)*map_width+(x/LOS_SCALE)]) return FALSE; else return TRUE; } BoolI8 LOS(I8 x1,I8 y1,I8 x2,I8 y2) { //Line of sight return Line(NULL,x1*LOS_SCALE/SCREEN_SCALE,y1*LOS_SCALE/SCREEN_SCALE,0, x2*LOS_SCALE/SCREEN_SCALE,y2*LOS_SCALE/SCREEN_SCALE,0,&LOSPlot); } U0 UpdateWin(TaskStruct *task) { GrBitMap *base=GrAlias(gr_refreshed_base,task); I8 i,j,*r1,*r2,*r3,*s2w,xx,yy,zz,x,y,x1,y1,z1, x1w,y1w,x1h,y1h,xh,yh,zh, cx=task->win_pixel_width/2, cy=task->win_pixel_height/2; U1 *inter; F8 tt; P3I4 poly[4]; GrAllocDepthBuf(base); MemSet(panels_processed_bitmap,0,(map_width*map_height+7)>>3); //World to screen GrRotZEqu(base->r,man_theta+pi/2); GrRotXEqu(base->r,pi/2); GrSetRotMat(base,base->r); xh=-man_xx/SCREEN_SCALE; yh=-man_yy/SCREEN_SCALE; zh=0; GrRot(base->r,&xh,&yh,&zh); GrSetTranslation(base->r,xh,yh,zh); //Screen to world s2w=GrRotX(-pi/2,task); GrRotZEqu(s2w,-man_theta-pi/2); xh=0; yh=0; zh=SCREEN_SCALE; GrRot(s2w,&xh,&yh,&zh); //Rotate light source xx=base->ls.x; yy=base->ls.y; zz=-base->ls.z; base->transform(base,&xx,&yy,&zz); base->ls.x=xx; base->ls.y=yy; base->ls.z=zz; base->flags|=BMF_TRANSFORMATION; base->transform=&FPSTransform; base->x=cx; base->y=cy; r1=GrRotX(-pi/2,task); GrRotZEqu(r1,tP(task)); GrScaleMatEqu(r1,MONSTER_SCALE); r2=GrIdent(task); GrScaleMatEqu(r2,MONSTER_SCALE); r3=GrRotX(-pi/2,task); GrScaleMatEqu(r3,PLANT_SCALE); x1h=man_xx+yh*PLOT_GRID_WIDTH/2+xh*PLOT_GRID_HEIGHT; y1h=man_yy-xh*PLOT_GRID_WIDTH/2+yh*PLOT_GRID_HEIGHT; xh>>=1; yh>>=1; for (j=0;j<PLOT_GRID_HEIGHT*2;j++) { x1w=x1h; y1w=y1h; for (i=0;i<PLOT_GRID_WIDTH*4;i++) { xx=x1w/SCREEN_SCALE; yy=y1w/SCREEN_SCALE; x=xx*SCREEN_SCALE-man_xx; y=yy*SCREEN_SCALE-man_yy; if (1<=xx<map_width-1 && 1<=yy<map_height-1 && !LBts(panels_processed_bitmap,yy*map_width+xx)) { if ((base->color=map[yy*map_width+xx]) && LOS(xx*SCREEN_SCALE+SCREEN_SCALE/2,yy*SCREEN_SCALE+SCREEN_SCALE/2, man_xx,man_yy)) { poly[0].x=x; poly[0].y=y; poly[0].z=0; poly[1].x=x+SCREEN_SCALE; poly[1].y=y; poly[1].z=0; poly[2].x=x+SCREEN_SCALE; poly[2].y=y+SCREEN_SCALE; poly[2].z=0; poly[3].x=x; poly[3].y=y+SCREEN_SCALE; poly[3].z=0; GrFillPolygon3(base,4,poly); if (base->color==GREEN) { x1=x+SCREEN_SCALE/2; y1=y+SCREEN_SCALE/2; z1=0; GrTransform(base,&x1,&y1,&z1); if (z1>0) GrElemsPlotRotMat(base,x+SCREEN_SCALE/2,y+SCREEN_SCALE/2,0,<5>,r3); } if (!map[(yy+1)*map_width+xx]) { base->color=WHITE; poly[0].x=x; poly[0].y=y+SCREEN_SCALE; poly[0].z=0; poly[1].x=x+SCREEN_SCALE; poly[1].y=y+SCREEN_SCALE; poly[1].z=0; poly[2].x=x+SCREEN_SCALE; poly[2].y=y+SCREEN_SCALE; poly[2].z=SCREEN_SCALE; poly[3].x=x; poly[3].y=y+SCREEN_SCALE; poly[3].z=SCREEN_SCALE; GrFillPolygon3(base,4,poly); } if (!map[yy*map_width+xx+1]) { base->color=YELLOW; poly[0].x=x+SCREEN_SCALE; poly[0].y=y; poly[0].z=0; poly[1].x=x+SCREEN_SCALE; poly[1].y=y+SCREEN_SCALE; poly[1].z=0; poly[2].x=x+SCREEN_SCALE; poly[2].y=y+SCREEN_SCALE; poly[2].z=SCREEN_SCALE; poly[3].x=x+SCREEN_SCALE; poly[3].y=y; poly[3].z=SCREEN_SCALE; GrFillPolygon3(base,4,poly); } if (!map[(yy-1)*map_width+xx]) { base->color=WHITE; poly[0].x=x; poly[0].y=y; poly[0].z=0; poly[1].x=x+SCREEN_SCALE; poly[1].y=y; poly[1].z=0; poly[2].x=x+SCREEN_SCALE; poly[2].y=y; poly[2].z=SCREEN_SCALE; poly[3].x=x; poly[3].y=y; poly[3].z=SCREEN_SCALE; GrFillPolygon3(base,4,poly); } if (!map[yy*map_width+xx-1]) { base->color=YELLOW; poly[0].x=x; poly[0].y=y; poly[0].z=0; poly[1].x=x; poly[1].y=y+SCREEN_SCALE; poly[1].z=0; poly[2].x=x; poly[2].y=y+SCREEN_SCALE; poly[2].z=SCREEN_SCALE; poly[3].x=x; poly[3].y=y; poly[3].z=SCREEN_SCALE; GrFillPolygon3(base,4,poly); } } } x1w-=yh; y1w+=xh; } x1h-=xh; y1h-=yh; } //Draw Monsters for (i=0;i<NUM_MONSTERS;i++) { x=monsters[i].x; y=monsters[i].y; if (LOS(x,y,man_xx,man_yy)) { x-=man_xx; y-=man_yy; xx=x; yy=y; zz=0; GrTransform(base,&xx,&yy,&zz); if (zz>0) { if (monsters[i].dead) GrElemsPlotRotMat(base,x,y,0,<2>,r2); else { tt=Tri(tP(task),1.0); inter=GrElemsInterpolate(<3>,<4>,tt); GrElemsPlotRotMat(base,x,y,0,inter,r1); Free(inter); } } } } Free(r1); Free(r2); Free(r3); //Draw Map heads-up display, scaled 2 pixels Free(base->r); GrSetRotMat(base,GrIdent(task)); base->x=task->win_pixel_width -2*map_width; base->y=task->win_pixel_height-2*map_height; base->z=0; base->transform=&GrTransform; base->pen_width=2; for (i=0;i<map_height;i++) for (j=0;j<map_width;j++) { base->color=map[(map_height-1-i)*map_width+j]; GrPlot3(base,2*j,2*i,0); } //Draw Things on heads-up Map base->color=LTPURPLE; for (i=0;i<NUM_MONSTERS;i++) if (!monsters[i].dead) GrPlot3(base,2*(monsters[i].x/SCREEN_SCALE),2*(map_height-1-monsters[i].y/SCREEN_SCALE),0); base->color=LTCYAN; GrPlot3(base,2*(man_xx/SCREEN_SCALE),2*(map_height-1-man_yy/SCREEN_SCALE),0); if (tf) { base->color=LTRED; if (Blink(,tP(task))) GrPutS(base,cx-(FONT_WIDTH*14)/2,cy-FONT_HEIGHT/2,"Game Completed"); tt=tf; } else { base->color=LTGREEN; GrLine(base,cx-5,cy,cx+5,cy); GrLine(base,cx,cy-5,cx,cy+5); tt=tP(task); } GrPrintF(base,0,0,"Enemy:%d Time:%3.2f Best:%3.2f",monsters_left,tt-t0,best_score); Free(s2w); GrDel(base); } U0 Fire() { I8 i,x,y; F8 d,dx,dy,xx=Cos(man_theta),yy=Sin(man_theta); Noise(100,300,1000); for (i=0;i<NUM_MONSTERS;i++) { x=monsters[i].x; y=monsters[i].y; if (!monsters[i].dead && LOS(x,y,man_xx,man_yy)) { dx=x-man_xx; dy=man_yy-y; if (d=Sqrt(dx*dx+dy*dy)) { dx/=d; dy/=d; if (dx*xx+dy*yy>0.995) { monsters[i].dead=TRUE; if (!--monsters_left) { tf=tP; if (tf-t0<best_score) best_score=tf-t0; } } } } } } U0 Init() { I8 i,x,y; I8 min_map_x,max_map_x,min_map_y,max_map_y; GrBitMap *base; GrElemsExtents(<1>,&min_map_x,&max_map_x,&min_map_y,&max_map_y); map_width =(max_map_x-min_map_x+1)/MAP_SCALE+2; map_height=(max_map_y-min_map_y+1)/MAP_SCALE+2; Free(map); Free(panels_processed_bitmap); map=CAlloc(map_width*map_height*sizeof(U1)); panels_processed_bitmap=MAlloc((map_width*map_height+7)>>3); base=GrNew(BMT_COLOR4,map_width*MAP_SCALE,map_height*MAP_SCALE); GrElemsPlot(base,-min_map_x+MAP_SCALE,-min_map_y+MAP_SCALE,0,<1>); for (y=1;y<map_height-1;y++) for (x=1;x<map_width-1;x++) map[(map_height-1-y)*map_width+x]=GrPeek(base,x*MAP_SCALE,y*MAP_SCALE); GrDel(base); man_xx=(1+MAN_START_X)*SCREEN_SCALE; man_yy=(map_height-1-MAN_START_Y)*SCREEN_SCALE; man_theta=0; for (i=0;i<NUM_MONSTERS;i++) { monsters[i].dead=FALSE; do { monsters[i].x=RandU8%((map_width-2)*SCREEN_SCALE)+SCREEN_SCALE; monsters[i].y=RandU8%((map_height-2)*SCREEN_SCALE)+SCREEN_SCALE; } while (!map[(monsters[i].y/SCREEN_SCALE)*map_width+monsters[i].x/SCREEN_SCALE]); } monsters_left=NUM_MONSTERS; tf=0; t0=tP; } U0 AnimateTask() { I8 i,x,y,dd; while (TRUE) { dd=0.25*SCREEN_SCALE*Sin(tP(Fs->parent_task)/2); for (i=0;i<NUM_MONSTERS;i++) if (!monsters[i].dead) { x=monsters[i].x; y=monsters[i].y; if (i&1) x+=dd; else y+=dd; if (0<=x<=map_width*SCREEN_SCALE && 0<=y<=map_height*SCREEN_SCALE && map[(y/SCREEN_SCALE)*map_width+x/SCREEN_SCALE]) { if (!map[(y/SCREEN_SCALE)*map_width+x/SCREEN_SCALE+1] && x-RoundDownI8(x,SCREEN_SCALE)>SCREEN_SCALE/2 || !map[(y/SCREEN_SCALE)*map_width+x/SCREEN_SCALE-1] && x-RoundDownI8(x,SCREEN_SCALE)<SCREEN_SCALE/2) x=RoundDownI8(x,SCREEN_SCALE)+SCREEN_SCALE/2; if (!map[(y/SCREEN_SCALE+1)*map_width+x/SCREEN_SCALE] && y-RoundDownI8(y,SCREEN_SCALE)>SCREEN_SCALE/2 || !map[(y/SCREEN_SCALE-1)*map_width+x/SCREEN_SCALE] && y-RoundDownI8(y,SCREEN_SCALE)<SCREEN_SCALE/2) y=RoundDownI8(y,SCREEN_SCALE)+SCREEN_SCALE/2; monsters[i].x=x; monsters[i].y=y; } } Sleep(20); } } U0 CleanUp() { Free(map); Free(panels_processed_bitmap); map=NULL; panels_processed_bitmap=NULL; } U0 SongTask() { //Song by Terry A. Davis Fs->task_end_cb=&SndTaskEndCB; MusicSettingsReset; while (TRUE) { Play("W12q.A#1eG2AeA#qAq.A#1eG2eAeA#qA"); Play("q.A#1eG2A#A1qG2q.A#1eG2A#A1qG"); Play("3eA#A2qG3eA#A2qG3eA#A2G3AA#A2qG"); } } U0 MoveMan(F8 theta) { I8 x,y,color,step=SCREEN_SCALE/2; do { x=man_xx+step*Cos(theta); y=man_yy-step*Sin(theta); x=Limit(x,0,map_width*SCREEN_SCALE); y=Limit(y,0,map_height*SCREEN_SCALE); color=map[y/SCREEN_SCALE*map_width+x/SCREEN_SCALE]; if (color==RED || color==GREEN) { man_xx=x; man_yy=y; break; } else step>>=1; } while (step); } U0 FPS() { I8 msg_code,p1,p2,ch,sc; PopUpOk( "I refuse to rip-off the original\r\n" "so this is intentionally crappy\r\n" "and included for demonstration\r\n" "purposes.\r\n\r\n" "Write games, don't play them.\r\n"); MenuPush( "File {" " Abort(,CH_CTRLQ);" " Exit(,CH_ESC);" "}" "Play {" " Restart(,CH_CR);" " Forward(,,SC_CURSOR_UP);" " Backward(,,SC_CURSOR_DOWN);" " Left(,,SC_CURSOR_LEFT);" " Right(,,SC_CURSOR_RIGHT);" " Fire(,CH_SPACE);" "}" ); SettingsPush; //See SettingsPush WinMax; Init; Fs->animate_task=Spawn(&AnimateTask,NULL,"Animate",Fs); Fs->song_task=Spawn(&SongTask,NULL,"Song",Fs); Fs->text_attr=WHITE+BLACK<<4; WordStat(OFF); //The text layer under the graphics lags a frame //therefore the fill operations screw-up without this. WinTextClear; try { Fs->update_win=&UpdateWin; do { Init; ch=0; do { while (msg_code=ScanMsg(&p1,&p2,1<<MSG_KEY_DOWN|1<<MSG_KEY_UP)) { ch=p1; sc=p2; if (msg_code==MSG_KEY_DOWN) { switch (ch) { case CH_SPACE: Fire; break; case 0: switch (sc.u1[0]) { case SC_CURSOR_RIGHT: man_theta+=pi/32; break; case SC_CURSOR_LEFT: man_theta-=pi/32; break; case SC_CURSOR_UP: MoveMan(man_theta); break; case SC_CURSOR_DOWN: MoveMan(man_theta+pi); break; } break; } } } WinSync; //msgs are only queued by winmngr } while (ch!=CH_ESC && ch!=CH_CR && ch!=CH_CTRLQ); } while (ch!=CH_ESC && ch!=CH_CTRLQ); } catch Fs->catch_except=TRUE; SettingsPop; CleanUp; MenuPop; AcctRegWriteBranch("LT/FPS","F8 best_score=%5.4f;\r\n",best_score); } FPS;