/* I did not limit myself to real physics. It's a game. I suppose the gun 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.templeos.org/files/SimStrSetUp.zip 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. */ #define XMSGF_ANTISPIN 0 RegSetDftEntry("TempleOS/XCaliber", "I64 best_score=0;\n" "I64 msg_flags=0;\n" ); RegExeBranch("TempleOS/XCaliber"); class MyMass:CMass { F64 temperature; F64 radius; I64 color; Bool no_overlap; }; class MySpring:CSpring { F64 strength; I64 color; }; #define SPIN_GAIN 0.25 #define MAX_MASSES 8 #define MAX_SPRINGS 16 #define MAX_MISSILES 2 #define ST_HUMAN1 0 #define ST_ENEMY1 1 #define ST_ENEMY2 2 extern class Ship; #define MISSILE_LEN 5 class Missile { Missile *next,*last; F64 explosion_radius; F64 fuse_time,die_timeout; MyMass p_front,p_back; MySpring s[5]; U8 *img; Ship *owner,*target; Bool active,launched,exploding; U8 label[5]; } missile_head; 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; Missile missiles[MAX_MISSILES]; Bool lasering,exploding,laser_overheat; } ship_head,*human; F64 human_t_left,human_t_right,human_antispin; class Shot { Shot *next,*last; F64 radius,explosion_radius; F64 fuse_time,die_timeout; MyMass p; Bool exploding; } shot_head; #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 Bool game_over,show_level_msg; #define NUM_STARS 100 I64 stars_x[NUM_STARS],stars_y[NUM_STARS]; CMathODE *ode=NULL; I64 level,score,remaining; $BG,0$ $SP,"<1>",BI=1$ $SP,"<2>",BI=2$ $SP,"<3>",BI=3$ $SP,"<4>",BI=4$ $SP,"<5>",BI=5$ $SP,"<6>",BI=6$ $SP,"<7>",BI=7$ $SP,"<8>",BI=8$ $BG$ //********************************** Ship Bool CheckOverlap() { CD3 p; MyMass *tmpm,*tmpm1; tmpm=ode->next_mass; while (tmpm!=&ode->next_mass) { tmpm1=ode->next_mass; while (tmpm1!=&ode->next_mass) { if (tmpm!=tmpm1 && !tmpm->no_overlap && !tmpm1->no_overlap) { D3Sub(&p,&tmpm->x,&tmpm1->x); if (D3NormSqr(&p)<=Sqr(tmpm->radius+tmpm1->radius)) return TRUE; } tmpm1=tmpm1->next; } tmpm=tmpm->next; } return FALSE; } U0 MissileNew(Ship *tmpsp,I64 n) { I64 i; CD3 p,p1,p2; Missile *tmpmi=&tmpsp->missiles[n]; MemSet(tmpmi,0,sizeof(Missile)); D3Equ(&tmpmi->p_front.x, (tmpsp->p[n+1].x+tmpsp->p[n+3].x)/2, (tmpsp->p[n+1].y+tmpsp->p[n+3].y)/2,0); D3Copy(&tmpmi->p_back.x,&tmpmi->p_front.x); if (n&1) StrCpy(tmpmi->label,"L"); else StrCpy(tmpmi->label,"R"); tmpmi->owner=tmpsp; tmpmi->explosion_radius=5; tmpmi->p_front.mass=0.1; tmpmi->p_back.mass =0.1; tmpmi->p_front.radius=2; tmpmi->p_back.radius =2; tmpmi->p_front.no_overlap=TRUE; tmpmi->p_back.no_overlap =TRUE; D3Sub(&p1,&tmpsp->p[0].x,&tmpsp->p[1].x); D3Sub(&p2,&tmpsp->p[0].x,&tmpsp->p[2].x); D3Unit(D3Add(&p,&p1,&p2)); D3AddEqu(&tmpmi->p_front.x,D3MulEqu(D3Copy(&p1,&p),MISSILE_LEN/2+1)); D3SubEqu(&tmpmi->p_back.x ,D3MulEqu(D3Copy(&p1,&p),MISSILE_LEN/2-1)); D3Copy(&tmpmi->p_front.DxDt,&tmpsp->p[n].DxDt); D3Copy(&tmpmi->p_back.DxDt,&tmpsp->p[n].DxDt); QueIns(&tmpmi->p_front,ode->last_mass); QueIns(&tmpmi->p_back, ode->last_mass); tmpmi->s[0].end1=&tmpmi->p_front; tmpmi->s[0].end2=&tmpmi->p_back; tmpmi->s[1].end1=&tmpmi->p_front; tmpmi->s[1].end2=&tmpsp->p[n+1]; tmpmi->s[2].end1=&tmpmi->p_back; tmpmi->s[2].end2=&tmpsp->p[n+1]; tmpmi->s[3].end1=&tmpmi->p_front; tmpmi->s[3].end2=&tmpsp->p[n+3]; tmpmi->s[4].end1=&tmpmi->p_back; tmpmi->s[4].end2=&tmpsp->p[n+3]; for (i=0;i<5;i++) { tmpmi->s[i].const=10000; tmpmi->s[i].strength =20000; tmpmi->s[i].color=BLACK; tmpmi->s[i].rest_len=D3Dist(&tmpmi->s[i].end1->x,&tmpmi->s[i].end2->x); QueIns(&tmpmi->s[i],ode->last_spring); } tmpmi->img=$IB,"<7>",BI=7$; tmpmi->active=TRUE; QueIns(tmpmi,missile_head.last); } Ship *ShipNew(I64 x,I64 y,I64 type) { I64 i; Ship *tmpsp=CAlloc(sizeof(Ship)); switch (tmpsp->type=type) { case ST_HUMAN1: tmpsp->fire_rate=25; tmpsp->masses=5; tmpsp->p[0].x=x; tmpsp->p[0].y=y; tmpsp->p[1].x=x+3; tmpsp->p[1].y=y+10; tmpsp->p[2].x=x-3; tmpsp->p[2].y=y+10; tmpsp->p[3].x=x+20; tmpsp->p[3].y=y+20; tmpsp->p[4].x=x-20; tmpsp->p[4].y=y+20; for (i=0;imasses;i++) { tmpsp->p[i].mass=1; if (i<3) tmpsp->p[i].radius=2.5; else tmpsp->p[i].radius=4; tmpsp->p[i].drag_profile_factor=3; QueIns(&tmpsp->p[i],ode->last_mass); } tmpsp->p[3].mass/=10.0; tmpsp->p[4].mass/=10.0; tmpsp->springs=7; tmpsp->s[0].end1=&tmpsp->p[0]; tmpsp->s[0].end2=&tmpsp->p[1]; tmpsp->s[1].end1=&tmpsp->p[2]; tmpsp->s[1].end2=&tmpsp->p[0]; tmpsp->s[2].end1=&tmpsp->p[1]; tmpsp->s[2].end2=&tmpsp->p[2]; tmpsp->s[3].end1=&tmpsp->p[1]; tmpsp->s[3].end2=&tmpsp->p[3]; tmpsp->s[4].end1=&tmpsp->p[0]; tmpsp->s[4].end2=&tmpsp->p[3]; tmpsp->s[5].end1=&tmpsp->p[2]; tmpsp->s[5].end2=&tmpsp->p[4]; tmpsp->s[6].end1=&tmpsp->p[0]; tmpsp->s[6].end2=&tmpsp->p[4]; for (i=0;isprings;i++) { tmpsp->s[i].rest_len= D3Dist(&tmpsp->s[i].end1->x,&tmpsp->s[i].end2->x); tmpsp->s[i].const=10000; tmpsp->s[i].strength =30000; if (i<=2) tmpsp->s[i].color=LTCYAN; else tmpsp->s[i].color=LTGRAY; QueIns(&tmpsp->s[i],ode->last_spring); } MissileNew(tmpsp,0); MissileNew(tmpsp,1); remaining=0; break; case ST_ENEMY1: tmpsp->fire_rate=2.5; tmpsp->masses=3; tmpsp->p[0].x=x; tmpsp->p[0].y=y; tmpsp->p[1].x=x+15; tmpsp->p[1].y=y; tmpsp->p[2].x=x; tmpsp->p[2].y=y+15; for (i=0;imasses;i++) { tmpsp->p[i].mass=1; tmpsp->p[i].radius=7; tmpsp->p[i].drag_profile_factor=3; QueIns(&tmpsp->p[i],ode->last_mass); } tmpsp->springs=3; tmpsp->s[0].end1=&tmpsp->p[0]; tmpsp->s[0].end2=&tmpsp->p[1]; tmpsp->s[1].end1=&tmpsp->p[1]; tmpsp->s[1].end2=&tmpsp->p[2]; tmpsp->s[2].end1=&tmpsp->p[2]; tmpsp->s[2].end2=&tmpsp->p[0]; for (i=0;isprings;i++) { tmpsp->s[i].rest_len= D3Dist(&tmpsp->s[i].end1->x,&tmpsp->s[i].end2->x); tmpsp->s[i].const=10000; tmpsp->s[i].strength =20000; tmpsp->s[i].color=BLACK; QueIns(&tmpsp->s[i],ode->last_spring); } remaining++; break; case ST_ENEMY2: tmpsp->fire_rate=5.0; tmpsp->masses=5; tmpsp->p[0].x=x; tmpsp->p[0].y=y; tmpsp->p[1].x=x-7; tmpsp->p[1].y=y+10; tmpsp->p[2].x=x+7; tmpsp->p[2].y=y+10; tmpsp->p[3].x=x-14; tmpsp->p[3].y=y+20; tmpsp->p[4].x=x+14; tmpsp->p[4].y=y+20; for (i=0;imasses;i++) { tmpsp->p[i].mass=1; tmpsp->p[i].radius=6; tmpsp->p[i].drag_profile_factor=5; tmpsp->p[i].color=PURPLE; QueIns(&tmpsp->p[i],ode->last_mass); } tmpsp->springs=7; tmpsp->s[0].end1=&tmpsp->p[0]; tmpsp->s[0].end2=&tmpsp->p[1]; tmpsp->s[1].end1=&tmpsp->p[0]; tmpsp->s[1].end2=&tmpsp->p[2]; tmpsp->s[2].end1=&tmpsp->p[1]; tmpsp->s[2].end2=&tmpsp->p[2]; tmpsp->s[3].end1=&tmpsp->p[1]; tmpsp->s[3].end2=&tmpsp->p[3]; tmpsp->s[4].end1=&tmpsp->p[2]; tmpsp->s[4].end2=&tmpsp->p[4]; tmpsp->s[5].end1=&tmpsp->p[2]; tmpsp->s[5].end2=&tmpsp->p[3]; tmpsp->s[6].end1=&tmpsp->p[1]; tmpsp->s[6].end2=&tmpsp->p[4]; for (i=0;isprings;i++) { tmpsp->s[i].rest_len= D3Dist(&tmpsp->s[i].end1->x,&tmpsp->s[i].end2->x); tmpsp->s[i].const= 40000; tmpsp->s[i].strength =75000; if (i>=3) tmpsp->s[i].color=LTPURPLE; else tmpsp->s[i].color=BLACK; QueIns(&tmpsp->s[i],ode->last_spring); } remaining++; break; } QueIns(tmpsp,ship_head.last); return tmpsp; } U0 MissileDel(Missile *tmpmi) { I64 i; if (tmpmi->active) { QueRem(tmpmi); for(i=0;i<5;i++) QueRem(&tmpmi->s[i]); QueRem(&tmpmi->p_front); QueRem(&tmpmi->p_back); tmpmi->active=FALSE; } } U0 ShipDel(Ship *tmpsp) { I64 i; if (!tmpsp) return; for (i=0;imasses;i++) QueRem(&tmpsp->p[i]); for (i=0;isprings;i++) QueRem(&tmpsp->s[i]); for (i=0;i<2;i++) MissileDel(&tmpsp->missiles[i]); QueRem(tmpsp); Free(tmpsp); remaining--; } U0 PlaceShip(I64 type) { Ship *tmpsp; if (CheckOverlap) return; while (TRUE) { tmpsp=ShipNew(RandU16%(Fs->pix_width-20)+10, RandU16%(Fs->pix_height-20)+10,type); if (CheckOverlap) ShipDel(tmpsp); else break; } } //********************************** Human Ship I64 Tweaked() { CD3 p,p1,p2; if (human) { D3Sub(&p1,&human->p[0].x,&human->p[1].x); D3Sub(&p2,&human->p[0].x,&human->p[2].x); D3Add(&p,&p1,&p2); D3Unit(&p); D3Sub(&p1,&human->p[0].x,&human->p[3].x); D3Sub(&p2,&human->p[0].x,&human->p[4].x); D3Unit(&p1); D3Unit(&p2); if (!(human->s[3].flags&SSF_INACTIVE) && D3Dot(&p,&p1)>Cos(20*ã/180)) return 3; if (!(human->s[5].flags&SSF_INACTIVE) && D3Dot(&p,&p2)>Cos(20*ã/180)) return 4; return 0; } } U0 AllDel(CMathODE *ode) { Ship *tmpsp,*tmpsp1; QueRem(ode); tmpsp=ship_head.next; while (tmpsp!=&ship_head) { tmpsp1=tmpsp->next; ShipDel(tmpsp); tmpsp=tmpsp1; } human=NULL; QueDel(&shot_head,TRUE); ODEDel(ode); } Bool LaserPlot(CDC *dc,I64 x,I64 y,I64) { I64 c; c=GrPeek(dc,x,y); if (c!=BLACK && c!=WHITE) return FALSE; else { GrPlot(dc,x,y); return TRUE; } } //********************************** U0 DrawIt(CTask *task,CDC *dc) { I64 i,j; F64 arg; Ship *tmpsp; Shot *tmps; Missile *tmpmi; CD3 p,p1,p2; F64 t_left,t_right,spin,d,x,y; MySpring *tmpsps; U8 *img; Bool draw_laser_line=FALSE; if (ode!=task->last_ode) return; dc->color=WHITE; GrPrint(dc,0,0,"Level:%d Score:%d High Score:%d",level,score,best_score); if (game_over) { if (Blink) GrPrint(dc,(task->pix_width-9*FONT_WIDTH)/2, (task->pix_height-FONT_HEIGHT)/2,"Game Over"); } else if (show_level_msg) { if (Blink) GrPrint(dc,(task->pix_width-8*FONT_WIDTH)/2, (task->pix_height-FONT_HEIGHT)/2+50,"Level %d",level); } for (i=0;inext_spring; while (tmpsps!=&ode->next_spring) { if (!(tmpsps->flags&SSF_INACTIVE) && tmpsps->color) { dc->color=tmpsps->color; GrLine(dc,tmpsps->end1->x,tmpsps->end1->y, tmpsps->end2->x,tmpsps->end2->y); } tmpsps=tmpsps->next; } tmpmi=missile_head.next; while (tmpmi!=&missile_head) { if (tmpmi->active) { if (tmpmi->launched && tmpmi->exploding) { d=(tS-tmpmi->fuse_time)/(tmpmi->die_timeout-tmpmi->fuse_time); d=7*Sin(ã*d)*tmpmi->explosion_radius+1; for (i=1;icolor=YELLOW; else dc->color=LTRED; GrCircle(dc,tmpmi->p_front.x,tmpmi->p_front.y,i); } } else SpriteZ3B(dc,(tmpmi->p_front.x+tmpmi->p_back.x)/2, (tmpmi->p_front.y+tmpmi->p_back.y)/2,0,tmpmi->img, Arg(tmpmi->p_front.x-tmpmi->p_back.x, tmpmi->p_front.y-tmpmi->p_back.y)); } tmpmi=tmpmi->next; } tmpsp=ship_head.next; while (tmpsp!=&ship_head) { if (!tmpsp->exploding) switch (tmpsp->type) { case ST_HUMAN1: if (tmpsp->spacewalk_side) { t_left=0; t_right=0; } else { if (d=D3Norm(D3Sub(&p1,&tmpsp->p[0].x,&tmpsp->p[1].x))) { D3Sub(&p2,&tmpsp->p[0].DxDt,&tmpsp->p[1].DxDt); D3Cross(&p,&p1,&p2); spin=p.z/d; } else spin=0; t_left =Clamp(human_t_left+SPIN_GAIN*spin*human_antispin, 0,MAX_THRUST); if (d=D3Norm(D3Sub(&p1,&tmpsp->p[0].x,&tmpsp->p[2].x))) { D3Sub(&p2,&tmpsp->p[0].DxDt,&tmpsp->p[2].DxDt); D3Cross(&p,&p1,&p2); spin=p.z/d; } else spin=0; t_right=Clamp(human_t_right-SPIN_GAIN*spin*human_antispin, 0,MAX_THRUST); } D3Sub(&p1,&tmpsp->p[1].x,&tmpsp->p[0].x); D3Sub(&p2,&tmpsp->p[2].x,&tmpsp->p[0].x); D3Unit(D3Add(&p,&p1,&p2)); if (!(tmpsp->s[3].flags&SSF_INACTIVE)) { dc->color=YELLOW; D3AddEqu(D3Mul(&p1,t_left/25,&p),&tmpsp->p[3].x); GrLine(dc,tmpsp->p[1].x,tmpsp->p[1].y,p1.x,p1.y); arg=Arg(p.x,p.y); SpriteZ3B(dc,tmpsp->p[3].x,tmpsp->p[3].y,0,$IB,"",BI=1$,arg); } if (!(tmpsp->s[5].flags&SSF_INACTIVE)) { dc->color=YELLOW; D3AddEqu(D3Mul(&p2,t_right/25,&p),&tmpsp->p[4].x); GrLine(dc,tmpsp->p[2].x,tmpsp->p[2].y,p2.x,p2.y); arg=Arg(p.x,p.y); SpriteZ3B(dc,tmpsp->p[4].x,tmpsp->p[4].y,0,$IB,"",BI=1$,arg); } if (tS>tmpsp->reload_timeout) img=$IB,"",BI=2$; else img=$IB,"",BI=3$; arg=Arg(p.x,p.y); switch (level) { case 3: if (!(tmpsp->s[3].flags&SSF_INACTIVE)) SpriteZ3B(dc,tmpsp->p[3].x,tmpsp->p[3].y,0,img,arg); if (!(tmpsp->s[5].flags&SSF_INACTIVE)) SpriteZ3B(dc,tmpsp->p[4].x,tmpsp->p[4].y,0,img,arg); case 2: if (!(tmpsp->s[1].flags&SSF_INACTIVE)) SpriteZ3B(dc,tmpsp->p[1].x,tmpsp->p[1].y,0,img,arg); if (!(tmpsp->s[2].flags&SSF_INACTIVE)) SpriteZ3B(dc,tmpsp->p[2].x,tmpsp->p[2].y,0,img,arg); case 1: SpriteZ3B(dc,tmpsp->p[0].x,tmpsp->p[0].y,0,img,arg); break; default: SpriteZ3B(dc,tmpsp->p[0].x,tmpsp->p[0].y,0,$IB,"",BI=6$,arg); if (tmpsp->lasering && !tmpsp->laser_overheat) { draw_laser_line=TRUE; Snd(1000); } } ctrl_panel.laser_temperature=tmpsp->laser_temperature; if (tmpsp->spacewalk_side) { d=1.0-(tmpsp->spacewalk_timeout-tS)/SPACEWALK_TIME; if (d>1.0) { tmpsp->spacewalk_side=0; ctrl_panel.spacewalk=FALSE; } else { if (d<0.5) { d=d*2; x=tmpsp->p[0].x*(1.0-d)+ tmpsp->p[tmpsp->spacewalk_side].x*(d); y=tmpsp->p[0].y*(1.0-d)+ tmpsp->p[tmpsp->spacewalk_side].y*(d); } else { d=(d-0.5)*2; x=tmpsp->p[tmpsp->spacewalk_side].x*(1.0-d)+ tmpsp->p[0].x*(d); y=tmpsp->p[tmpsp->spacewalk_side].y*(1.0-d)+ tmpsp->p[0].y*(d); } SpriteZ3B(dc,x,y,0,$IB,"",BI=4$,arg+0.75*Sin(tS*2)); } } else { if (ctrl_panel.spacewalk) { if (tmpsp->spacewalk_side=Tweaked) tmpsp->spacewalk_timeout=tS+SPACEWALK_TIME; else ctrl_panel.spacewalk=FALSE; } } break; case ST_ENEMY2: for (i=3;imasses;i++) { dc->color=tmpsp->p[i].color; GrCircle(dc,tmpsp->p[i].x,tmpsp->p[i].y,tmpsp->p[i].radius); GrFloodFill(dc,tmpsp->p[i].x,tmpsp->p[i].y+2,TRUE); dc->color=WHITE; GrCircle(dc,tmpsp->p[i].x,tmpsp->p[i].y,tmpsp->p[i].radius); } case ST_ENEMY1: D3DivEqu(D3Sub(&p1,&tmpsp->p[1].x,&tmpsp->p[0].x),2.0); D3DivEqu(D3Sub(&p2,&tmpsp->p[2].x,&tmpsp->p[0].x),2.0); D3Unit(D3Add(&p,&p1,&p2)); if (tS>tmpsp->reload_timeout) img=$IB,"",BI=2$; else img=$IB,"",BI=3$; arg=Arg(p.x,p.y); SpriteZ3B(dc,tmpsp->p[0].x,tmpsp->p[0].y,0,img,arg); arg=Arg(p1.x,p1.y); SpriteZ3B(dc,tmpsp->p[0].x+p1.x,tmpsp->p[0].y+p1.y,0, $IB,"",BI=5$,arg); arg=Arg(p2.x,p2.y); SpriteZ3B(dc,tmpsp->p[0].x+p2.x,tmpsp->p[0].y+p2.y,0, $IB,"",BI=5$,arg); for (i=0;imasses;i++) { dc->color=YELLOW; if (tmpsp->p[i].temperature>=1.0) GrCircle(dc,tmpsp->p[i].x,tmpsp->p[i].y, tmpsp->p[i].temperature); } break; } else if (tmpsp->die_time<=tS<=tmpsp->die_timeout) for (j=0;jmasses;j++) { d=(tS-tmpsp->die_time)/(tmpsp->die_timeout-tmpsp->die_time); d=7*Sin(ã*d)*(6+j)+1; for (i=1;icolor=YELLOW; else dc->color=LTRED; GrCircle(dc,tmpsp->p[j].x,tmpsp->p[j].y,i); } } tmpsp=tmpsp->next; } tmps=shot_head.next; while (tmps!=&shot_head) { if (tmps->exploding) { d=(tS-tmps->fuse_time)/(tmps->die_timeout-tmps->fuse_time); d=7*Sin(ã*d)*tmps->explosion_radius+1; for (i=1;icolor=YELLOW; else dc->color=LTRED; GrCircle(dc,tmps->p.x,tmps->p.y,i); } } else { if (tmps->radius<1.0) { dc->color=LTGREEN; GrPlot(dc,tmps->p.x,tmps->p.y); } else { dc->color=YELLOW; GrCircle(dc,tmps->p.x,tmps->p.y,tmps->radius); if (tmps->radius>=2.0) GrFloodFill(dc,tmps->p.x,tmps->p.y,TRUE); dc->color=LTGREEN; GrCircle(dc,tmps->p.x,tmps->p.y,tmps->radius); } } tmps=tmps->next; } if (human && draw_laser_line) { D3Sub(&p1,&human->p[1].x,&human->p[0].x); D3Sub(&p2,&human->p[2].x,&human->p[0].x); D3Unit(D3Add(&p,&p1,&p2)); dc->color=LTBLUE; Line(dc,human->p[0].x-10*p.x,human->p[0].y-10*p.y,0, human->p[0].x-800*p.x,human->p[0].y-800*p.y,0,&LaserPlot); } tmpmi=missile_head.next; while (tmpmi!=&missile_head) { if (tmpsp=tmpmi->target) { dc->color=LTRED; GrCircle(dc,tmpsp->p[0].x,tmpsp->p[0].y,10); GrPrint(dc,tmpsp->p[0].x+12,tmpsp->p[0].y-4,tmpmi->label); } tmpmi=tmpmi->next; } } U0 Explosion(MyMass *tmpm1,MyMass *tmpm2,F64 r) { MyMass *tmpm; CD3 p1; F64 d; tmpm=ode->next_mass; while (tmpm!=&ode->next_mass) { if (tmpm!=tmpm1 && tmpm!=tmpm2) { D3Sub(&p1,&tmpm->state->x,&tmpm1->state->x); d=D3NormSqr(&p1)-tmpm->radius*tmpm->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(&tmpm->DstateDt->DxDt,&p1); } } tmpm=tmpm->next; } } Ship TargetGet(Missile *tmpmi) { Ship *tmpsp,*res=NULL; F64 dd,best_dd=MAX_F64; I64 i; CD3 p,p1,p2; D3Unit(D3Sub(&p,&tmpmi->p_front.state->x,&tmpmi->p_back.state->x)); tmpsp=ship_head.next; while (tmpsp!=&ship_head) { if (!tmpsp->exploding && tmpsp!=tmpmi->owner) for (i=0;imasses;i++) { D3Sub(&p1,&tmpsp->p[i].state->x,&tmpmi->p_front.state->x); D3Unit(D3Copy(&p2,&p1)); D3Cross(&p1,&p,&p2); if (D3Dot(&p,&p2)>0 && D3Norm(&p1)<=ã/16) { dd=D3NormSqr(&p1); if (ddnext; } return res; } U0 MyDerivative(CMathODE *ode,F64,COrder2D3 *,COrder2D3 *) { I64 i; F64 d,dd,dd2,spin,t_left,t_right,é_err,é_thrust,DéDt; CTask *task=ode->win_task; CD3 p,p1,p2; Ship *tmpsp; Shot *tmps; Missile *tmpmi; MyMass *tmpm,*tmpm1; tmpm=ode->next_mass; while (tmpm!=&ode->next_mass) { d=tmpm->state->x; if (d-tmpm->radius<0) tmpm->DstateDt->DxDt+=Sqr(Sqr(Sqr(d-tmpm->radius))); if (d+tmpm->radius>task->pix_width) tmpm->DstateDt->DxDt-=Sqr(Sqr(Sqr((d+tmpm->radius)-task->pix_width))); d=tmpm->state->y; if (d-tmpm->radius<0) tmpm->DstateDt->DyDt+=Sqr(Sqr(Sqr(d-tmpm->radius))); if (d+tmpm->radius>task->pix_height) tmpm->DstateDt->DyDt-=Sqr(Sqr(Sqr((d+tmpm->radius)-task->pix_height))); tmpm1=ode->next_mass; while (tmpm1!=&ode->next_mass) { if (tmpm!=tmpm1) { D3Sub(&p,&tmpm->state->x,&tmpm1->state->x); dd=D3NormSqr(&p); dd2=Sqr(tmpm->radius+tmpm1->radius); if (dd<=dd2) { d=Sqrt(dd)+0.0001; D3MulEqu(&p,Sqr(Sqr(dd2-dd))/d); D3AddEqu(&tmpm ->DstateDt->DxDt,&p); D3SubEqu(&tmpm1->DstateDt->DxDt,&p); } } tmpm1=tmpm1->next; } tmpm=tmpm->next; } tmpsp=ship_head.next; while (tmpsp!=&ship_head) { if (tmpsp->exploding && tmpsp->die_time<=tS<=tmpsp->die_timeout) for (i=0;imasses;i++) Explosion(&tmpsp->p[i],NULL,tmpsp->p[i].radius/3.0); switch (tmpsp->type) { case ST_HUMAN1: if (!tmpsp->exploding) { if (tmpsp->spacewalk_side) { t_left=0; t_right=0; d=1.0-(tmpsp->spacewalk_timeout-tS)/SPACEWALK_TIME; if (0.485p[2].state->x,&tmpsp->p[1].state->x)); if (tmpsp->spacewalk_side==3) { tmpsp->p[3].DstateDt->DxDt-=10*MAX_THRUST*p.x; tmpsp->p[3].DstateDt->DyDt-=10*MAX_THRUST*p.y; tmpsp->p[1].DstateDt->DxDt+=10*MAX_THRUST*p.x; tmpsp->p[1].DstateDt->DyDt+=10*MAX_THRUST*p.y; } else { tmpsp->p[4].DstateDt->DxDt+=10*MAX_THRUST*p.x; tmpsp->p[4].DstateDt->DyDt+=10*MAX_THRUST*p.y; tmpsp->p[2].DstateDt->DxDt-=10*MAX_THRUST*p.x; tmpsp->p[2].DstateDt->DyDt-=10*MAX_THRUST*p.y; } } } else { if (d=D3Norm(D3Sub(&p1,&tmpsp->p[0].state->x, &tmpsp->p[1].state->x))) { D3Sub(&p2,&tmpsp->p[0].state->DxDt,&tmpsp->p[1].state->DxDt); D3Cross(&p,&p1,&p2); spin=p.z/d; } else spin=0; t_left =Clamp(human_t_left+SPIN_GAIN*spin*human_antispin, 0,MAX_THRUST); if (d=D3Norm(D3Sub(&p1,&tmpsp->p[0].state->x, &tmpsp->p[2].state->x))) { D3Sub(&p2,&tmpsp->p[0].state->DxDt,&tmpsp->p[2].state->DxDt); D3Cross(&p,&p1,&p2); spin=p.z/d; } else spin=0; t_right=Clamp(human_t_right-SPIN_GAIN*spin*human_antispin, 0,MAX_THRUST); D3Sub(&p1,&tmpsp->p[0].state->x,&tmpsp->p[1].state->x); D3Sub(&p2,&tmpsp->p[0].state->x,&tmpsp->p[2].state->x); D3Unit(D3Add(&p,&p1,&p2)); if (!(tmpsp->s[3].flags&SSF_INACTIVE)) { D3Mul(&p1,t_left,&p); D3AddEqu(&tmpsp->p[3].DstateDt->DxDt,&p1); } if (!(tmpsp->s[5].flags&SSF_INACTIVE)) { D3Mul(&p2,t_right,&p); D3AddEqu(&tmpsp->p[4].DstateDt->DxDt,&p2); } } } break; } tmpsp=tmpsp->next; } tmps=shot_head.next; while (tmps!=&shot_head) { if (tmps->exploding) Explosion(&tmps->p,NULL,tmps->explosion_radius); tmps=tmps->next; } tmpmi=missile_head.next; while (tmpmi!=&missile_head) { if (tmpmi->active) { if (tmpmi->launched) { if (tmpmi->exploding) Explosion(&tmpmi->p_front,&tmpmi->p_back,tmpmi->explosion_radius); else { //Guide missile if (tmpsp=tmpmi->target) { D3Unit(D3Sub(&p,&tmpmi->p_front.state->x, &tmpmi->p_back.state->x)); D3Sub(&p1,&tmpsp->p[0].state->x,&tmpmi->p_front.state->x); d=D3Norm(&p1); D3Unit(&p1); é_err=D3Dot(&p,&p1); D3Sub(&p1,&tmpmi->p_front.state->DxDt,&tmpmi->p_back.state->DxDt); D3Cross(&p2,&p,&p1); DéDt=D3Norm(&p2); if (p2.z<0) DéDt=-DéDt; é_thrust=Clamp(200*(é_err+2*DéDt)/(d+200),-ã/8,ã/8); p2.x=p.x*Cos(é_thrust)-p.y*Sin(é_thrust); p2.y=p.y*Cos(é_thrust)+p.x*Sin(é_thrust); p2.z=0; D3AddEqu(&tmpmi->p_back.DstateDt->DxDt,D3MulEqu(&p2,MAX_THRUST)); } } } else tmpmi->target=TargetGet(tmpmi); } else tmpmi->target=NULL; tmpmi=tmpmi->next; } } U0 CheckDamage() { I64 i,j,k; Ship *tmpsp,*tmpsp1; MyMass *tmpm,*best_mass; CD3 p,p1,p2; F64 d,best_distance; tmpm=ode->next_mass; while (tmpm!=&ode->next_mass) { tmpm->temperature*=0.9; tmpm=tmpm->next; } if (human) { human->laser_temperature*=0.975; if (human->laser_overheat) { if (human->laser_temperaturelaser_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(&p1,&human->p[0].x,&human->p[1].x); D3Sub(&p2,&human->p[0].x,&human->p[2].x); D3Unit(D3Add(&p,&p1,&p2)); p2.x=p.y; p2.y=-p.x; p2.z=0; best_mass=NULL; best_distance=MAX_F64; tmpm=ode->next_mass; while (tmpm!=&ode->next_mass) { D3Sub(&p1,&human->p[0].x,&tmpm->x); if (Abs(D3Dot(&p1,&p2))radius && D3Dot(&p1,&p)<0.0) { d=D3NormSqr(&p1); if (dnext; } if (best_mass) best_mass->temperature+=1.0; } } } tmpsp=ship_head.next; while (tmpsp!=&ship_head) { tmpsp1=tmpsp->next; switch (tmpsp->type) { case ST_HUMAN1: if (tmpsp->exploding) { if (tS>tmpsp->die_timeout) { ShipDel(tmpsp); human=NULL; } } else for (i=0;isprings;i++) { if (Abs(tmpsp->s[i].f)>tmpsp->s[i].strength) { tmpsp->s[i].flags|=SSF_INACTIVE; if (i==4) MissileDel(&tmpsp->missiles[0]); else if (i==5) MissileDel(&tmpsp->missiles[1]); } if (tmpsp->s[i].flags&SSF_INACTIVE) { if (i<3) { if (!tmpsp->exploding) { tmpsp->exploding=TRUE; tmpsp->die_time=tS; tmpsp->die_timeout=tS+0.75; game_over=TRUE; Noise(750,1000,2000); break; } } } } break; default: if (tmpsp->exploding) { if (tS>tmpsp->die_timeout) { ShipDel(tmpsp); score+=level; if (score>best_score) best_score=score; } } else { j=0; for (i=0;isprings;i++) { if (tmpsp->s[i].flags&SSF_INACTIVE) j++; else if (Abs(tmpsp->s[i].f)>tmpsp->s[i].strength) { tmpsp->s[i].flags|=SSF_INACTIVE; j++; } } k=0; for (i=0;imasses;i++) { if (tmpsp->p[i].temperature>MASS_MAX_TEMP) k++; } if (j>1 || k) { tmpsp->exploding=TRUE; tmpsp->die_time=tS; tmpsp->die_timeout=tS+0.75; Noise(750,1000,3000); } } } tmpsp=tmpsp1; } } //********************************** Shots U0 FireOneGun(Ship *tmpsp,I64 n,F64 r,F64 fuse_time) { CD3 p,p1,p2; Shot *tmps; tmps=CAlloc(sizeof(Shot)); D3Copy(&tmps->p.x,&tmpsp->p[n].x); tmps->explosion_radius=tmps->radius=r; tmps->fuse_time=tS+1.5*fuse_time; tmps->die_timeout=tmps->fuse_time+0.125; tmps->p.mass=0.3*r*r*r; D3Sub(&p1,&tmpsp->p[0].x,&tmpsp->p[1].x); D3Sub(&p2,&tmpsp->p[0].x,&tmpsp->p[2].x); D3Unit(D3Add(&p,&p1,&p2)); D3AddEqu(&tmps->p.x,D3MulEqu(D3Copy(&p1,&p),r+tmpsp->p[0].radius+5)); D3AddEqu(D3MulEqu(&p,1000/(r+1)),&tmpsp->p[n].DxDt); D3Copy(&tmps->p.DxDt,&p); D3MulEqu(&p,tmps->p.mass/tmpsp->p[n].mass/100.0); D3SubEqu(&tmpsp->p[n].DxDt,&p); QueIns(&tmps->p,ode->last_mass); QueIns(tmps,shot_head.last); tmpsp->reload_timeout=tS+r/tmpsp->fire_rate; Noise(100,r*200,r*400); } U0 FireOneMissile(Ship *tmpsp,I64 n) { I64 i; Missile *tmpmi=&tmpsp->missiles[n]; if (!tmpmi->launched && tmpmi->target) { tmpmi->fuse_time=tS+1.0; tmpmi->die_timeout=tmpmi->fuse_time+0.125; tmpmi->img=$IB,"<8>",BI=8$; for (i=1;i<5;i++) tmpmi->s[i].flags|=SSF_INACTIVE; tmpmi->launched=TRUE; Sweep(250,300,350); } } U0 HumanFireGunBegin() { F64 r=3.0*ctrl_panel.shot_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 && tS>human->reload_timeout) switch (level) { case 3: if (!(human->s[3].flags&SSF_INACTIVE)) FireOneGun(human,3,r,fuse_time); if (!(human->s[5].flags&SSF_INACTIVE)) FireOneGun(human,4,r,fuse_time); case 2: if (!(human->s[1].flags&SSF_INACTIVE)) FireOneGun(human,1,r,fuse_time); if (!(human->s[2].flags&SSF_INACTIVE)) FireOneGun(human,2,r,fuse_time); case 1: FireOneGun(human,0,r,fuse_time); break; } } } U0 HumanFireMissileBegin(I64 n) { if (human && !human->exploding && !human->spacewalk_side && tS>human->reload_timeout) FireOneMissile(human,n); } U0 HumanFireLaserBegin() { if (human && !human->exploding && !human->spacewalk_side && tS>human->reload_timeout) human->lasering=TRUE; } U0 HumanFireLaserEnd() { if (human && !human->exploding) { human->lasering=FALSE; Snd(0); } } U0 ExpireShots() { Shot *tmps=shot_head.next,*tmps1; while (tmps!=&shot_head) { tmps1=tmps->next; if (!tmps->exploding && tS>tmps->fuse_time) { tmps->exploding=TRUE; Noise(50,3000,6000); } else if (tS>tmps->die_timeout) { QueRem(tmps); QueRem(&tmps->p); Free(tmps); } tmps=tmps1; } } U0 ExpireMissiles() { I64 i; F64 dd,best_dd; Missile *tmpmi=missile_head.next,*tmpm1; while (tmpmi!=&missile_head) { tmpm1=tmpmi->next; if (tmpmi->launched) { best_dd=MAX_F64; if (tmpmi->target) for (i=0;itarget->masses;i++) { dd=D3DistSqr(&tmpmi->p_front.x,&tmpmi->target->p[i].x); if (ddexploding && (best_dd<30*30 || tS>tmpmi->fuse_time)) { tmpmi->p_front.mass=10.0; //They go flying, if too light. tmpmi->p_back.mass =10.0; tmpmi->exploding=TRUE; Noise(50,3000,6000); } else if (tS>tmpmi->die_timeout) MissileDel(tmpmi); } tmpmi=tmpm1; } } //********************************** AI U0 AI() { CD3 p,p1,p2; Ship *tmpsp=ship_head.next; if (human && !human->exploding) { while (tmpsp!=&ship_head) { D3Sub(&p1,&tmpsp->p[0].x,&tmpsp->p[1].x); D3Sub(&p2,&tmpsp->p[0].x,&tmpsp->p[2].x); D3Add(&p,&p1,&p2); D3Sub(&p1,&human->p[0].x,&tmpsp->p[0].x); if (D3Dot(D3Unit(&p),D3Unit(&p1))>0.995 && tS>tmpsp->reload_timeout) { FireOneGun(tmpsp,0,1.5+.5,.4); } tmpsp=tmpsp->next; } } } //********************************** Init U0 InitLevel() { I64 i; if (level==4) OneTimePopUp(&msg_flags,XMSGF_ANTISPIN, "Press $$GREEN$$$$FG$$ for anti-spin stabilizer.\n"); human=ShipNew(Fs->pix_width/2,Fs->pix_height/2,ST_HUMAN1); for (i=0;iflags|=ODEF_PAUSED; } U0 Init() { I64 i; game_over=FALSE; score=0; level=1; QueInit(&ship_head); QueInit(&shot_head); QueInit(&missile_head); for (i=0;itext_attr=BLACK<<4+WHITE; MenuPush( "File {" " Abort(,CH_SHIFT_ESC);" " Exit(,CH_ESC);" "}" "Game {" " Restart(,'\n');" " LevelUp(,'+');" " LevelDown(,'-');" "}" "Play {" " Fire(,CH_SPACE);" " Thrust(,,SC_CURSOR_UP);" " StopSpin(,,SC_CURSOR_DOWN);" " Left(,,SC_CURSOR_LEFT);" " Right(,,SC_CURSOR_RIGHT);" " LeftMissile(,,SC_CURSOR_LEFT|SCF_CTRL);" " RightMissile(,,SC_CURSOR_RIGHT|SCF_CTRL);" " 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);" "}" ); AutoComplete; WinBorder; WinMax; DocCursor; DocClear; Fs->win_inhibit=WIG_TASK_DFT-WIF_SELF_FOCUS -WIF_SELF_BORDER-WIF_FOCUS_TASK_MENU-WIF_SELF_CTRLS; Fs->draw_it=&DrawIt; do { ode=ODENew(0,0.01,ODEF_HAS_MASSES); ode->derivative=&MyDerivative; ode->min_tolerance=1e-9; ode->drag_v3=0.00001; Init; QueIns(ode,Fs->last_ode); ch=0; do { while (!game_over && !show_level_msg && (msg_code=ScanMsg(&a1,&a2,1<=4) HumanFireLaserEnd; break; } break; } } AI; ExpireShots; ExpireMissiles; CheckDamage; Refresh; //msgs are only qued by winmgr if (show_level_msg) { ch=GetKey(&sc); if (ch=='\n') 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!='\n' && ch!=CH_SHIFT_ESC); AllDel(ode); } while (ch!=CH_ESC && ch!=CH_SHIFT_ESC); SettingsPop; CtrlPanelDel(cp); MenuPop; RegWriteBranch("TempleOS/XCaliber", "I64 best_score=%d;\n" "I64 msg_flags=%d;\n",best_score,msg_flags); } ž ÿÿÿÿÿÿÿÿ   ÿÿÿÿýÿÿÿ ÿÿÿÿ  ÿÿÿÿ þÿÿÿþÿÿÿ ‚øÿÿÿýÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿ  ÿÿÿÿÿÿÿÿ  ÿÿÿÿÿÿÿ  ÿÿÿÿÿÿÿÿÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿ‚øÿÿÿýÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ ýÿÿÿüÿÿÿýÿÿÿ þÿÿÿÿÿÿÿüÿÿÿ þÿÿÿýÿÿÿ þÿÿÿýÿÿÿÿÿÿÿ ÿÿÿÿýÿÿÿ ýÿÿÿ ÿÿÿÿ ÿÿÿÿýÿÿÿ üÿÿÿûÿÿÿ  ÿÿÿÿüÿÿÿúÿÿÿâ ýÿÿÿ  ÷ÿÿÿÿÿÿÿ ùÿÿÿ  ýÿÿÿþÿÿÿÿÿÿÿ ÿÿÿÿýÿÿÿ ÷ÿÿÿ ÿÿÿÿþÿÿÿ  úÿÿÿÿÿÿÿ õÿÿÿ õÿÿÿúÿÿÿ þÿÿÿ ùÿÿÿ ùÿÿÿþÿÿÿ– úÿÿÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿÿúÿÿÿüÿÿÿþÿÿÿúÿÿÿ ýÿÿÿùÿÿÿ ùÿÿÿùÿÿÿÿÿÿÿ ùÿÿÿÿÿÿÿýÿÿÿÿÿÿÿbüÿÿÿþÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿb÷ÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ