0001 #include "GarbageCollector";
0002 CTask *mem_task=Fs;
0003 F64 Game_tS() {
0004   return tS;
0005 }
0006 extern U0 ThingDel(U64 *w,Bool hard=TRUE);
0007 extern U0 LockWorld();
0008 extern U0 UnlockWorld();
0009 extern U8 *RocketChooseTarget(U8 *r);
0010 #include "Skeletal";
0011 #include "LevelEditor";
0012 #include "Matrix4x4Inv";
0013 #include "AStar";
0014 #include "Chat";
0015 #include "UI";
0016 #include "MeshMakers";
0017 C2DWorld *world=C2DWorldNew(BLACK);
0018 I64 ms_x=0,ms_y=0;
0019 
0020 U8 *pinworm_body_mesh;
0021 U8 *pinworm_head_mesh;
0022 U8 *rocket_mesh;
0023 
0024 U8 *burger_mesh;
0025 
0026 U0 InitMeshes() {
0027   I64 mat[16],step;
0028 //Food 
0029   U8 *bun_top,*bun_bottom,*tmp;
0030   bun_bottom=PrimitiveCylinder3D(30,-10.);
0031   bun_top=PrimitiveCone3D(30,10);
0032   Mat4x4IdentEqu(mat);
0033   Mat4x4TranslationEqu(mat,0,0,15);
0034   burger_mesh=CD6Merge(bun_bottom,bun_top,mat);
0035   CD6SetColor(burger_mesh,YELLOW);
0036   tmp=PrimitiveRect3D(50,50,15);
0037   CD6SetColor(tmp,LTPURPLE);
0038   Mat4x4IdentEqu(mat);
0039   Mat4x4TranslationEqu(mat,0,0,15/2);
0040   bun_top=CD6Merge(burger_mesh,tmp,mat);
0041   Mat4x4IdentEqu(mat);
0042   Mat4x4PointTo(mat,0,-50,0);
0043   Mat4x4Scale(mat,1);
0044   burger_mesh=CD6NewTransform(bun_top,mat);
0045   
0046   
0047   //Pinworm head
0048   pinworm_body_mesh=PrimitiveSphere3D(30./2,pi/3);
0049   U8 *head=PrimitiveRect3D(50,50,50),*body;
0050   U8 *horn=PrimitiveCone3D(10,50,pi/3),*thruster;
0051   Mat4x4IdentEqu(mat);
0052   Mat4x4TranslationEqu(mat,25,-25,0);
0053   Mat4x4PointTo(mat,50,-50,0);
0054   pinworm_head_mesh=CD6Merge(head,horn,mat);
0055   Mat4x4IdentEqu(mat);
0056   Mat4x4TranslationEqu(mat,25,25,0);
0057   Mat4x4PointTo(mat,50,50,0);
0058   pinworm_head_mesh=CD6Merge(pinworm_head_mesh,horn,mat);
0059 //Rcoket
0060   Mat4x4IdentEqu(mat);
0061   body=PrimitiveCylinder3D(30,60);
0062   head=PrimitiveCone3D(30,50);
0063   Mat4x4TranslationEqu(mat,0,0,60);
0064   tmp=CD6Merge(body,head,mat);
0065   body=tmp;
0066 //Make thrusters on rocket
0067   thruster=PrimitiveCylinder3D(15,45);
0068   for(step=0;step!=2;step++) {
0069     Mat4x4IdentEqu(mat);
0070     Mat4x4TranslationEqu(mat,15*Cos(step*pi),15*Sin(step*pi),-45);
0071     tmp=CD6Merge(body,thruster,mat);
0072     body=tmp;
0073   }
0074   Mat4x4IdentEqu(mat);
0075   Mat4x4RotY(mat,-pi/2);
0076   rocket_mesh=CD6NewTransform(body,mat);
0077 };
0078 InitMeshes;
0079 
0080 
0081 #define PW_BODY_IDENT 'PWBody'
0082 #define PW_HEAD_IDENT 'PWHead'
0083 #define PW_ROCKET_IDENT 'GNRckt'
0084 #define PW_GUN_IDENT 'GNGun'
0085 #define PW_EXPLODE_IDENT 'EXExpd'
0086 #define PW_BURGER_IDENT 'Burger'
0087 
0088 #define PW_MAX_DIST 30
0089 #define PW_LINK_ANGLE (pi/4)
0090 
0091 #define FRICTION .965
0092 
0093 class CWormStats {
0094   I64 rocket_mass;
0095   I64 max_rockets;
0096   F64 max_speed;
0097   F64 max_health;
0098 };
0099 
0100 #define WSTATS_F_ROCKET_MASS 1
0101 #define WSTATS_F_MAX_ROCKETS 2
0102 #define WSTATS_F_MAX_SPEED 4
0103 #define WSTATS_F_MAX_HEALTH 8
0104 
0105 U0 WormStatsInit(CWormStats *stats) {
0106   stats->rocket_mass=50;
0107   stats->max_rockets=1.;
0108   stats->max_speed=50;
0109   stats->max_health=100.;
0110 }
0111 
0112 U0 WormStatsLevelUp(CWormStats *stats,I64 flags,I64 level=1) {
0113   if(flags&WSTATS_F_MAX_SPEED)
0114     stats->max_speed+=level*15.;
0115   if(flags&WSTATS_F_MAX_HEALTH)
0116     stats->max_health+=level*50.;
0117   if(flags&WSTATS_F_MAX_ROCKETS)
0118     stats->max_rockets+=level;
0119   if(flags&WSTATS_F_ROCKET_MASS)
0120     stats->rocket_mass+=level;
0121 }
0122 extern class CPinWormBody;
0123 class CMass2:CMass {
0124   CPinWormBody *who;
0125   COrder2D3 dummy,dummy2;
0126 };
0127 class CSpring2:CSpring {
0128   F64 strength; //Froce before snapping.
0129 };
0130 #define THINGF_TO_DELETE 1
0131 #define THINGF_EXPLODED 2
0132 #define THINGF_DAMAGED 4
0133 #define THINGF_HIT_LINE 8
0134 #define THINGF_HIT_THING 16
0135 class CGameThing {
0136   CThing *qnext,*qlast;
0137   U64 ident;
0138   U64 flags;
0139   C2DObject *obj;
0140   CMass old_mass;
0141   F64 health,food;
0142   CMass2 mass;
0143   CI64Set *path_finder_data; //CMove
0144   F64 dx,dy,dz; //From player
0145   F64 dx2,dy2,dz2; //from explosions n stuff
0146   CGameThing *hit_thing;
0147   U0 (*animate)(CGameThing *,...);
0148   U0 (*collide_thing)(CGameThing *,...);
0149   U0 (*collide_line)(CGameThing *,...);
0150   U0 (*damage)(CGameThing *,...);
0151   U0 (*ai)(CGameThing *);
0152 }
0153 
0154 class CPinWormBody:CGameThing {
0155   CSpring2 spring;
0156   F64 angle;
0157   CPinWormBody *next;
0158   CPinWormBody *parent;
0159 };
0160 class CPinWorm:CPinWormBody {
0161   CWormStats stats;
0162   F64 last_rocket_tS;
0163   CBot *chat_bot;
0164   F64 spawn_at_x,spawn_at_y;
0165   F64 look_at_x,look_at_y;
0166 //Player
0167   CUIBigText *health_meter;
0168   CUIBigText *food_meter;
0169   CUITree *menu;
0170   CUIGrid *hotbar;
0171 };
0172 class CRocket:CGameThing {
0173   CMass2 mass2;
0174   CSpring2 spring;
0175   CGameThing *who_shot_rocket;
0176   F64 explosion_force;
0177 };
0178 class CGun:CGameThing {
0179   F64 last_fire;
0180   F64 max;
0181 };
0182 class CBurger:CGameThing {
0183   F64 amt;
0184 };
0185 class CGame {
0186   CQue objects;
0187   CLevel *level;
0188   CD3 camera_center;
0189   CQue particles;
0190   F64 camera_height;
0191   CChat *chat_log;
0192   I64 locked_flags;
0193   CUIRoot *game_ui;
0194 } game;
0195 class CExplosion:CGameThing {
0196   CGameThing *from;
0197   F64 start_tS;
0198   F64 duration;
0199   F64 force;
0200 //Private
0201   F64 rad;
0202 };
0203 CPinWorm *player_worm;
0204 U0 ThingActionExplode(CGameThing *thing,...) {
0205   thing->flags|=THINGF_EXPLODED;
0206   ThingDel(thing,FALSE);
0207 }
0208 Bool ThingIsStuck(CGameThing *thing,F64 fudge=1./10.) {
0209   CD3 tmp;
0210   F64 expected;
0211   D3Sub(&tmp,&thing->mass.x,&thing->old_mass.x);
0212   expected=D3Norm(&thing->dx);
0213   return D3Norm(&tmp)<expected*fudge;
0214 }
0215 U0 ComputeExplosionRadius(CExplosion *exp) {
0216   F64 rad=(Game_tS-exp->start_tS)/2./exp->duration;
0217   if(rad>.5) {
0218    rad=exp->force-exp->force*((rad-.5)*2)+2.;
0219   } else {
0220    rad=rad*exp->force*2;
0221   }
0222   exp->rad=Max(rad,1.);
0223   exp->mass.mass=exp->rad;
0224 }
0225 U0 DrawExplosion(CExplosion *exp,CDC *dc) {
0226   ComputeExplosionRadius(exp);
0227   F64 rad=exp->rad;
0228   I64 which;
0229   if(rad<=1.1) {
0230     exp->flags|=THINGF_TO_DELETE;
0231     return;
0232   }
0233   dc->thick=1;
0234   for(which=1;which<rad;++which) {
0235     dc->color=7+RandU64&7;
0236     GrCircle3(dc,exp->mass.x,exp->mass.y,0,which);
0237   } 
0238 }
0239 
0240 #include "Particles";
0241 CI64Set *GetThingsInRadius(F64 x,F64 y,F64 rad) {
0242   CI64Set *ret=I64SetNew;
0243   //TODO something better
0244   CGameThing *head=&game.objects,*t;
0245   for(t=head->qnext;head!=t;t=t->qnext) {
0246       if(Sqr(t->mass.x-x)+Sqr(t->mass.y-y)<rad*rad)
0247         I64SetAdd(ret,t);
0248   }
0249   return ret;
0250 }
0251 U0 DamageThing(CGameThing *t,F64 d) {
0252   t->health-=d;
0253   t->flags|=THINGF_DAMAGED;
0254 }
0255 
0256 U0 AddMass(CMass2 *m) {
0257   LockWorld;
0258   QueIns(m,mem_task->last_ode->last_mass);
0259   UnlockWorld;
0260 }
0261 
0262 CExplosion *ExplosionNew(F64 x,F64 y,CGameThing *from=NULL,F64 force=200.,F64 damage_percent=1/10.) {
0263   CExplosion *exp=GCCAlloc(sizeof(CExplosion));
0264   I64 cnt;
0265   CD3 tmp,at;
0266   F64 d;
0267   CI64Set *around=GetThingsInRadius(x,y,force);
0268   CGameThing *gt;
0269   exp->ident=PW_EXPLODE_IDENT;
0270   exp->from=from;
0271   exp->mass.x=x;
0272   exp->mass.y=y;
0273   exp->mass.mass=1;
0274   exp->mass.who=exp;
0275   exp->start_tS=Game_tS;
0276   exp->duration=1.;
0277   exp->force=200;
0278   AddMass(&exp->mass);
0279   QueIns(&exp->qnext,&game.objects);
0280 //Particles
0281   for(cnt=0;cnt!=21;cnt++) {
0282     ParticleNew(x,y,Rand*2*pi,15*Rand+15);
0283   }
0284 
0285   ClipRect(game.level,x-force/2.,y-force/2.,x+force/2.,y+force/2.);
0286 
0287   at.x=x;
0288   at.y=y;
0289   at.z=0;
0290   for(cnt=0;cnt!=around->cnt;++cnt) {
0291     gt=around->body[cnt];
0292     D3Sub(&tmp,&gt->mass.x,&at);
0293     d=D3Norm(&tmp);
0294     if(d<force-10.) {
0295       d+=10.;
0296       d=force-d;
0297       D3MulEqu(&tmp,d*.5);
0298       D3AddEqu(&gt->dx2,&tmp);
0299       if(from!=gt)
0300         DamageThing(gt,d*damage_percent);
0301     }
0302   }
0303 
0304   I64SetDel(around);
0305   Noise(250,50,80);
0306   return exp;
0307 }
0308 CPinWormBody *PinWormBodyNew(CPinWorm *worm) {
0309   F64 x;F64 y;
0310   CPinWormBody *body=GCCAlloc(sizeof(CPinWormBody)),*next;
0311   CMass *mass;
0312   CPinWorm *me=worm;
0313 again:;
0314   if(worm->ident==PW_HEAD_IDENT) {
0315     mass=&worm->mass;
0316     next=worm->next;
0317     x=worm->mass.x;
0318     y=worm->mass.y;
0319   } else if(worm->ident==PW_BODY_IDENT) {
0320     mass=&worm(CPinWormBody*)->mass;
0321     next=worm(CPinWormBody*)->next;
0322     x=worm(CPinWormBody*)->mass.x;
0323     y=worm(CPinWormBody*)->mass.y;
0324   }
0325   if(next) {
0326     worm=next;
0327     goto again;
0328   }
0329   body->parent=worm;
0330   worm(CPinWormBody*)->next=body;
0331   body->health=100;
0332   body->ident=PW_BODY_IDENT;
0333   body->mass.who=body;
0334   body->mass.x=x;
0335   body->mass.y=y;
0336   body->mass.mass=PW_MAX_DIST;
0337   AddMass(&body->mass);
0338   body->spring.const=1e5;
0339   body->spring.end2=&body->mass;
0340   body->spring.end1=mass;
0341   body->spring.flags=SSF_NO_COMPRESSION;
0342   body->spring.rest_len=25.;
0343   QueIns(&body->spring,Fs->last_ode->last_spring);
0344   QueIns(&body->qnext,&game.objects);
0345   body->obj=C2DObjectNewFromMesh(pinworm_body_mesh);
0346   body->obj->color=WHITE;
0347   AddObjectToWorld(body->obj,world);
0348   body->next=next;
0349   return body;
0350 }
0351 
0352 
0353 CPinWorm *PinWormNew(F64 x,F64 y) {
0354   CPinWorm *pw=GCCAlloc(sizeof(CPinWorm));
0355   pw->health=100;
0356   pw->ident=PW_HEAD_IDENT;
0357   pw->mass.x=x;
0358   pw->mass.y=y;
0359   pw->mass.mass=50.;
0360   pw->mass.who=pw;
0361   AddMass(&pw->mass);
0362   QueIns(&pw->qnext,&game.objects);
0363   pw->obj=C2DObjectNewFromMesh(pinworm_head_mesh);
0364   AddObjectToWorld(pw->obj,world);
0365   WormStatsInit(&pw->stats);
0366   return pw;
0367 }
0368 
0369 
0370 CBurger *BurgerNew(F64 x,F64 y) {
0371   CBurger *pw=GCCAlloc(sizeof(CPinWorm));
0372   pw->ident=PW_BURGER_IDENT;
0373   pw->mass.x=x;
0374   pw->mass.y=y;
0375   pw->mass.mass=30.;
0376   pw->mass.who=pw;
0377   AddMass(&pw->mass);
0378   QueIns(&pw->qnext,&game.objects);
0379   pw->obj=C2DObjectNewFromMesh(burger_mesh);
0380   AddObjectToWorld(pw->obj,world);
0381   return pw;
0382 }
0383 
0384 #include "Weapons/Green";
0385 #include "Weapons/Builder";
0386 U0 RocketCollideThing(CRocket *r,...) {
0387   CGameThing *other=argv[0];
0388   if(other!=r&&other!=r->who_shot_rocket)
0389     ThingActionExplode(r);
0390 }
0391 U0 RocketAI(CRocket *r) {
0392   CGameThing *target=RocketChooseTarget(r);
0393   F64 force=Max(20.*30.,D3Norm(&r->mass.DxDt)),angle;
0394   if(target) {
0395     angle=Arg(target->mass.x-r->mass.x,target->mass.y-r->mass.y);
0396     r->mass.DxDt=force*Cos(angle);
0397     r->mass.DyDt=force*Sin(angle);
0398   }
0399 }
0400 U0 RocketNew(F64 x,F64 y,F64 angle=0,F64 force=200.,CGameThing *who_shot=NULL) {
0401   CRocket *rocketb=GCCAlloc(sizeof(CRocket));
0402   rocketb->collide_line=&ThingActionExplode;
0403   rocketb->collide_thing=&RocketCollideThing;
0404   rocketb->ai=&RocketAI;
0405   rocketb->ident=PW_ROCKET_IDENT;
0406   rocketb->mass.x=x+30*Cos(angle);
0407   rocketb->mass.y=y+30*Sin(angle);
0408   rocketb->mass.mass=50;
0409   rocketb->mass.who=rocketb;
0410   rocketb->mass2.x=x+60*Cos(angle);
0411   rocketb->mass2.y=y+60*Sin(angle);
0412   rocketb->mass2.mass=25;
0413   rocketb->mass2.who=rocketb;
0414   rocketb->spring.end2=&rocketb->mass;
0415   rocketb->spring.end1=&rocketb->mass2;
0416   rocketb->spring.strength=10000;
0417   rocketb->spring.const=100.;
0418   rocketb->spring.rest_len=30.;
0419   rocketb->explosion_force=force;
0420   rocketb->who_shot_rocket=who_shot;
0421   rocketb->mass.DxDt=25*Cos(angle)*30;
0422   rocketb->mass.DyDt=25*Sin(angle)*30;
0423   rocketb->mass2.DxDt=25*Cos(angle)*30;
0424   rocketb->mass2.DyDt=25*Sin(angle)*30;
0425 
0426   AddMass(&rocketb->mass);
0427   AddMass(&rocketb->mass2);
0428   QueIns(&rocketb->spring,Fs->last_ode->last_spring);
0429 
0430   QueIns(&rocketb->qnext,&game.objects);
0431 
0432   rocketb->obj=C2DObjectNewFromMesh(rocket_mesh);
0433 //  rocketb->obj=C2DObjectNewFromMesh(MakeSpawnerMesh(RED));
0434   rocketb->obj->color=LTRED;
0435   AddObjectToWorld(rocketb->obj,world);
0436   return rocketb;
0437 }
0438 
0439 U0 PinWormFireRocket(CPinWorm *pw) {
0440   if(pw->last_rocket_tS+1./pw->stats.max_rockets>Game_tS)
0441     return;
0442   pw->last_rocket_tS=Game_tS;
0443   Sweep(250,70,50);
0444   RocketNew(pw->mass.x,
0445         pw->mass.y,
0446         pw->angle,
0447         200.,
0448         pw
0449         );
0450 }
0451 U0 PinWormFireGreen(CPinWorm *pw) {
0452 }
0453 U0 PinWormFireBuildWall(CPinWorm *pw) {
0454   if(pw->last_rocket_tS+.5>Game_tS)
0455     return;
0456   pw->last_rocket_tS=Game_tS;
0457   Sweep(250,70,50);
0458   
0459   BuildWallFire(pw->look_at_x,
0460         pw->look_at_y,
0461         pw
0462         );
0463 }
0464 
0465 
0466 Bool CanSeeThing(CGameThing *a,CGameThing *b) {
0467   F64 dist=Sqrt(Sqr(b->mass.y-a->mass.y)+Sqr(b->mass.x-a->mass.x));
0468   return MaxMoveDist(game.level,a->mass.x,a->mass.y,b->mass.x,b->mass.y)>dist;
0469 }
0470 
0471 
0472 U0 PinWormUnStuck(CPinWorm *pw) {
0473   CPinWormBody *body;
0474   if(ThingIsStuck(pw)) {
0475     body=pw->next;
0476     while(body&&CanSeeThing(pw,body)) {
0477       body=body->next;
0478     }
0479 //Cant see this pinworm body,fire a rocket at the wall(blocking the pinworm body)
0480     if(body) {
0481       pw->angle=Arg(body->mass.x-pw->mass.x,body->mass.y-pw->mass.y);
0482       PinWormFireRocket(pw);
0483     }
0484   }
0485 } 
0486 
0487 CPinWorm *BelongsTo(CPinWormBody *b) {
0488   while(b&&b->ident==PW_BODY_IDENT)
0489     b=b->parent;
0490   return b;
0491 }
0492 CPinWormBody *RocketChooseTarget(CRocket *r) {
0493   CGameThing *ignore=r->who_shot_rocket;
0494   CPinWormBody *body,*best=NULL,*head;
0495   I64 idx;
0496   F64 best_dist=I16_MAX*I16_MAX,dist;
0497   CD3 tmp;
0498 
0499   CI64Set *things=GetThingsInRadius(r->mass.x,r->mass.y,5000);
0500   for(idx=0;idx!=things->cnt;++idx) {
0501     body=things->body[idx];
0502     if(body->ident==PW_HEAD_IDENT||body->ident==PW_BODY_IDENT) {
0503       head=BelongsTo(body);
0504       if(head!=r->who_shot_rocket)
0505         if(r!=body) { //&&CanSeeThing(r,body)
0506           D3Sub(&tmp,&r->mass.x,&body->mass.x);
0507           dist=D3Norm(&tmp);
0508           if(dist<best_dist) {
0509             best_dist=dist;
0510             best=body;
0511           }
0512         }
0513     }
0514   }  
0515   return best;
0516 }
0517 U0 ThingDel(U64 *w,Bool hard=TRUE) {
0518   if(!w) return;
0519   CPinWorm *pw;
0520   CPinWormBody *body;
0521   CGun *g;
0522   CRocket *rocket;
0523   pw=w;
0524   if(!hard) {
0525     pw=w;
0526     pw->flags|=THINGF_TO_DELETE;
0527     return ;
0528   }
0529   LockWorld;
0530   if(pw->path_finder_data) 
0531     I64SetDel(pw->path_finder_data);
0532   if(w[2]==PW_GUN_IDENT) {
0533     g=w;
0534     C2DObjectDel(g->obj);
0535     QueRem(&g->mass);
0536     QueRem(g);
0537 ret:
0538     UnlockWorld;
0539     return;
0540   }
0541   if(w[2]==PW_ROCKET_IDENT) {
0542     rocket=w;
0543     C2DObjectDel(rocket->obj);
0544     QueRem(&rocket->mass);
0545     QueRem(&rocket->mass2);
0546     QueRem(&rocket->spring);
0547     QueRem(rocket);
0548     goto ret;
0549   }
0550   if(w[2]==PW_HEAD_IDENT) {
0551     pw=w;
0552     ThingDel(pw->next,FALSE);
0553     if(pw->chat_bot)
0554       BotDel(pw->chat_bot);
0555     C2DObjectDel(pw->obj);
0556     QueRem(&pw->mass);
0557     QueRem(&pw->qnext);
0558     goto ret;
0559   }
0560   if(w[2]==PW_GREEN_SHOT_IDENT) {
0561     body=w;
0562     C2DObjectDel(body->obj);
0563     QueRem(&body->mass);
0564     QueRem(body);
0565     goto ret;
0566   }
0567   if(w[2]==PW_BODY_IDENT) {
0568     body=w;
0569     ThingDel(body->next,FALSE);
0570     C2DObjectDel(body->obj);
0571 //Remove both ends,QueInit to allow re-QueRem
0572     QueRem(body->spring.end1);
0573     QueInit(body->spring.end1);
0574 //Ditto
0575     QueRem(body->spring.end2);
0576     QueInit(body->spring.end2);
0577 
0578     QueRem(&body->spring);
0579     QueRem(&body->qnext);
0580     QueRem(&body->mass);
0581     goto ret;
0582   }
0583   if(w[2]==PW_BURGER_IDENT) {
0584     pw=w;
0585     C2DObjectDel(pw->obj);
0586     QueRem(&pw->qnext);
0587     QueRem(&pw->mass);
0588     goto ret;
0589   }
0590 //
0591   pw=w;
0592   QueRem(&pw->mass);
0593   QueRem(w);
0594   GCFree(w);
0595   goto ret;
0596 }
0597 U0 UpdateWorldMatrix() {
0598   CDC *scrn=world->scrn;
0599   Mat4x4IdentEqu(scrn->r);
0600   Mat4x4TranslationEqu(scrn->r,
0601         -game.camera_center.x,
0602         -game.camera_center.y,
0603         300+game.camera_center.z+game.camera_height);
0604 }
0605 
0606 
0607 #include "StarBG.HC";
0608 U0 DrawStarsBG(CDC *d) {
0609   I64 xoff,yoff,x,y,cx=game.camera_center.x,cy=game.camera_center.y;
0610   d->color=WHITE;
0611   d->thick=4;
0612   for(xoff=cx-1024;xoff<=cx+1024;xoff+=STAR_SPACING) {
0613     for(yoff=cy-1024;yoff<=cy+1024;yoff+=STAR_SPACING) {
0614       x=xoff,y=yoff;
0615       if(Star(&x,&y)) {
0616         GrPlot3(d,x,y,1);
0617       }
0618     }
0619   }
0620  
0621 }
0622 
0623 U0 PinWormDrawIt(CTask *t,CDC *d) {
0624   LockWorld;
0625   if(player_worm) {
0626     game.camera_center.x=player_worm->mass.x;
0627     game.camera_center.y=player_worm->mass.y;
0628   }
0629   CPinWorm *pw;
0630   CPinWormBody *body;
0631   CLine *line,*head;
0632   CRocket *r;
0633   CNode *st,*en;
0634   CDC *scrn=world->scrn;
0635   I64 chat_w=LOG_WIDTH*8,chat_h=LOG_SZ*8;
0636   I64 chat_x=t->pix_width-chat_w-20,chat_y=20;
0637   UpdateWorldMatrix;
0638   for(pw=game.objects.next;pw!=&game.objects;pw=pw->qnext) {
0639     if(pw->obj)
0640       C2DObjectMove(pw->obj,pw->mass.x,pw->mass.y);
0641     if(pw->ident==PW_ROCKET_IDENT) {
0642       r=pw;
0643       C2DObjectRotate(pw->obj,
0644         Arg(r->mass.x-r->mass2.x,r->mass.y-r->mass2.y)
0645       );
0646     }
0647   }
0648   DrawWorld(t,world);
0649   DrawStarsBG(world->scrn);
0650   DrawParticles(world->scrn);
0651   for(pw=game.objects.next;pw!=&game.objects;pw=pw->qnext) {
0652     if(pw->ident==PW_EXPLODE_IDENT)
0653      DrawExplosion(pw,world->scrn);
0654   }
0655   if(game.level) {
0656     head=&game.level->lines;
0657     for(line=head->next;line!=head;line=line->next) {
0658       scrn->color=WHITE;
0659       scrn->thick=3;
0660       st=line->start;
0661       en=line->end;
0662       GrLine3(scrn,st->x,st->y,0,en->x,en->y,0);
0663 
0664     }
0665   }
0666   GrBlot(d,0,0,world->scrn);
0667   DrawUI(t,d);
0668   ChatDraw(d,chat_x,chat_y,game.chat_log);
0669   UnlockWorld;
0670 }
0671 F64 LineDist(CD3 *a,CD3 *b,CD3 *pt,CD3 *hit_at=NULL) {
0672   CD3 rel_pt;
0673   CD3 rel_b;
0674   CD3 from;
0675   D3Sub(&rel_pt,pt,a);
0676   D3Sub(&rel_b,b,a);
0677 <1>
0678 0679 0680 0681 0682 F64 rel=D3Dot(&rel_pt,&rel_b)/D3Dot(&rel_b,&rel_b); 0683 if(0.<=rel<=1.) { 0684 from.x=a->x+rel_b.x*rel; 0685 from.y=a->y+rel_b.y*rel; 0686 from.z=0; 0687 } else if(rel<0.) { 0688 D3Equ(&from,a); 0689 } else if(rel>1.) { 0690 D3Equ(&from,b); 0691 } 0692 if(hit_at) 0693 D3Copy(hit_at,&from); 0694 D3SubEqu(&from,pt); 0695 return D3Norm(&from); 0696 } 0697 F64 Friction(CD3 *dxdt,F64 max) { 0698 F64 norm=D3Norm(dxdt); 0699 F64 inp=norm/max,force; 0700 if(inp<1.) 0701 return 1.; 0702 //Sigmoid,cap at around max 0703 F64 sigmoid=1./(1.+Exp(-inp)); 0704 //Weighted average between sigmoid x force fudge 0705 force=max/norm*sigmoid+(1.-sigmoid); 0706 return force; 0707 } 0708 Bool IsProjectile(CGameThing *t) { 0709 return t->ident==PW_ROCKET_IDENT|| 0710 t->ident==PW_GREEN_SHOT_IDENT; 0711 } 0712 U0 Derivative(CMathODE *ode,F64 ,COrder2D3 *,COrder2D3 *) { 0713 LockWorld; 0714 CMass2 *mass,*head=&ode->next_mass,*mass2; 0715 CSpring2 *spr; 0716 F64 d,max,dd,angle,speed; 0717 CD3 tmp,*tmp_ptr,hit_at; 0718 CBurger *burger; 0719 I64 idx=0; 0720 CPinWormBody *body,*body2; 0721 CPinWorm *who; 0722 for(mass=head->next;mass!=head;mass=mass->next) { 0723 if(who=mass->who) { 0724 MemCpy(&who->old_mass,mass,sizeof CMass); 0725 } 0726 if(mass->who&&mass->who->ident==PW_HEAD_IDENT) { 0727 d=Friction(&mass->state->DxDt,who->stats.max_speed*30); 0728 D3MulEqu(&mass->state->DxDt,d); 0729 } else if(mass->who&&mass->who->ident==PW_ROCKET_IDENT) { 0730 d=Friction(&mass->state->DxDt,50*30); 0731 D3MulEqu(&mass->state->DxDt,d); 0732 } else if(mass->who&&mass->who->ident==PW_GREEN_SHOT_IDENT) { 0733 d=Friction(&mass->state->DxDt,300.*30.); 0734 D3MulEqu(&mass->state->DxDt,d); 0735 } else { 0736 D3MulEqu(&mass->state->DxDt,FRICTION); 0737 } 0738 } 0739 for(body=game.objects.next;body!=&game.objects;body=body->qnext) { 0740 tmp_ptr=&body->mass.state->DxDt; 0741 D3AddEqu(tmp_ptr,&body(CPinWorm*)->dx2); 0742 D3Equ(&body(CPinWorm*)->dx2,0,0,0); 0743 if(body->ident==PW_HEAD_IDENT) { 0744 body2=body->next; 0745 D3AddEqu(tmp_ptr,&body(CPinWorm*)->dx); 0746 while(body2) { 0747 spr=&body2->spring; 0748 if(spr->end2&&spr->end1) 0749 body2->angle=Arg(spr->end1->x-spr->end2->x,spr->end1->y-spr->end2->y); 0750 0751 body2=body2->next; 0752 } 0753 } 0754 } 0755 0756 CRocket *rocket; 0757 0758 //Balls repel each other 0759 for(mass=head->next;mass!=head;mass=mass->next) { 0760 for(mass2=head->next;mass2!=head;mass2=mass2->next) { 0761 if(mass!=mass2) { 0762 d=D3NormSqr(D3Sub(&tmp,&mass->state->x,&mass2->state->x))+1.; 0763 max=Max(mass->mass,mass2->mass); 0764 //If burger and worm,burgers seek worm 0765 if(burger=mass->who) { 0766 if(who=mass2->who) { 0767 if(burger->ident==PW_BURGER_IDENT&&who->ident==PW_HEAD_IDENT) { 0768 if(d<300.*300) { 0769 dd=(300.-Sqrt(d))/600.; 0770 D3MulEqu(&tmp,dd); 0771 D3SubEqu(&mass->state->DxDt,&tmp); 0772 goto gravity_skip; 0773 } 0774 } 0775 } 0776 } 0777 if(d<max*max) { 0778 if(rocket=mass->who) { 0779 //Check for player(?) pinworm's body 0780 who=mass2->who; 0781 while(who&&who->ident==PW_BODY_IDENT) { 0782 who=who(CPinWormBody*)->parent; 0783 } 0784 if(who!=rocket) { 0785 rocket->flags|=THINGF_HIT_THING; 0786 rocket->hit_thing=who; 0787 } 0788 } 0789 d=Sqrt(d); 0790 if(mass2->who&&mass2->who->ident==PW_EXPLODE_IDENT) { 0791 if(who=mass->who) 0792 if(mass2->who(CExplosion*)->from!=who) 0793 DamageThing(who,3.*Rand+3.); 0794 dd=(max-d+10)*10; 0795 } else 0796 dd=(max-d)*.5; 0797 D3MulEqu(&tmp,dd/(d+3)); 0798 D3AddEqu(&mass->state->DxDt,&tmp); 0799 gravity_skip:; 0800 } 0801 } 0802 } 0803 } 0804 //Balls cannot pass lines 0805 CLine *line; 0806 CI64Set *lines; 0807 CD3 a,b; 0808 if(game.level) { 0809 for(mass=head->next;mass!=head;mass=mass->next) { 0810 lines=LevelGetLinesInRadius(game.level,mass->state->x,mass->state->y,mass->mass+speed); 0811 0812 for(idx=0;idx!=lines->cnt;++idx) { 0813 line=lines->body[idx]; 0814 a.x=line->start->x; 0815 a.y=line->start->y; 0816 a.z=0; 0817 b.x=line->end->x; 0818 b.y=line->end->y; 0819 b.z=0; 0820 0821 // 0822 // Heres the deal,DONT speed through walls,so check walls within radius of speed 0823 // 0824 tmp.x=mass->state->x+mass->state->DxDt*(1.-1e-15); 0825 tmp.y=mass->state->y+mass->state->DyDt*(1.-1e-15); 0826 tmp.z=0; 0827 if(PlaneIntersect(&hit_at,&mass->state->x,&tmp,&a,&b)) { 0828 D3Sub(&mass->state->DxDt,&hit_at,&mass->state->x); 0829 rocket=mass->who; 0830 if(rocket) 0831 rocket->flags|=THINGF_HIT_LINE; 0832 } 0833 0834 0835 0836 d=LineDist(&a,&b,&mass->state->x,&tmp)+1.; 0837 max=mass->mass/1.5; 0838 if(d<max+5.) { 0839 if(mass->who) { 0840 rocket=mass->who; 0841 rocket->flags|=THINGF_HIT_LINE; 0842 } 0843 D3SubEqu(&tmp,&mass->state->x); 0844 dd=(max*2-d); 0845 D3MulEqu(&tmp,dd/(d+1)); 0846 D3Equ(&mass->state->DxDt,-tmp.x,-tmp.y,0); 0847 } 0848 } 0849 I64SetDel(lines); 0850 next_lines:; 0851 } 0852 } 0853 UnlockWorld; 0854 } 0855 0856 CI64Set *GetThingsOfTypeInRadius(F64 x,F64 y,F64 rad,U64 ident=INVALID_PTR) { 0857 CI64Set *ret=I64SetNew; 0858 //TODO something better 0859 CGameThing *head=&game.objects,*t; 0860 for(t=head->qnext;head!=t;t=t->qnext) { 0861 if(t->ident==ident||ident==INVALID_PTR) 0862 if(Sqr(t->mass.x-x)+Sqr(t->mass.y-y)<rad*rad) 0863 I64SetAdd(ret,t); 0864 } 0865 return ret; 0866 } 0867 U0 CheckPowerUps() { 0868 CBurger *burger,*head=&game.objects; 0869 CPinWorm *pw; 0870 CD3 tmp; 0871 for(burger=head->qnext;burger!=head;burger=burger->qnext) { 0872 for(pw=head->qnext;pw!=head;pw=pw->qnext) { 0873 if(burger->ident==PW_BURGER_IDENT&&pw->ident==PW_HEAD_IDENT) { 0874 if(!(burger->flags&THINGF_TO_DELETE)) { 0875 D3Sub(&tmp,&burger->mass.x,&pw->mass.x); 0876 if(D3Norm(&tmp)<60.&&CanSeeThing(burger,pw)) { 0877 ThingDel(burger,FALSE); //soft delete 0878 PinWormBodyNew(pw); 0879 } 0880 } 0881 } 0882 } 0883 } 0884 } 0885 0886 0887 CPinWorm *PinWormDie(CPinWorm *worm) { 0888 if(worm==player_worm) 0889 player_worm=NULL; 0890 CPinWormBody *body=worm; 0891 CBurger *b; 0892 F64 boom_force,angle; 0893 I64 i; 0894 while(body=body->next) { 0895 b=BurgerNew(body->mass.x,body->mass.y); 0896 angle=Rand*2*pi; 0897 boom_force=Rand*30.+10; 0898 b->dx2=boom_force*Cos(angle); 0899 b->dy2=boom_force*Sin(angle); 0900 0901 //Boom particles 0902 for(i=0;i!=3;++i) 0903 ParticleNew(body->mass.x,body->mass.y,2*pi*Rand,Rand*40.+5.); 0904 0905 ThingDel(body,FALSE); //soft delete 0906 } 0907 b=BurgerNew(worm->mass.x,worm->mass.y); 0908 ThingDel(worm,FALSE); //soft delete 0909 } 0910 0911 U0 ScrnCoordsToWorldCoords(I64 *_x,I64 *_y) { 0912 I64 inv[16],x=*_x,y=*_y,z; 0913 // turn mouse cors to scrn cords 0914 UpdateWorldMatrix; 0915 F64 homogen=world->scrn->r[2*4+3]/ToF64(GR_SCALE); 0916 Mat4x4Equ(inv,world->scrn->r); 0917 Mat4x4Inv(inv); 0918 x=ToF64(*_x-world->scrn->x)/(200./homogen); 0919 y=ToF64(*_y-world->scrn->y)/(200./homogen); 0920 z=1; 0921 Mat4x4MulXYZ(inv,&x,&y,&z); 0922 *_x=x; 0923 *_y=y; 0924 } 0925 U0 PlayerAI(CPinWorm *worm) { 0926 game.camera_center.x=worm->mass.x; 0927 game.camera_center.y=worm->mass.y; 0928 // game.camera_center.z=worm->mass.z; 0929 I64 m,x,y; 0930 I64 wx,wy,sx,sy; 0931 U8 buf[STR_LEN]; 0932 F64 angle=worm->angle,d,dx=worm->dx,dy=worm->dy; 0933 if(!game.game_ui) { 0934 ui_root=game.game_ui=UIRootNew; 0935 worm->health_meter=UIBigTextNew("",40); 0936 worm->food_meter=UIBigTextNew("",40); 0937 worm->hotbar=UIGridNew(10,1); 0938 worm->menu=UITreeNew("Menu"); 0939 worm->menu->no_occupy_space=TRUE; 0940 worm->food_meter->no_occupy_space=TRUE; 0941 worm->health_meter->no_occupy_space=TRUE; 0942 worm->hotbar->no_occupy_space=TRUE; 0943 UIElemAdd(game.game_ui,worm->health_meter); 0944 UIElemAdd(game.game_ui,worm->food_meter); 0945 UIElemAdd(game.game_ui,worm->menu); 0946 UIElemAdd(game.game_ui,worm->hotbar); 0947 0948 } 0949 worm->health_meter->x=mem_task->pix_width-75; 0950 worm->health_meter->y=mem_task->pix_height-75; 0951 worm->health_meter->text=GCStrNew(StrPrint(buf,"Health:\n%fHP",worm->health)); 0952 0953 worm->food_meter->x=75; 0954 worm->food_meter->y=mem_task->pix_height-75; 0955 worm->food_meter->text=GCStrNew(StrPrint(buf,"WormBux:\n$%f",worm->food)); 0956 0957 worm->hotbar->x=Fs->pix_width>>1-worm->hotbar->w2/2.; 0958 worm->hotbar->y=Fs->pix_height-35; 0959 0960 0961 while(m=ScanMsg(&x,&y)) { 0962 if(UIMsg(ui_root,m,x,y)) 0963 goto skip; 0964 if(m==MSG_MS_MOVE) { 0965 ms_x=x; 0966 ms_y=y; 0967 } 0968 if(m==MSG_MS_L_DOWN) { 0969 ms_x=x; 0970 ms_y=y; 0971 } 0972 if(m==MSG_KEY_DOWN) { 0973 if(game.chat_log->focus) { 0974 if(!ChatInteract(game.chat_log,m,x,y)) 0975 goto dft_key; 0976 } else { 0977 dft_key: 0978 if(ToUpper(x)=='T') 0979 game.chat_log->focus=TRUE; 0980 if(x==CH_ESC) { 0981 Exit; 0982 } 0983 } 0984 } 0985 if(m==MSG_MS_L_DOWN||m==MSG_MS_MOVE) { 0986 ms_x=x; 0987 ms_y=y; 0988 sx=x,sy=y; 0989 wx=worm->mass.x; 0990 wy=worm->mass.y; 0991 ScrnCoordsToWorldCoords(&sx,&sy); 0992 worm->look_at_x=sx; 0993 worm->look_at_y=sy; 0994 angle=Arg(dx=sx-wx,dy=sy-wy); 0995 } 0996 0997 if(m==MSG_MS_L_DOWN) { 0998 worm->angle=angle; 0999 // PinWormFireRocket(worm); 1000 // PinWormFireGreen(worm); 1001 PinWormFireBuildWall(worm); 1002 } 1003 skip:; 1004 } 1005 if(worm->health<=0) { 1006 PinWormDie(worm); 1007 return; 1008 } 1009 1010 d=Clamp(Sqrt( 1011 Sqr(dx)+ 1012 Sqr(dy)),15.,worm->stats.max_speed); 1013 worm->dx=dx,worm->dy=dy; 1014 D3Unit(&worm->dx); 1015 D3MulEqu(&worm->dx,d); 1016 if(d>30.) { 1017 worm->angle=angle; 1018 worm->obj->rot=angle; 1019 } 1020 } 1021 #include "AI.HC"; 1022 U0 InitLevelThings() { 1023 CLevel *l=game.level; 1024 CThing *thing,*head=&l->things; 1025 CPinWorm *pw; 1026 for(thing=head->next;thing!=head;thing=thing->next) { 1027 if(!StrCmp(thing->class_name,"Player")) { 1028 pw=PinWormNew(thing->x,thing->y); 1029 pw->ai=&PlayerAI; 1030 pw->health=5000.; 1031 player_worm=pw; 1032 } else if(!StrCmp(thing->class_name,"PinWorm")) { 1033 pw=PinWormNew(thing->x,thing->y); 1034 pw->chat_bot=BotNewFromPersonalityFile("AssHole_Chat.HC"); 1035 QueIns(pw->chat_bot,&game.chat_log->bots); 1036 pw->ai=&AIPinWorm_AI; 1037 } else if(!StrCmp(thing->class_name,"Rocket")) { 1038 RocketNew(thing->x,thing->y); 1039 } else if(!StrCmp(thing->class_name,"Burger")) { 1040 BurgerNew(thing->x,thing->y); 1041 } 1042 } 1043 } 1044 CTask *lock_owner=NULL; 1045 U0 LockWorld() { 1046 if(lock_owner==Fs) 1047 return; 1048 while(LBts(&game.locked_flags,0)) 1049 Yield; 1050 lock_owner=Fs; 1051 } 1052 U0 UnlockWorld() { 1053 lock_owner=NULL; 1054 LBtr(&game.locked_flags,0); 1055 } 1056 U0 PinWormGame() { 1057 game.level=LevelNew; 1058 game.camera_height=0; 1059 game.chat_log=ChatNew; 1060 game.locked_flags=0; 1061 WinBorder; 1062 WinMax; 1063 DocMax; 1064 AutoComplete(0); 1065 Bts(&Fs->win_inhibit,WIf_FOCUS_TASK_MS_L_D); 1066 Bts(&Fs->win_inhibit,WIf_FOCUS_TASK_MS_R_D); 1067 CMathODE *ode=ODENew(0,1e-2,ODEF_HAS_MASSES); 1068 I64 x,y,m,move_x=100,move_y=100,z,inv[16]; 1069 I64 x2,y2,z2; 1070 F64 angle,force,dx,dy; 1071 CMass *to_add; 1072 I64 old_z=ms.pos.z; 1073 world->scrn->flags|=DCF_TRANSFORMATION; 1074 CGameThing *thing,*next; 1075 ode->acceleration_limit=500000.; 1076 ode->derive=&Derivative; 1077 QueIns(ode,Fs->last_ode); 1078 QueInit(&game.objects); 1079 QueInit(&game.particles); 1080 LoadLevel(game.level,"Test.DD"); 1081 LevelInitGrid(game.level); 1082 InitLevelThings; 1083 Fs->draw_it=&PinWormDrawIt; 1084 while(TRUE) { 1085 for(thing=game.objects.next;thing!=&game.objects;thing=next) { 1086 next=thing->qnext; 1087 if(thing->collide_line&&thing->flags&THINGF_HIT_LINE) { 1088 (*thing->collide_line)(thing); 1089 thing->flags&=~THINGF_HIT_LINE; 1090 } 1091 if(thing->collide_thing&&thing->flags&THINGF_HIT_THING) { 1092 (*thing->collide_thing)(thing,thing->hit_thing); 1093 thing->flags&=~THINGF_HIT_THING; 1094 } 1095 if(thing->ai) 1096 (*thing->ai)(thing); 1097 if(thing->animate) 1098 (*thing->animate)(thing); 1099 if(thing->flags&THINGF_EXPLODED) { 1100 force=200.; 1101 if(thing->ident==PW_ROCKET_IDENT) { 1102 force=thing(CRocket*)->explosion_force; 1103 ExplosionNew(thing->mass.x,thing->mass.y,thing(CRocket*)->who_shot_rocket,force); 1104 ThingDel(thing,FALSE); 1105 } 1106 } 1107 } 1108 delete_again:; 1109 for(thing=game.objects.next;thing!=&game.objects;thing=next) { 1110 next=thing->qnext; 1111 if(thing->flags&THINGF_TO_DELETE) { 1112 ThingDel(thing); 1113 goto delete_again; 1114 } 1115 } 1116 UpdateParticles; 1117 CheckPowerUps; 1118 game.camera_height+=(ms.pos.z-old_z)*5.; 1119 if(game.camera_height<0.) { 1120 game.camera_height=0; 1121 } 1122 1123 old_z=ms.pos.z; 1124 LockWorld; 1125 GCCollect; 1126 UnlockWorld; 1127 Refresh; 1128 } 1129 } 1130 //Uf("ExplosionNew"); 1131 PinWormGame; 1132