Trivial Solutions Corp. 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. /* 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 AcctRegSetDefaultEntry("LT/XCaliber","I8 best_score=0;\r\n"); AcctRegExecuteBranch("LT/XCaliber"); class MyMass { MassStruct m; F8 temperature; F8 radius; I8 color; }; class MySpring { SpringStruct s; F8 strength; I8 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; I8 type,masses,springs; MyMass p[MAX_MASSES]; MySpring s[MAX_SPRINGS]; F8 fire_rate; F8 reload_timeout,spacewalk_timeout; F8 die_time,die_timeout; I8 spacewalk_side; F8 laser_temperature; BoolI1 lasering,exploding,laser_overheat; } *human; F8 human_t_left,human_t_right,human_antispin; class Bullet { Bullet *next,*last; F8 radius; F8 fuse_time,die_timeout; MyMass p; BoolI1 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 BoolI1 game_over,show_level_msg; #define NUM_STARS 100 I8 stars_x[NUM_STARS],stars_y[NUM_STARS]; Ship ship_root; Bullet bullet_root; Ode *ode=NULL; I8 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 BoolI8 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(I8 x,I8 y,I8 type) { I8 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) { I8 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(I8 type) { Ship *ee; if (CheckOverlap) return; while (TRUE) { ee=ShipNew(RandU2%(Fs->win_pixel_width-20)+10,RandU2%(Fs->win_pixel_height-20)+10,type); if (CheckOverlap) ShipDel(ee); else break; } } // ********************************** Human Ship I8 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); } BoolI8 LaserPlot(GrBitMap *base,I8 x,I8 y,I8 z) { I8 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) { I8 i,j; GrBitMap *base=GrAlias(gr_refreshed_base,task); F8 arg; Ship *temps; Bullet *tempb; D3 p,p1,p2; F8 t_left,t_right,spin,d,x,y; MySpring *tempss; U1 *img; BoolI1 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); GrElemsPlotRotZ(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); GrElemsPlotRotZ(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)) GrElemsPlotRotZ(base,temps->p[3].m.x,temps->p[3].m.y,0,img,arg); if (!(temps->s[5].s.flags&SSF_INACTIVE)) GrElemsPlotRotZ(base,temps->p[4].m.x,temps->p[4].m.y,0,img,arg); case 2: if (!(temps->s[1].s.flags&SSF_INACTIVE)) GrElemsPlotRotZ(base,temps->p[1].m.x,temps->p[1].m.y,0,img,arg); if (!(temps->s[2].s.flags&SSF_INACTIVE)) GrElemsPlotRotZ(base,temps->p[2].m.x,temps->p[2].m.y,0,img,arg); case 1: GrElemsPlotRotZ(base,temps->p[0].m.x,temps->p[0].m.y,0,img,arg); break; default: GrElemsPlotRotZ(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); } GrElemsPlotRotZ(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); GrElemsPlotRotZ(base,temps->p[0].m.x,temps->p[0].m.y,0,img,arg); arg=Arg(p1.x,p1.y); GrElemsPlotRotZ(base,temps->p[0].m.x+p1.x,temps->p[0].m.y+p1.y,0,<EnemySide>,arg); arg=Arg(p2.x,p2.y); GrElemsPlotRotZ(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,F8 r) { MyMass *tempm; D3 p1; F8 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,F8 t,Order2D3 *state,Order2D3 *DstateDt) { nounusedwarn t,state,DstateDt; I8 i; F8 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() { I8 i,j,k; Ship *temps,*temps1; MyMass *tempm,*best_mass; D3 p,p1,p2; F8 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_F8; 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,I8 g,F8 r,F8 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() { F8 r=3.0*ctrl_panel.bullet_radius/CTRL_PANEL_RANGE+0.5, fuse_time=ToF8(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() { I8 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() { I8 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]=RandU2%GR_WIDTH; stars_y[i]=RandU2%GR_HEIGHT; } human_t_left=0; human_t_right=0; human_antispin=0; InitLevel; } // ********************************** Main U0 XCaliber() { U8 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); 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.u1[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.u1[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","I8 best_score=%d;\r\n",best_score); }