001 U0 ParsePersonality(U8 *file,U8 *who="AIPinWorm") {
002   U8 *f=FileRead(file);
003   CCmpCtrl *cc=CmpCtrlNew(f,,file);
004   Lex(cc);
005   CDoc *ai_fun_doc=DocNew;
006   DocPrint(ai_fun_doc,"U0 %s_AI(CGameThing *self) {\n",who);
007   I64 depth=0;
008   I64 s,e;
009   while(cc->token) {
010     if(cc->token==TK_IDENT) {
011       if(!StrCmp(cc->cur_str,"switch")) {
012         if(Lex(cc)!='(')
013           LexExcept(cc,"Expected a '(' at ");
014         Lex(cc);
015         if(cc->token==TK_IDENT) {
016           if(!cc->hash_entry)
017             goto expected_cond;
018 
019           if(!(cc->hash_entry->type&HTT_FUN))
020             goto expected_cond;
021           DocPrint(ai_fun_doc,"switch(.001+%s(self",cc->cur_str);
022         } else {
023 expected_cond:;
024           LexExcept(cc,"Expected condition function at ");
025         }
026         Lex(cc);
027 arg:;
028         if(cc->token!=')') {
029           if(cc->token==TK_STR) {
030             DocPrint(ai_fun_doc,",\"%Q\"",cc->cur_str);
031             Lex(cc);
032             goto arg;
033           } else {
034             DocPrint(ai_fun_doc,",%d",LexExpressionI64(cc));
035             goto arg;
036           }
037         }
038         if(Lex(cc)!='{')
039           LexExcept(cc,"Expected a '{' at ");
040         Lex(cc);
041         DocPrint(ai_fun_doc,")) {\n$ID,2$");
042         ++depth;
043       } else if(!StrCmp(cc->cur_str,"default")) {
044         if(':'!=Lex(cc))
045           LexExcept(cc,"Expected a ':' ");
046         Lex(cc);
047         DocPrint(ai_fun_doc,"break;default:\n");
048       } else if(!StrCmp(cc->cur_str,"case")) {
049         Lex(cc);
050         if(cc->token!=TK_I64)
051           LexExcept(cc,"Expected a percentage at ");
052         s=cc->cur_i64;
053         e=cc->cur_i64;
054         Lex(cc);
055         if(cc->token==TK_ELLIPSIS) {
056           if(Lex(cc)!=TK_I64);
057             LexExcept(cc,"Expected a percentage at ");
058            e=cc->cur_i64;
059            Lex(cc);
060         }
061         if(cc->token!=':')
062           LexExcept(cc,"Expected a ':' at ");
063         DocPrint(ai_fun_doc,"break;case %d ... %d:\n",s,e);
064         Lex(cc);
065       } else if(cc->hash_entry) {
066         if(!(cc->hash_entry->type&HTT_FUN)) {
067           if(cc->hash_entry->type&HTT_KEYWORD)
068             if(!StrCmp(cc->cur_str,"goto")) {
069               Lex(cc);
070               if(cc->token!=TK_IDENT)
071                 LexExcept(cc,"Expected label at ");
072               DocPrint(ai_fun_doc,"goto %s;\n",cc->cur_str);
073               Lex(cc);
074               goto next;
075             }
076           goto label;
077         }
078         DocPrint(ai_fun_doc,"%s(self",cc->cur_str);
079         Lex(cc);
080         while(cc->token!=';') {
081           if(cc->token==TK_STR) {
082             DocPrint(ai_fun_doc,",\"%Q\"",cc->cur_str);
083             Lex(cc);
084           } else {
085             DocPrint(ai_fun_doc,",%d",LexExpressionI64(cc));
086           }
087         }
088         DocPrint(ai_fun_doc,");\n");
089         Lex(cc);
090       } else {
091 label:;
092         DocPrint(ai_fun_doc,"%s:\n",cc->cur_str);
093         if(Lex(cc)!=':')
094           LexExcept(cc,"Expected ':' at ");
095         Lex(cc);
096       }
097     } else if(cc->token=='}') {
098       if(!depth--)
099         LexExcept(cc,"Unexpected '}' at ");
100       DocPrint(ai_fun_doc,"$ID,-2$}\n");
101       Lex(cc);
102     } else if(cc->token==';')
103         Lex(cc);
104     else
105         LexExcept(cc,"Unexpected token at ");
106 next:;
107   }
108 done:;
109   DocPrint(ai_fun_doc,"}\n");
110   DocTop(ai_fun_doc);
111   DocInsDoc(DocPut,ai_fun_doc);
112   DocTop(ai_fun_doc);
113   ExeDoc(ai_fun_doc);
114   DocInsDoc(DocPut,ai_fun_doc);
115   DocDel(ai_fun_doc);
116   CmpCtrlDel(cc);
117 }
118 class CAIPinWorm:CPinWorm {
119   F64 next_path_find_tS;
120 };
121 
122 CPinWorm *SeekThing(CAIPinWorm *worm,U64 ident=PW_HEAD_IDENT) {
123   CAIPinWorm *head=&game.objects,*cur,*best=NULL;
124   CD3 ass;
125   F64 best_d=I16_MAX,d;
126   for(cur=head->qnext;cur!=head;cur=cur->qnext) {
127     if(cur->ident==ident&&worm!=cur) {
128       D3Sub(&ass,&cur->mass.x,&worm->mass.x);
129       if((d=D3Norm(&ass))<best_d) {
130         best_d=d;
131         best=cur;
132       }
133     }
134 
135   }
136   return best;
137 }
138 
139 
140 //
141 // Conditions
142 //
143 F64 BurgerAround(CAIPinWorm *worm,F64 rad=300.) {
144   CI64Set *set=GetThingsOfTypeInRadius(worm->mass.x,worm->mass.y,rad,PW_BURGER_IDENT);
145   F64 per=set->cnt;
146   I64SetDel(set);
147   return per;
148 }
149 F64 EnemyAround(CAIPinWorm *worm,F64 rad=300.) {
150   CI64Set *set=GetThingsOfTypeInRadius(worm->mass.x,worm->mass.y,rad,PW_HEAD_IDENT);
151   F64 per=Max(set->cnt-1,0.); //TODO count enemy
152   I64SetDel(set);
153   return per;
154 }
155 F64 RocketAround(CAIPinWorm *worm,F64 rad=300.) {
156   CI64Set *set=GetThingsOfTypeInRadius(worm->mass.x,worm->mass.y,rad,PW_ROCKET_IDENT);
157   F64 per=0.;
158   I64 i;
159   CRocket *opp;
160   for(i=0;i!=set->cnt;++i) {
161     opp=set->body[i];
162     if(opp->who_shot_rocket!=worm)
163       ++per;
164   }
165   I64SetDel(set);
166   return per;
167 }
168 F64 Health(CAIPinWorm *worm) {
169   return Max(worm->health,0.);
170 }
171 F64 CanSeeEnemy(CAIPinWorm *worm) {
172   CPinWorm *other=SeekThing(worm,PW_HEAD_IDENT);
173   if(!other) return other;
174   return CanSeeThing(worm,other);  
175   
176 } 
177 //
178 //  Actions
179 //
180 
181 
182 //May be a burger or a pinworm
183 Bool MoveTowards(CAIPinWorm *worm,CGameThing *t) {
184   F64 dx,dy,d,angle;
185   if(worm->next_path_find_tS>Game_tS) {
186     if(CanSeeThing(worm,t)) {
187       if(worm->path_finder_data)
188         I64SetDel(worm->path_finder_data);
189       worm->path_finder_data=NULL;
190       angle=worm->angle=Arg(dx=t->mass.x-worm->mass.x,dy=t->mass.y-worm->mass.y);
191       d=Sqrt(dx*dx+dy*dy);
192       worm->dx=Min(d,worm->stats.max_speed)*Cos(angle);
193       worm->dy=Min(d,worm->stats.max_speed)*Sin(angle);
194       return TRUE;
195     }
196     return FALSE;
197   }
198   worm->next_path_find_tS=Game_tS+1.5;
199   if(worm->path_finder_data)
200     I64SetDel(worm->path_finder_data);
201   worm->path_finder_data=PathFind(game.level,worm->mass.x,worm->mass.y,t->mass.x,t->mass.y);
202   return TRUE;
203 }
204 Bool Attack(CAIPinWorm *worm) {
205   CPinWorm *other=SeekThing(worm);
206   if(other) {
207     worm->angle=Arg(other->mass.x-worm->mass.x,other->mass.y-worm->mass.y);
208     PinWormFireRocket(worm);
209     return TRUE; 
210   }
211   return FALSE;
212 }
213 Bool Die(CAIPinWorm *worm) {
214   PinWormDie(worm);
215   return TRUE;
216 }
217 Bool Chase(CAIPinWorm *worm) {
218   CPinWorm *other=SeekThing(worm);
219   if(!other) return FALSE;
220   MoveTowards(worm,other);
221   return TRUE;
222 }
223 Bool Say(CAIPinWorm *worm,...) {
224   U8 *str;
225   if(!argc) return FALSE;
226   str=argv[RandU64%argc]; 
227   if(worm->chat_bot) {
228      ChatAdd(game.chat_log,str,worm->chat_bot);
229   }
230 }
231 Bool ChaseBurger(CAIPinWorm *worm) {
232   Bool has=FALSE;
233   CI64Set *burgers=GetThingsOfTypeInRadius(worm->mass.x,worm->mass.y,350.,PW_BURGER_IDENT);
234   CBurger *burger,*best=NULL;
235   CD3 ass;
236   F64 best_d=I16_MAX,d;
237   I64 i;
238   if(burgers->cnt) {
239      for(i=0;i!=burgers->cnt;++i) {
240       burger=burgers->body[i];
241       D3Sub(&ass,&burger->mass.x,&worm->mass.x);
242       if((d=D3Norm(&ass))<best_d) {
243         best=burger;
244       }
245     }
246     if(best) {
247       MoveTowards(worm,best);
248       has=TRUE;
249     }
250   }
251   I64SetDel(burgers);
252   return has;
253 }
254 Bool Evade(CAIPinWorm *worm) {
255   F64 x,y;
256   I64 i,i2;
257   F64 rdx,rdy;
258   F64 angle,dot;
259   Bool evaded=FALSE;
260   Bool close=FALSE;
261   CI64Set *rockets=GetThingsOfTypeInRadius(x=worm->mass.x,y=worm->mass.y,350.,PW_ROCKET_IDENT);
262   CRocket *r;
263   for(i=0;i!=rockets->cnt;++i) {
264     r=rockets->body[i];
265     if(r->who_shot_rocket!=worm) {
266       rdx=r->mass.DxDt;
267       rdy=r->mass.DyDt;
268 //Check if missile is oppsite direction of worm(Dot Product 21).
269       dot=rdx*worm->mass.DxDt+rdy*worm->mass.DyDt;
270       if(dot>0.) {
271         close=Sqrt(Sqr(worm->mass.x-r->mass.x)+Sqr(worm->mass.y-r->mass.y))<160.;
272         angle=Arg(rdx,rdy);
273 //Try to go opposite side of rocket
274         if(rdx*(worm->mass.y-r->mass.y)>rdy*(worm->mass.x-r->mass.x)) {
275           if(close)
276             angle+=pi/2; //to side
277           else
278             angle+=pi/4;
279 
280         } else {
281           if(close)
282             angle-=pi/2; //to side
283           else
284             angle-=pi/4;
285         }
286 
287         worm->dx=Cos(angle)*worm->stats.max_speed;
288         worm->dy=Sin(angle)*worm->stats.max_speed;
289         worm->angle=angle;
290         evaded=TRUE;
291       }
292     }
293   }
294   I64SetDel(rockets);
295   return evaded;
296 }
297 U0 AIPinWorm_AI(CAIPinWorm *worm) {
298   //Weight out outcomes or somthin
299   F64 now=Game_tS;
300   F64 last_attack=worm->last_rocket_tS;
301   F64 next_attack=last_attack+1./worm->stats.max_rockets;
302   ChaseBurger(worm);
303   if(now>next_attack)
304     Attack(worm);
305   if(!Evade(worm)) {
306     if(worm->path_finder_data&&worm->path_finder_data->cnt) {
307        PathFinderGo(game.level,worm->path_finder_data,10.,50.,&worm->mass.x,&worm->dx);
308        PinWormUnStuck(worm);
309     }
310   }
311   worm->obj->rot=worm->angle;
312 }
313 ParsePersonality("AssHole_AI.HC","AIPinWorm");