Trivial Solutions Happy Programmers Make Us Happy The LoseThos 64-bit PC Operating System File:/LT/Apps/X-Caliber/X-Caliber.CPZ This, and all LoseThos files, are public domain. Do whatever you like. God is watching; God is just; God is never out-done in generosity. He talks to me! LoseThos code compiles with the 64-bit LoseThos compiler/assembler I wrote. Boot the LoseThos CD and you can compile it. Not in Kansas anymore! Click here for preliminary compiler information you need. /* I did not limit myself to real physics. It's a game. I suppose it shoots plazma blobs that have a delayed explosion due to a chemical reaction timer. The caliber of the gun is adjustible. I did a simple anti-spin controller when you press cursor-down. It's intentionally not very good. It needs to take into account flexing of the struts. You'll notice when you try to shoot the laser. If you are interested in control theory, you might download simstructure, a program I wrote. http://www.losethos.com/files/SimStrSetup.exe Product Key: L00-W10-M70 There's not sound in space and explosions don't make shock waves, or something like that. In X-Caliber, ships are destroyed by causing springs to break due to acceleration differences on different masses in a ship. A real game would be dull with guided missiles that always hit and lasers that never miss. Don't be anal. If you aspire to making games, do like Hollywood. They know the public isn't interested in realism. */ // ********************************** Glbls AcctRegSetDftEntry("LT/XCaliber","I64 best_score=0;\r\n"); AcctRegExecuteBranch("LT/XCaliber"); class MyMass { MassStruct m; F64 temperature; F64 radius; I64 color; }; class MySpring { SpringStruct s; F64 strength; I64 color; }; #define SPIN_GAIN 0.25 #define MAX_MASSES 8 #define MAX_SPRINGS 16 #define ST_HUMAN1 0 #define ST_ENEMY1 1 #define ST_ENEMY2 2 class Ship { Ship *next,*last; I64 type,masses,springs; MyMass p[MAX_MASSES]; MySpring s[MAX_SPRINGS]; F64 fire_rate; F64 reload_timeout,spacewalk_timeout; F64 die_time,die_timeout; I64 spacewalk_side; F64 laser_temperature; BoolI8 lasering,exploding,laser_overheat; } *human; F64 human_t_left,human_t_right,human_antispin; class Bullet { Bullet *next,*last; F64 radius; F64 fuse_time,die_timeout; MyMass p; BoolI8 exploding; }; #define MAX_THRUST 200.0 #define MAX_ANTISPIN 25.0 #define SPACEWALK_TIME 7.5 #define CMD_NULL 0 #define CMD_SPIN_LEFT 1 #define CMD_SPIN_RIGHT 2 #define CMD_THRUST 3 #define CMD_FIRE 4 #define CMD_EXIT 5 BoolI8 game_over,show_level_msg; #define NUM_STARS 100 I64 stars_x[NUM_STARS],stars_y[NUM_STARS]; Ship ship_root; Bullet bullet_root; Ode *ode=NULL; I64 level,score,remaining; // ********************************** Pictures <thruster>/* Graphics Not Rendered in HTML */ <gun_ready>/* Graphics Not Rendered in HTML */ <gun_busy>/* Graphics Not Rendered in HTML */ <spacewalk>/* Graphics Not Rendered in HTML */ <EnemySide>/* Graphics Not Rendered in HTML */ <Laser>/* Graphics Not Rendered in HTML */ // ********************************** Ship BoolI64 CheckOverlap() { D3 p; MyMass *tempm,*tempm1; tempm=ode->next_mass; while (tempm!=&ode->next_mass) { tempm1=ode->next_mass; while (tempm1!=&ode->next_mass) { if (tempm!=tempm1) { D3Sub(&tempm->m.x,&tempm1->m.x,&p); if (D3SqrNorm(&p)<=Sqr(tempm->radius+tempm1->radius)) return TRUE; } tempm1=tempm1->m.next; } tempm=tempm->m.next; } return FALSE; } Ship *ShipNew(I64 x,I64 y,I64 type) { I64 i; Ship *temps=CAlloc(sizeof(Ship)); switch (temps->type=type) { case ST_HUMAN1: temps->fire_rate=25; temps->masses=5; temps->p[0].m.x=x; temps->p[0].m.y=y; temps->p[1].m.x=x+3; temps->p[1].m.y=y+10; temps->p[2].m.x=x-3; temps->p[2].m.y=y+10; temps->p[3].m.x=x+20; temps->p[3].m.y=y+20; temps->p[4].m.x=x-20; temps->p[4].m.y=y+20; for (i=0;i<temps->masses;i++) { temps->p[i].m.mass=1; if (i<3) temps->p[i].radius=2.5; else temps->p[i].radius=4; temps->p[i].m.drag_profile_factor=3; InsQue(&temps->p[i].m,ode->last_mass); } temps->p[3].m.mass/=10.0; temps->p[4].m.mass/=10.0; temps->springs=7; temps->s[0].s.end1=&temps->p[0].m; temps->s[0].s.end2=&temps->p[1].m; temps->s[1].s.end1=&temps->p[2].m; temps->s[1].s.end2=&temps->p[0].m; temps->s[2].s.end1=&temps->p[1].m; temps->s[2].s.end2=&temps->p[2].m; temps->s[3].s.end1=&temps->p[1].m; temps->s[3].s.end2=&temps->p[3].m; temps->s[4].s.end1=&temps->p[0].m; temps->s[4].s.end2=&temps->p[3].m; temps->s[5].s.end1=&temps->p[2].m; temps->s[5].s.end2=&temps->p[4].m; temps->s[6].s.end1=&temps->p[0].m; temps->s[6].s.end2=&temps->p[4].m; for (i=0;i<temps->springs;i++) { temps->s[i].s.rest_len=D3Dist(&temps->s[i].s.end1->x,&temps->s[i].s.end2->x); temps->s[i].s.constant=10000; temps->s[i].strength =30000; if (i<=2) temps->s[i].color=LTCYAN; else temps->s[i].color=LTGRAY; InsQue(&temps->s[i].s,ode->last_spring); } remaining=0; break; case ST_ENEMY1: temps->fire_rate=2.5; temps->masses=3; temps->p[0].m.x=x; temps->p[0].m.y=y; temps->p[1].m.x=x+15; temps->p[1].m.y=y; temps->p[2].m.x=x; temps->p[2].m.y=y+15; for (i=0;i<temps->masses;i++) { temps->p[i].m.mass=1; temps->p[i].radius=7; temps->p[i].m.drag_profile_factor=3; InsQue(&temps->p[i].m,ode->last_mass); } temps->springs=3; temps->s[0].s.end1=&temps->p[0].m; temps->s[0].s.end2=&temps->p[1].m; temps->s[1].s.end1=&temps->p[1].m; temps->s[1].s.end2=&temps->p[2].m; temps->s[2].s.end1=&temps->p[2].m; temps->s[2].s.end2=&temps->p[0].m; for (i=0;i<temps->springs;i++) { temps->s[i].s.rest_len=D3Dist(&temps->s[i].s.end1->x,&temps->s[i].s.end2->x); temps->s[i].s.constant=10000; temps->s[i].strength =20000; temps->s[i].color=BLACK; InsQue(&temps->s[i].s,ode->last_spring); } remaining++; break; case ST_ENEMY2: temps->fire_rate=5.0; temps->masses=5; temps->p[0].m.x=x; temps->p[0].m.y=y; temps->p[1].m.x=x-7; temps->p[1].m.y=y+10; temps->p[2].m.x=x+7; temps->p[2].m.y=y+10; temps->p[3].m.x=x-14; temps->p[3].m.y=y+20; temps->p[4].m.x=x+14; temps->p[4].m.y=y+20; for (i=0;i<temps->masses;i++) { temps->p[i].m.mass=1; temps->p[i].radius=6; temps->p[i].m.drag_profile_factor=5; temps->p[i].color=PURPLE; InsQue(&temps->p[i].m,ode->last_mass); } temps->springs=7; temps->s[0].s.end1=&temps->p[0].m; temps->s[0].s.end2=&temps->p[1].m; temps->s[1].s.end1=&temps->p[0].m; temps->s[1].s.end2=&temps->p[2].m; temps->s[2].s.end1=&temps->p[1].m; temps->s[2].s.end2=&temps->p[2].m; temps->s[3].s.end1=&temps->p[1].m; temps->s[3].s.end2=&temps->p[3].m; temps->s[4].s.end1=&temps->p[2].m; temps->s[4].s.end2=&temps->p[4].m; temps->s[5].s.end1=&temps->p[2].m; temps->s[5].s.end2=&temps->p[3].m; temps->s[6].s.end1=&temps->p[1].m; temps->s[6].s.end2=&temps->p[4].m; for (i=0;i<temps->springs;i++) { temps->s[i].s.rest_len=D3Dist(&temps->s[i].s.end1->x,&temps->s[i].s.end2->x); temps->s[i].s.constant= 40000; temps->s[i].strength =75000; if (i>=3) temps->s[i].color=LTPURPLE; else temps->s[i].color=BLACK; InsQue(&temps->s[i].s,ode->last_spring); } remaining++; break; } InsQue(temps,ship_root.last); return temps; } U0 ShipDel(Ship *temps) { I64 i; if (!temps) return; for (i=0;i<temps->masses;i++) RemQue(&temps->p[i].m); for (i=0;i<temps->springs;i++) RemQue(&temps->s[i]); RemQue(temps); Free(temps); remaining--; } U0 PlaceShip(I64 type) { Ship *ee; if (CheckOverlap) return; while (TRUE) { ee=ShipNew(RandU16%(Fs->win_pixel_width-20)+10,RandU16%(Fs->win_pixel_height-20)+10,type); if (CheckOverlap) ShipDel(ee); else break; } } // ********************************** Human Ship I64 Tweaked() { D3 p,p1,p2; if (human) { D3Sub(&human->p[0].m.x,&human->p[1].m.x,&p1); D3Sub(&human->p[0].m.x,&human->p[2].m.x,&p2); D3Add(&p1,&p2,&p); D3Unit(&p); D3Sub(&human->p[0].m.x,&human->p[3].m.x,&p1); D3Sub(&human->p[0].m.x,&human->p[4].m.x,&p2); D3Unit(&p1); D3Unit(&p2); if (!(human->s[3].s.flags&SSF_INACTIVE) && D3Dot(&p,&p1)>Cos(20*pi/180)) return 3; if (!(human->s[5].s.flags&SSF_INACTIVE) && D3Dot(&p,&p2)>Cos(20*pi/180)) return 4; return 0; } } U0 AllDel(Ode *ode) { Ship *temps,*temps1; Bullet *tempb=bullet_root.next,*tempb1; RemQue(ode); temps=ship_root.next; while (temps!=&ship_root) { temps1=temps->next; ShipDel(temps); temps=temps1; } human=NULL; while (tempb!=&bullet_root) { tempb1=tempb->next; RemQue(tempb); Free(tempb); tempb=tempb1; } OdeDel(ode); } BoolI64 LaserPlot(GrBitMap *base,I64 x,I64 y,I64 z) { I64 c; nounusedwarn z; c=GrPeek(base,x,y); if (c!=BLACK && c!=WHITE) return FALSE; else { GrPlot(base,x,y); return TRUE; } } // ********************************** U0 UpdateWin(TaskStruct *task) { I64 i,j; GrBitMap *base=GrAlias(gr_refreshed_base,task); F64 arg; Ship *temps; Bullet *tempb; D3 p,p1,p2; F64 t_left,t_right,spin,d,x,y; MySpring *tempss; U8 *img; BoolI8 draw_laser_line=FALSE; if (ode!=task->last_ode) goto done_draw; base->color=WHITE; GrPrintF(base,0,0,"Level:%d Score:%d High Score:%d",level,score,best_score); if (game_over) { if (Blink(,tP(task))) GrPutS(base,(task->win_pixel_width-9*FONT_WIDTH)/2, (task->win_pixel_height-FONT_HEIGHT)/2,"Game Over"); } else if (show_level_msg) { if (Blink(,tP(task))) GrPrintF(base,(task->win_pixel_width-8*FONT_WIDTH)/2, (task->win_pixel_height-FONT_HEIGHT)/2+50,"Level %d",level); } for (i=0;i<NUM_STARS;i++) GrPlot(base,stars_x[i],stars_y[i]); tempss=ode->next_spring; while (tempss!=&ode->next_spring) { if (!(tempss->s.flags&SSF_INACTIVE)) { base->color=tempss->color; GrLine(base,tempss->s.end1->x,tempss->s.end1->y, tempss->s.end2->x,tempss->s.end2->y); } tempss=tempss->s.next; } temps=ship_root.next; while (temps!=&ship_root) { if (!temps->exploding) switch (temps->type) { case ST_HUMAN1: if (temps->spacewalk_side) { t_left=0; t_right=0; } else { if (d=D3Norm(D3Sub(&temps->p[0].m.x,&temps->p[1].m.x,&p1))) { D3Sub(&temps->p[0].m.DxDt,&temps->p[1].m.DxDt,&p2); D3Cross(&p1,&p2,&p); spin=p.z/d; } else spin=0; t_left =Limit(human_t_left+SPIN_GAIN*spin*human_antispin,0,MAX_THRUST); if (d=D3Norm(D3Sub(&temps->p[0].m.x,&temps->p[2].m.x,&p1))) { D3Sub(&temps->p[0].m.DxDt,&temps->p[2].m.DxDt,&p2); D3Cross(&p1,&p2,&p); spin=p.z/d; } else spin=0; t_right=Limit(human_t_right-SPIN_GAIN*spin*human_antispin,0,MAX_THRUST); } D3Sub(&temps->p[1].m.x,&temps->p[0].m.x,&p1); D3Sub(&temps->p[2].m.x,&temps->p[0].m.x,&p2); D3Unit(D3Add(&p1,&p2,&p)); if (!(temps->s[3].s.flags&SSF_INACTIVE)) { base->color=YELLOW; D3AddEqu(D3Mul(t_left/25,&p,&p1),&temps->p[3].m.x); GrLine(base,temps->p[1].m.x,temps->p[1].m.y,p1.x,p1.y); arg=Arg(p.x,p.y); GrElemsPlotRotZ3b(base,temps->p[3].m.x,temps->p[3].m.y,0,<thruster>,arg); } if (!(temps->s[5].s.flags&SSF_INACTIVE)) { base->color=YELLOW; D3AddEqu(D3Mul(t_right/25,&p,&p2),&temps->p[4].m.x); GrLine(base,temps->p[2].m.x,temps->p[2].m.y,p2.x,p2.y); arg=Arg(p.x,p.y); GrElemsPlotRotZ3b(base,temps->p[4].m.x,temps->p[4].m.y,0,<thruster>,arg); } if (tP(task)>temps->reload_timeout) img=<gun_ready>; else img=<gun_busy>; arg=Arg(p.x,p.y); switch (level) { case 3: if (!(temps->s[3].s.flags&SSF_INACTIVE)) GrElemsPlotRotZ3b(base,temps->p[3].m.x,temps->p[3].m.y,0,img,arg); if (!(temps->s[5].s.flags&SSF_INACTIVE)) GrElemsPlotRotZ3b(base,temps->p[4].m.x,temps->p[4].m.y,0,img,arg); case 2: if (!(temps->s[1].s.flags&SSF_INACTIVE)) GrElemsPlotRotZ3b(base,temps->p[1].m.x,temps->p[1].m.y,0,img,arg); if (!(temps->s[2].s.flags&SSF_INACTIVE)) GrElemsPlotRotZ3b(base,temps->p[2].m.x,temps->p[2].m.y,0,img,arg); case 1: GrElemsPlotRotZ3b(base,temps->p[0].m.x,temps->p[0].m.y,0,img,arg); break; default: GrElemsPlotRotZ3b(base,temps->p[0].m.x,temps->p[0].m.y,0,<Laser>,arg); if (temps->lasering && !temps->laser_overheat) { draw_laser_line=TRUE; Snd(1000); } break; } ctrl_panel.laser_temperature=temps->laser_temperature; if (temps->spacewalk_side) { d=1.0-(temps->spacewalk_timeout-tP(task))/SPACEWALK_TIME; if (d>1.0) { temps->spacewalk_side=0; ctrl_panel.spacewalk=FALSE; } else { if (d<0.5) { d=d*2; x=temps->p[0].m.x*(1.0-d)+temps->p[temps->spacewalk_side].m.x*(d); y=temps->p[0].m.y*(1.0-d)+temps->p[temps->spacewalk_side].m.y*(d); } else { d=(d-0.5)*2; x=temps->p[temps->spacewalk_side].m.x*(1.0-d)+temps->p[0].m.x*(d); y=temps->p[temps->spacewalk_side].m.y*(1.0-d)+temps->p[0].m.y*(d); } GrElemsPlotRotZ3b(base,x,y,0,<spacewalk>,arg+0.75*Sin(tP(task)*2)); } } else { if (ctrl_panel.spacewalk) { if (temps->spacewalk_side=Tweaked) temps->spacewalk_timeout=tP(task)+SPACEWALK_TIME; else ctrl_panel.spacewalk=FALSE; } } break; case ST_ENEMY2: for (i=3;i<temps->masses;i++) { base->color=temps->p[i].color; GrCircle(base,temps->p[i].m.x,temps->p[i].m.y,temps->p[i].radius); GrFloodFill(base,temps->p[i].m.x,temps->p[i].m.y+2,TRUE); base->color=WHITE; GrCircle(base,temps->p[i].m.x,temps->p[i].m.y,temps->p[i].radius); } case ST_ENEMY1: D3DivEqu(D3Sub(&temps->p[1].m.x,&temps->p[0].m.x,&p1),2.0); D3DivEqu(D3Sub(&temps->p[2].m.x,&temps->p[0].m.x,&p2),2.0); D3Unit(D3Add(&p1,&p2,&p)); if (tP(task)>temps->reload_timeout) img=<gun_ready>; else img=<gun_busy>; arg=Arg(p.x,p.y); GrElemsPlotRotZ3b(base,temps->p[0].m.x,temps->p[0].m.y,0,img,arg); arg=Arg(p1.x,p1.y); GrElemsPlotRotZ3b(base,temps->p[0].m.x+p1.x,temps->p[0].m.y+p1.y,0,<EnemySide>,arg); arg=Arg(p2.x,p2.y); GrElemsPlotRotZ3b(base,temps->p[0].m.x+p2.x,temps->p[0].m.y+p2.y,0,<EnemySide>,arg); for (i=0;i<temps->masses;i++) { base->color=YELLOW; if (temps->p[i].temperature>=1.0) GrCircle(base,temps->p[i].m.x,temps->p[i].m.y,temps->p[i].temperature); } break; } else if (temps->die_time<=tP(task)<=temps->die_timeout) for (j=0;j<temps->masses;j++) { d=(tP(task)-temps->die_time)/(temps->die_timeout-temps->die_time); d=7*Sin(pi*d)*(6+j)+1; for (i=1;i<d;i++) { if (i&1) base->color=YELLOW; else base->color=LTRED; GrCircle(base,temps->p[j].m.x,temps->p[j].m.y,i); } } temps=temps->next; } tempb=bullet_root.next; while (tempb!=&bullet_root) { if (tP(task)>tempb->fuse_time) { d=(tP(task)-tempb->fuse_time)/(tempb->die_timeout-tempb->fuse_time); d=7*Sin(pi*d)*tempb->radius+1; for (i=1;i<d;i++) { if (i&1) base->color=YELLOW; else base->color=LTRED; GrCircle(base,tempb->p.m.x,tempb->p.m.y,i); } } else { if (tempb->radius<1.0) { base->color=LTGREEN; GrPlot(base,tempb->p.m.x,tempb->p.m.y); } else { base->color=YELLOW; GrCircle(base,tempb->p.m.x,tempb->p.m.y,tempb->radius); if (tempb->radius>=2.0) GrFloodFill(base,tempb->p.m.x,tempb->p.m.y,TRUE); base->color=LTGREEN; GrCircle(base,tempb->p.m.x,tempb->p.m.y,tempb->radius); } } tempb=tempb->next; } if (human && draw_laser_line) { D3Sub(&human->p[1].m.x,&human->p[0].m.x,&p1); D3Sub(&human->p[2].m.x,&human->p[0].m.x,&p2); D3Unit(D3Add(&p1,&p2,&p)); base->color=LTBLUE; Line(base,human->p[0].m.x-10*p.x,human->p[0].m.y-10*p.y,0,human->p[0].m.x-800*p.x,human->p[0].m.y-800*p.y,0,&LaserPlot); } done_draw: GrDel(base); } U0 Explosion(MyMass *tempm1,F64 r) { MyMass *tempm; D3 p1; F64 d; tempm=ode->next_mass; while (tempm!=&ode->next_mass) { if (tempm!=tempm1) { D3Sub(&tempm->m.state->x,&tempm1->m.state->x,&p1); d=D3SqrNorm(&p1)-tempm->radius*tempm->radius; if (d<100.0*100.0) { if (d<1) d=1; else d=Sqrt(d); d=2e8*r`7/d`6; if (d>1e5) d=1e5; D3MulEqu(&p1,d); D3AddEqu(&tempm->m.DstateDt->DxDt,&p1); } } tempm=tempm->m.next; } } U0 MyDerivative(Ode *ode,F64 t,Order2D3 *state,Order2D3 *DstateDt) { nounusedwarn t,state,DstateDt; I64 i; F64 d,dd,dd2,spin,t_left,t_right; TaskStruct *task=ode->win_task; D3 p,p1,p2; Bullet *tempb; Ship *temps; MyMass *tempm,*tempm1; tempm=ode->next_mass; while (tempm!=&ode->next_mass) { d=tempm->m.state->x; if (d-tempm->radius<0) tempm->m.DstateDt->DxDt+=Sqr(Sqr(Sqr(d-tempm->radius))); if (d+tempm->radius>task->win_pixel_width) tempm->m.DstateDt->DxDt-=Sqr(Sqr(Sqr((d+tempm->radius)-task->win_pixel_width))); d=tempm->m.state->y; if (d-tempm->radius<0) tempm->m.DstateDt->DyDt+=Sqr(Sqr(Sqr(d-tempm->radius))); if (d+tempm->radius>task->win_pixel_height) tempm->m.DstateDt->DyDt-=Sqr(Sqr(Sqr((d+tempm->radius)-task->win_pixel_height))); tempm1=ode->next_mass; while (tempm1!=&ode->next_mass) { if (tempm!=tempm1) { D3Sub(&tempm->m.state->x,&tempm1->m.state->x,&p); dd=D3SqrNorm(&p); dd2=Sqr(tempm->radius+tempm1->radius); if (dd<=dd2) { d=Sqrt(dd)+0.0001; D3MulEqu(&p,Sqr(Sqr(dd2-dd))/d); D3AddEqu(&tempm ->m.DstateDt->DxDt,&p); D3SubEqu(&tempm1->m.DstateDt->DxDt,&p); } } tempm1=tempm1->m.next; } tempm=tempm->m.next; } temps=ship_root.next; while (temps!=&ship_root) { if (temps->exploding && temps->die_time<=tP(ode->win_task)<=temps->die_timeout) for (i=0;i<temps->masses;i++) Explosion(&temps->p[i].m,temps->p[i].radius/3.0); switch (temps->type) { case ST_HUMAN1: if (!temps->exploding) { if (temps->spacewalk_side) { t_left=0; t_right=0; d=1.0-(temps->spacewalk_timeout-tP(ode->win_task))/SPACEWALK_TIME; if (0.485<d<0.515) { D3Unit(D3Sub(&temps->p[2].m.state->x,&temps->p[1].m.state->x,&p)); if (temps->spacewalk_side==3) { temps->p[3].m.DstateDt->DxDt-=10*MAX_THRUST*p.x; temps->p[3].m.DstateDt->DyDt-=10*MAX_THRUST*p.y; temps->p[1].m.DstateDt->DxDt+=10*MAX_THRUST*p.x; temps->p[1].m.DstateDt->DyDt+=10*MAX_THRUST*p.y; } else { temps->p[4].m.DstateDt->DxDt+=10*MAX_THRUST*p.x; temps->p[4].m.DstateDt->DyDt+=10*MAX_THRUST*p.y; temps->p[2].m.DstateDt->DxDt-=10*MAX_THRUST*p.x; temps->p[2].m.DstateDt->DyDt-=10*MAX_THRUST*p.y; } } } else { if (d=D3Norm(D3Sub(&temps->p[0].m.state->x,&temps->p[1].m.state->x,&p1))) { D3Sub(&temps->p[0].m.state->DxDt,&temps->p[1].m.state->DxDt,&p2); D3Cross(&p1,&p2,&p); spin=p.z/d; } else spin=0; t_left =Limit(human_t_left+SPIN_GAIN*spin*human_antispin,0,MAX_THRUST); if (d=D3Norm(D3Sub(&temps->p[0].m.state->x,&temps->p[2].m.state->x,&p1))) { D3Sub(&temps->p[0].m.state->DxDt,&temps->p[2].m.state->DxDt,&p2); D3Cross(&p1,&p2,&p); spin=p.z/d; } else spin=0; t_right=Limit(human_t_right-SPIN_GAIN*spin*human_antispin,0,MAX_THRUST); D3Sub(&temps->p[0].m.state->x,&temps->p[1].m.state->x,&p1); D3Sub(&temps->p[0].m.state->x,&temps->p[2].m.state->x,&p2); D3Unit(D3Add(&p1,&p2,&p)); if (!(temps->s[3].s.flags&SSF_INACTIVE)) { D3Mul(t_left,&p,&p1); D3AddEqu(&temps->p[3].m.DstateDt->DxDt,&p1); } if (!(temps->s[5].s.flags&SSF_INACTIVE)) { D3Mul(t_right,&p,&p2); D3AddEqu(&temps->p[4].m.DstateDt->DxDt,&p2); } } } break; } temps=temps->next; } tempb=bullet_root.next; while (tempb!=&bullet_root) { if (tempb->fuse_time<=tP(ode->win_task)<=tempb->die_timeout) Explosion(&tempb->p.m,tempb->radius); tempb=tempb->next; } } U0 CheckDamage() { I64 i,j,k; Ship *temps,*temps1; MyMass *tempm,*best_mass; D3 p,p1,p2; F64 d,best_distance; tempm=ode->next_mass; while (tempm!=&ode->next_mass) { tempm->temperature*=0.9; tempm=tempm->m.next; } if (human) { human->laser_temperature*=0.975; if (human->laser_overheat) { if (human->laser_temperature<LASER_THRESHOLD_TEMP) human->laser_overheat=FALSE; } if (!human->laser_overheat && human->lasering) { if (human->laser_temperature>=LASER_MAX_TEMP) { human->laser_overheat=TRUE; Snd(0); } else { human->laser_temperature+=1.0; D3Sub(&human->p[0].m.x,&human->p[1].m.x,&p1); D3Sub(&human->p[0].m.x,&human->p[2].m.x,&p2); D3Unit(D3Add(&p1,&p2,&p)); p2.x=p.y; p2.y=-p.x; p2.z=0; best_mass=NULL; best_distance=MAX_F64; tempm=ode->next_mass; while (tempm!=&ode->next_mass) { D3Sub(&human->p[0].m.x,&tempm->m.x,&p1); if (Abs(D3Dot(&p1,&p2))<tempm->radius && D3Dot(&p1,&p)<0.0) { d=D3SqrNorm(&p1); if (d<best_distance) { best_distance=d; best_mass=tempm; } } tempm=tempm->m.next; } if (best_mass) best_mass->temperature+=1.0; } } } temps=ship_root.next; while (temps!=&ship_root) { temps1=temps->next; switch (temps->type) { case ST_HUMAN1: if (temps->exploding) { if (tP>temps->die_timeout) { ShipDel(temps); human=NULL; } } else for (i=0;i<temps->springs;i++) { if (Abs(temps->s[i].s.f)>temps->s[i].strength) temps->s[i].s.flags|=SSF_INACTIVE; if (temps->s[i].s.flags&SSF_INACTIVE) { if (i<3) { if (!temps->exploding) { temps->exploding=TRUE; temps->die_time=tP; temps->die_timeout=tP+0.75; game_over=TRUE; Noise(750,1000,2000); break; } } } } break; default: if (temps->exploding) { if (tP>temps->die_timeout) { ShipDel(temps); score+=level; if (score>best_score) best_score=score; } } else { j=0; for (i=0;i<temps->springs;i++) { if (temps->s[i].s.flags&SSF_INACTIVE) j++; else if (Abs(temps->s[i].s.f)>temps->s[i].strength) { temps->s[i].s.flags|=SSF_INACTIVE; j++; } } k=0; for (i=0;i<temps->masses;i++) { if (temps->p[i].temperature>MASS_MAX_TEMP) k++; } if (j>1 || k) { temps->exploding=TRUE; temps->die_time=tP; temps->die_timeout=tP+0.75; Noise(750,1000,3000); } } break; } temps=temps1; } } // ********************************** Bullets U0 FireOneGun(Ship *temps,I64 g,F64 r,F64 fuse_time) { D3 p,p1,p2; Bullet *tempb; tempb=CAlloc(sizeof(Bullet)); D3Copy(&tempb->p.m.x,&temps->p[g].m.x); tempb->radius=r; tempb->fuse_time=tP+1.5*fuse_time; tempb->die_timeout=tempb->fuse_time+0.125; tempb->p.m.mass=0.3*r*r*r; D3Sub(&temps->p[0].m.x,&temps->p[1].m.x,&p1); D3Sub(&temps->p[0].m.x,&temps->p[2].m.x,&p2); D3Add(&p1,&p2,&p); D3AddEqu(&tempb->p.m.x,D3MulEqu(D3Copy(&p1,D3Unit(&p)),r+temps->p[0].radius+5)); D3AddEqu(D3MulEqu(&p,1000/(r+1)),&temps->p[g].m.DxDt); D3Copy(&tempb->p.m.DxDt,&p); D3MulEqu(&p,tempb->p.m.mass/temps->p[g].m.mass/100.0); D3SubEqu(&temps->p[g].m.DxDt,&p); InsQue(&tempb->p.m,ode->last_mass); InsQue(tempb,bullet_root.last); temps->reload_timeout=tP+r/temps->fire_rate; Noise(100,r*200,r*400); } U0 HumanFireBegin() { F64 r=3.0*ctrl_panel.bullet_radius/CTRL_PANEL_RANGE+0.5, fuse_time=ToF64(ctrl_panel.fuse_time+1)/CTRL_PANEL_RANGE; if (human) { if (!human->exploding && !human->spacewalk_side && tP>human->reload_timeout) switch (level) { case 3: if (!(human->s[3].s.flags&SSF_INACTIVE)) FireOneGun(human,3,r,fuse_time); if (!(human->s[5].s.flags&SSF_INACTIVE)) FireOneGun(human,4,r,fuse_time); case 2: if (!(human->s[1].s.flags&SSF_INACTIVE)) FireOneGun(human,1,r,fuse_time); if (!(human->s[2].s.flags&SSF_INACTIVE)) FireOneGun(human,2,r,fuse_time); case 1: FireOneGun(human,0,r,fuse_time); break; default: human->lasering=TRUE; break; } } } U0 HumanFireEnd() { if (human && !human->exploding) switch (level) { case 3: case 2: case 1: break; default: human->lasering=FALSE; Snd(0); break; } } U0 ExpireShots() { Bullet *tempb=bullet_root.next,*tempb1; while (tempb!=&bullet_root) { tempb1=tempb->next; if (tP>tempb->fuse_time && !tempb->exploding) { tempb->exploding=TRUE; Noise(50,3000,6000); } if (tP>tempb->die_timeout) { RemQue(tempb); RemQue(&tempb->p.m); Free(tempb); } tempb=tempb1; } } // ********************************** AI U0 AI() { D3 p,p1,p2; Ship *temps=ship_root.next; if (human && !human->exploding) { while (temps!=&ship_root) { D3Sub(&temps->p[0].m.x,&temps->p[1].m.x,&p1); D3Sub(&temps->p[0].m.x,&temps->p[2].m.x,&p2); D3Add(&p1,&p2,&p); D3Sub(&human->p[0].m.x,&temps->p[0].m.x,&p1); if (D3Dot(D3Unit(&p),D3Unit(&p1))>0.995 && tP>temps->reload_timeout) { FireOneGun(temps,0,1.5+.5,.4); } temps=temps->next; } } } // ********************************** Init U0 InitLevel() { I64 i; human=ShipNew(Fs->win_pixel_width/2,Fs->win_pixel_height/2,ST_HUMAN1); for (i=0;i<level+2;i++) PlaceShip(ST_ENEMY1); PlaceShip(ST_ENEMY2); show_level_msg=TRUE; ode->flags|=ODEF_PAUSED; } U0 Init() { I64 i; game_over=FALSE; score=0; level=1; ship_root.next=ship_root.last=&ship_root; bullet_root.next=bullet_root.last=&bullet_root; for (i=0;i<NUM_STARS;i++) { stars_x[i]=RandU16%GR_WIDTH; stars_y[i]=RandU16%GR_HEIGHT; } human_t_left=0; human_t_right=0; human_antispin=0; InitLevel; } // ********************************** Main U0 XCaliber() { U64 ch,msg_code,p1,p2,sc; Ctrl *cp=CtrlPanelNew; SettingsPush; //See SettingsPush Preempt(OFF); Fs->text_attr=BLACK<<4+WHITE; MenuPush( "File {" " Abort(,CH_CTRLQ);" " Exit(,CH_ESC);" "}" "Play {" " Restart(,CH_CR);" " Fire(,CH_SPACE);" " Thrust(,,SC_CURSOR_UP);" " StopSpin(,,SC_CURSOR_DOWN);" " Left(,,SC_CURSOR_LEFT);" " Right(,,SC_CURSOR_RIGHT);" " Spackwalk(,'w');" " LongerFuse(,,SC_CURSOR_RIGHT|SCF_SHIFT);" " ShorterFuse(,,SC_CURSOR_LEFT|SCF_SHIFT);" " LargerShot(,,SC_CURSOR_UP|SCF_SHIFT);" " SmallerShot(,,SC_CURSOR_DOWN|SCF_SHIFT);" "}" ); WinMax; WordStat(OFF); WinBorder(ON); Fs->win_inhibit=WIF_ALL-WIF_BORDER-WIF_MENU-WIF_CTRLS; Fs->update_win=&UpdateWin; do { ode=OdeNew(0,0.01,ODEF_HAS_MASSES); ode->derivative=&MyDerivative; ode->min_tolerance=1e-9; ode->drag_v3=0.00001; Init; InsQue(ode,Fs->last_ode); ch=0; do { while (!game_over && !show_level_msg && (msg_code=ScanMsg(&p1,&p2,1<<MSG_KEY_DOWN|1<<MSG_KEY_UP))) { switch (msg_code) { case MSG_KEY_DOWN: ch=p1; sc=p2; switch (ch) { case 0: switch (sc.u8[0]) { case SC_CURSOR_RIGHT: if (sc&SCF_SHIFT) ctrl_panel.fuse_time+=2; else human_t_right=MAX_THRUST; break; case SC_CURSOR_LEFT: if (sc&SCF_SHIFT) ctrl_panel.fuse_time-=2; else human_t_left =MAX_THRUST; break; case SC_CURSOR_UP: if (sc&SCF_SHIFT) ctrl_panel.bullet_radius+=2; else { human_t_right=MAX_THRUST; human_t_left =MAX_THRUST; } break; case SC_CURSOR_DOWN: if (sc&SCF_SHIFT) ctrl_panel.bullet_radius-=2; else human_antispin=MAX_ANTISPIN; break; } break; case CH_SPACE: HumanFireBegin; break; case 'w': ctrl_panel.spacewalk=TRUE; break; } break; case MSG_KEY_UP: ch=p1; sc=p2; switch (ch) { case 0: switch (sc.u8[0]) { case SC_CURSOR_RIGHT: human_t_right=0; break; case SC_CURSOR_LEFT: human_t_left =0; break; case SC_CURSOR_UP: human_t_right=0; human_t_left =0; break; case SC_CURSOR_DOWN: human_antispin=0; break; } break; case CH_CR: ch=0; break; case CH_SPACE: HumanFireEnd; break; } break; } } AI; ExpireShots; CheckDamage; WinSync; //msgs are only queued by winmngr if (show_level_msg) { ch=GetKey(&sc); if (ch==CH_CR) ch=0; ode->flags&=~ODEF_PAUSED; show_level_msg=FALSE; } else if (game_over) { ch=ScanChar; } else { if (!remaining) { level++; ShipDel(human); human=NULL; InitLevel; } } } while (ch!=CH_ESC && ch!=CH_CR && ch!=CH_CTRLQ); AllDel(ode); } while (ch!=CH_ESC && ch!=CH_CTRLQ); SettingsPop; CtrlPanelDel(cp); MenuPop; AcctRegWriteBranch("LT/XCaliber","I64 best_score=%d;\r\n",best_score); }