1 | /* |
2 | Copyright (C) 1998-2001 Andreas Zehender <az@azweb.de> |
3 | |
4 | it under the terms of the GNU General Public License as published by |
5 | the Free Software Foundation; either version 2 of the License, or |
6 | (at your option) any later version. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
16 | */ |
17 | |
18 | #include "ai.h" |
19 | |
20 | #include <math.h> |
21 | |
22 | #include <QVector> |
23 | |
24 | #include "mathroutines.h" |
25 | #include "options.h" |
26 | #include "structs.h" |
27 | |
28 | int Ai::calcFrameIncrement[Options::EnumAiDifficulty::COUNT] = {15,8,6,2}; |
29 | int Ai::calcPositionNumber[Options::EnumAiDifficulty::COUNT] = {10,15,20,60}; |
30 | int Ai::calcShotDirections[Options::EnumAiDifficulty::COUNT] = {4,7,10,12}; |
31 | int Ai::calcCollisions[Options::EnumAiDifficulty::COUNT] = {30,15,10,10}; |
32 | int Ai::calcNextShot[Options::EnumAiDifficulty::COUNT] = {300,200,90,60}; |
33 | |
34 | Ai::Ai(int pn,ShipSprite* s[2],QList<BulletSprite*>* b[2], |
35 | QList<MineSprite*>* m[2],SConfig *c) |
36 | { |
37 | int i; |
38 | |
39 | playerNumber=pn; |
40 | opponentNumber=(pn+1)%2; |
41 | cfg=c; |
42 | |
43 | for(i=0;i<2;i++) |
44 | { |
45 | ship[i]=s[i]; |
46 | bullets[i]=b[i]; |
47 | mines[i]=m[i]; |
48 | shipsNextPositions[i]=new QVector<AiSprite> |
49 | ((int)(calcPositionNumber[Options::aiDifficulty(playerNumber)]/cfg->gamespeed)); |
50 | aiMines[i]=new QVector<AiSprite>(cfg->maxMines); |
51 | mineNumber[i]=0; |
52 | } |
53 | } |
54 | |
55 | Ai::~Ai() |
56 | { |
57 | int i; |
58 | |
59 | for (i=0;i<2;i++) |
60 | { |
61 | delete shipsNextPositions[i]; |
62 | delete aiMines[i]; |
63 | } |
64 | |
65 | qDeleteAll(myShots); |
66 | myShots.clear(); |
67 | |
68 | qDeleteAll(objectsHitByShip); |
69 | objectsHitByShip.clear(); |
70 | |
71 | qDeleteAll(minesHitByShot); |
72 | minesHitByShot.clear(); |
73 | } |
74 | |
75 | void Ai::newRound() |
76 | { |
77 | accelerateFramesNumber=0; |
78 | rotateFramesNumber=0; |
79 | shoot=false; |
80 | score=1e10; |
81 | |
82 | rotation=RNONE; |
83 | acc=false; |
84 | bullet=false; |
85 | mine=false; |
86 | |
87 | borderTime=-1; |
88 | sunTime=-1; |
89 | |
90 | calculateCollisions=(int)(calcCollisions[Options::aiDifficulty(playerNumber)] |
91 | /cfg->gamespeed); |
92 | waitShot=(int) rint( random.getDouble() * |
93 | calcNextShot[Options::aiDifficulty(playerNumber)] |
94 | /cfg->gamespeed); |
95 | |
96 | qDeleteAll(myShots); |
97 | myShots.clear(); |
98 | qDeleteAll(objectsHitByShip); |
99 | objectsHitByShip.clear(); |
100 | qDeleteAll(minesHitByShot); |
101 | minesHitByShot.clear(); |
102 | } |
103 | |
104 | void Ai::think() |
105 | { |
106 | setSpriteFieldSize(); |
107 | |
108 | qDeleteAll(myShots); |
109 | myShots.clear(); |
110 | borderTime=-1; |
111 | sunTime=-1; |
112 | score--; |
113 | if(waitShot>0) |
114 | waitShot--; |
115 | |
116 | calculateNextPositions(); |
117 | if(Options::aiDifficulty(playerNumber)!=Options::EnumAiDifficulty::Trainee) |
118 | testForHits(); |
119 | if(waitShot<=0) |
120 | { |
121 | tryShots(); |
122 | shotScores(); |
123 | } |
124 | chooseAction(); |
125 | |
126 | |
127 | if(rotateFramesNumber<=0) |
128 | { |
129 | rotation=RNONE; |
130 | if(accelerateFramesNumber<=0) |
131 | { |
132 | acc=false; |
133 | if(shoot) |
134 | { |
135 | bullet=true; |
136 | shoot=false; |
137 | } |
138 | else |
139 | bullet=false; |
140 | score=1e10; |
141 | } |
142 | else |
143 | { |
144 | acc=true; |
145 | accelerateFramesNumber--; |
146 | } |
147 | } |
148 | else |
149 | rotateFramesNumber--; |
150 | |
151 | } |
152 | |
153 | |
154 | AiSprite Ai::nextPosition(AiSprite sp,double mult) |
155 | { |
156 | double abs_2,nx,ny,sq,eg; |
157 | if(!sp.sun) |
158 | { |
159 | abs_2=sp.x*sp.x+sp.y*sp.y; |
160 | if(abs_2<1) |
161 | abs_2=1; |
162 | sq=sqrt(abs_2); |
163 | nx=sp.x/sq; |
164 | ny=sp.y/sq; |
165 | eg=cfg->gravity*mult; |
166 | sp.dx-=eg*nx/abs_2; |
167 | sp.dy-=eg*ny/abs_2; |
168 | |
169 | sp.x+=sp.dx*mult; |
170 | sp.y+=sp.dy*mult; |
171 | |
172 | if(sp.x*sp.x+sp.y*sp.y<1600) |
173 | sp.sun=true; |
174 | else |
175 | { |
176 | //simple bounds actions |
177 | if(sp.x>sfwidth_2) |
178 | { |
179 | sp.x-=sfwidth; |
180 | sp.border=true; |
181 | } |
182 | else if(sp.x<-sfwidth_2) |
183 | { |
184 | sp.x+=sfwidth; |
185 | sp.border=true; |
186 | } |
187 | if(sp.y>sfheight_2) |
188 | { |
189 | sp.y-=sfheight; |
190 | sp.border=true; |
191 | } |
192 | else if(sp.y<-sfheight_2) |
193 | { |
194 | sp.y+=sfheight; |
195 | sp.border=true; |
196 | } |
197 | } |
198 | } |
199 | |
200 | return sp; |
201 | } |
202 | |
203 | void Ai::nextPositions(AiSprite sp,QVector<AiSprite> *a,int frames) |
204 | { |
205 | int i,num; |
206 | double fmult=cfg->gamespeed*frames; |
207 | |
208 | (*a)[0]=nextPosition(sp,cfg->gamespeed); |
209 | num=a->size(); |
210 | for(i=1;i<num;i++) |
211 | (*a)[i]=nextPosition((*a)[i-1],fmult); |
212 | } |
213 | |
214 | void Ai::calculateNextPositions() |
215 | { |
216 | unsigned int i; |
217 | int j; |
218 | |
219 | j=(int)(calcPositionNumber[Options::aiDifficulty(playerNumber)]/cfg->gamespeed); |
220 | |
221 | if(shipsNextPositions[0]->size() != j) |
222 | for(i=0;i<2;i++) |
223 | shipsNextPositions[i]->resize(j); |
224 | |
225 | for(i=0;i<2;i++) |
226 | nextPositions(ship[i]->toAiSprite(),shipsNextPositions[i], |
227 | calcFrameIncrement[Options::aiDifficulty(playerNumber)]); |
228 | |
229 | if(cfg->maxMines > aiMines[0]->size()) |
230 | for(i=0;i<2;i++) |
231 | aiMines[i]->resize(cfg->maxMines); |
232 | |
233 | for(i=0;i<2;i++) |
234 | { |
235 | for (j=0; j<mines[i]->size(); j++) |
236 | { |
237 | (*(aiMines[i]))[j]=mines[i]->value(j)->toAiSprite(); |
238 | } |
239 | mineNumber[i]=j; |
240 | } |
241 | } |
242 | |
243 | void Ai::tryShots() |
244 | { |
245 | AiSprite shot,me; |
246 | double rot,nr,nx,ny; |
247 | int i,f,frameIncrement,; |
248 | Hit hit; |
249 | Shot *goodShot; |
250 | |
251 | me=ship[playerNumber]->toAiSprite(); |
252 | rot=ship[playerNumber]->getRotation(); |
253 | |
254 | //Each 'frameIncrement' frames a shot is tried |
255 | frameIncrement=(int)((2*M_PI/calcShotDirections[Options::aiDifficulty(playerNumber)]) |
256 | /cfg->rotationSpeed); |
257 | if(frameIncrement==0) |
258 | frameIncrement=1; |
259 | //Number of frames needed to rotate 180 degrees |
260 | frameNum=(int)(M_PI/(frameIncrement*cfg->rotationSpeed)); |
261 | |
262 | //if too much bullets are on the playfield, no shot is tried |
263 | if(bullets[playerNumber]->count() < |
264 | (cfg->maxBullets+ship[playerNumber]->getBulletPowerups())) |
265 | { |
266 | for(f=0;f<=frameNum;f++) |
267 | { |
268 | if(f!=0) |
269 | for(i=0;i<frameIncrement;i++) |
270 | me=nextPosition(me,cfg->gamespeed); |
271 | else |
272 | me=nextPosition(me,cfg->gamespeed); |
273 | |
274 | if(!ship[playerNumber]->reloadsBullet(f*frameIncrement*cfg->gamespeed)) |
275 | { |
276 | for(i=0;i<2;i++) |
277 | { |
278 | if((f==0)&&(i==1)) |
279 | continue; |
280 | if(i==0) |
281 | nr=rot+frameIncrement*f*cfg->rotationSpeed; |
282 | else |
283 | nr=rot-frameIncrement*f*cfg->rotationSpeed; |
284 | |
285 | nx=cos(nr); |
286 | ny=sin(nr); |
287 | shot.x=me.x+nx*SHOTDIST; |
288 | shot.y=me.y+ny*SHOTDIST; |
289 | shot.dx=me.dx+nx*cfg->shotSpeed; |
290 | shot.dy=me.dy+ny*cfg->shotSpeed; |
291 | shot.sun=false; |
292 | shot.border=false; |
293 | |
294 | hit=firstObject(shot,f*frameIncrement, |
295 | calcFrameIncrement[Options::aiDifficulty(playerNumber)]); |
296 | if((hit.object!=HNOTHING) && |
297 | !((hit.object==HSHIP)&&(hit.playerNumber==playerNumber))) |
298 | { |
299 | goodShot=new Shot; |
300 | goodShot->hit=hit; |
301 | goodShot->rotation=(i==0?RLEFT:RRIGHT); |
302 | goodShot->rotationFrames=f*frameIncrement; |
303 | goodShot->score=1e10; |
304 | myShots.append(goodShot); |
305 | } |
306 | } |
307 | } |
308 | } |
309 | } |
310 | } |
311 | |
312 | Hit Ai::firstObject(AiSprite shot,int time,int frames) |
313 | { |
314 | int optime,i,num,rtime,basetime,t,m; |
315 | double dist,distx,disty,shiplastdist=0; |
316 | bool shipdistgreater=true,hitfound=false; |
317 | Hit hit={HNOTHING,0,0,0,1e10}; |
318 | |
319 | basetime=time/frames; |
320 | if((time%frames)>0) |
321 | basetime++; |
322 | rtime=basetime*frames-time; |
323 | optime=shipsNextPositions[0]->size(); |
324 | |
325 | num=optime-basetime; |
326 | if(num>0) |
327 | { |
328 | for(t=0;(t<num)&&(!hitfound)&&(!shot.sun);t++) |
329 | { |
330 | if(t==0) |
331 | shot=nextPosition(shot,cfg->gamespeed*rtime); |
332 | else |
333 | shot=nextPosition(shot,cfg->gamespeed*frames); |
334 | |
335 | //distance to other objects |
336 | for(i=0;i<2;i++) |
337 | { |
338 | distx=(*(shipsNextPositions[i]))[basetime].x-shot.x; |
339 | disty=(*(shipsNextPositions[i]))[basetime].y-shot.y; |
340 | dist=distx*distx+disty*disty; |
341 | //own ship |
342 | if(i==playerNumber) |
343 | { |
344 | if(dist<shiplastdist) |
345 | shipdistgreater=false; |
346 | if((!shipdistgreater)&&(dist<hit.distance)) |
347 | { |
348 | hit.object=HSHIP; |
349 | hit.objectNumber=0; |
350 | hit.playerNumber=i; |
351 | hit.hitTime=basetime*frames; |
352 | hit.distance=dist; |
353 | } |
354 | shiplastdist=dist; |
355 | } |
356 | //other ship |
357 | else if(dist<hit.distance) |
358 | { |
359 | hit.object=HSHIP; |
360 | hit.objectNumber=0; |
361 | hit.playerNumber=i; |
362 | hit.hitTime=basetime*frames; |
363 | hit.distance=dist; |
364 | } |
365 | |
366 | //mines |
367 | for(m=0;m<mineNumber[i];m++) |
368 | { |
369 | distx=(*(aiMines[i]))[m].x-shot.x; |
370 | disty=(*(aiMines[i]))[m].y-shot.y; |
371 | dist=distx*distx+disty*disty; |
372 | |
373 | if(dist<hit.distance) |
374 | { |
375 | hit.object=HMINE; |
376 | hit.playerNumber=i; |
377 | hit.objectNumber=m; |
378 | hit.hitTime=basetime*frames; |
379 | hit.distance=dist; |
380 | } |
381 | } |
382 | } |
383 | if(hit.distance<100) |
384 | hitfound=true; |
385 | basetime++; |
386 | } |
387 | } |
388 | |
389 | return hit; |
390 | } |
391 | |
392 | void Ai::testForHits() |
393 | { |
394 | AiSprite shot; |
395 | int i,j; |
396 | int m,p; |
397 | BulletSprite *bullet; |
398 | Hit *h; |
399 | Hit hit; |
400 | bool hitfound=false; |
401 | double distance,dx,dy; |
402 | int listsize; // used for caching QtList::size() |
403 | |
404 | if(calculateCollisions>0) |
405 | { |
406 | calculateCollisions--; |
407 | i=0; |
408 | listsize = objectsHitByShip.size(); |
409 | while(i<listsize) |
410 | { |
411 | h = objectsHitByShip[i]; |
412 | if(h->hitTime>0) |
413 | { |
414 | h->hitTime--; |
415 | i++; |
416 | } |
417 | else |
418 | { |
419 | objectsHitByShip.removeAt(i); |
420 | delete h; |
421 | listsize--; |
422 | } |
423 | } |
424 | i=0; |
425 | listsize = minesHitByShot.size(); |
426 | while(i<listsize) |
427 | { |
428 | h=minesHitByShot[i]; |
429 | if(h->hitTime>0) |
430 | { |
431 | h->hitTime--; |
432 | i++; |
433 | } |
434 | else |
435 | { |
436 | minesHitByShot.removeAt(i); |
437 | delete h; |
438 | listsize--; |
439 | } |
440 | } |
441 | } |
442 | else |
443 | { |
444 | qDeleteAll(objectsHitByShip); |
445 | objectsHitByShip.clear(); |
446 | qDeleteAll(minesHitByShot); |
447 | minesHitByShot.clear(); |
448 | for(i=0;i<2;i++) |
449 | { |
450 | listsize = bullets[i]->size(); |
451 | for (j=0; j<listsize; j++) |
452 | { |
453 | bullet=bullets[i]->value(j); |
454 | shot=bullet->toAiSprite(); |
455 | hit=firstObject(shot,0,calcFrameIncrement[Options::aiDifficulty(playerNumber)]); |
456 | if(hit.object==HMINE) |
457 | { |
458 | h=new Hit(hit); |
459 | minesHitByShot.append(h); |
460 | } |
461 | if((hit.object==HSHIP)&&(hit.playerNumber==playerNumber)) |
462 | { |
463 | h=new Hit(hit); |
464 | h->object=HSHOT; |
465 | objectsHitByShip.append(h); |
466 | } |
467 | } |
468 | } |
469 | |
470 | hit.object=HNOTHING; |
471 | hit.distance=400; |
472 | |
473 | for(i=0;(i<shipsNextPositions[0]->size()) && |
474 | !(*shipsNextPositions[playerNumber])[i].sun;i++) |
475 | { |
476 | if((borderTime<0) && (*shipsNextPositions[playerNumber])[i].border) |
477 | borderTime=i*calcFrameIncrement[Options::aiDifficulty(playerNumber)]; |
478 | |
479 | dx=(*shipsNextPositions[playerNumber])[i].x; |
480 | dy=(*shipsNextPositions[playerNumber])[i].y; |
481 | distance=dx*dx+dy*dy; |
482 | if((distance<3025)&&(sunTime<0)) |
483 | sunTime=i*calcFrameIncrement[Options::aiDifficulty(playerNumber)]; |
484 | |
485 | if(!hitfound) |
486 | for(p=0;p<2;p++) |
487 | for(m=0;m<mineNumber[p];m++) |
488 | { |
489 | dx=(*shipsNextPositions[playerNumber])[i].x-(*aiMines[p])[m].x; |
490 | dy=(*shipsNextPositions[playerNumber])[i].y-(*aiMines[p])[m].y; |
491 | distance=dx*dx+dy*dy; |
492 | if(hit.distance>distance) |
493 | { |
494 | hit.object=HMINE; |
495 | hit.playerNumber=p; |
496 | hit.objectNumber=m; |
497 | hit.hitTime=i*calcFrameIncrement[Options::aiDifficulty(playerNumber)]; |
498 | hit.distance=distance; |
499 | if(distance<100) |
500 | hitfound=true; |
501 | } |
502 | } |
503 | } |
504 | if(hit.object!=HNOTHING) |
505 | { |
506 | h=new Hit(hit); |
507 | objectsHitByShip.append(h); |
508 | } |
509 | calculateCollisions=(int)(calcCollisions[Options::aiDifficulty(playerNumber)]/cfg->gamespeed); |
510 | } |
511 | } |
512 | |
513 | void Ai::shotScores() |
514 | { |
515 | Shot *s; |
516 | Hit *h,*mh; |
517 | bool found,foundmh; |
518 | double dist,dx,dy,fuel; |
519 | int i,j,k; |
520 | int listsize,listsize2; // used for caching QtList::size() |
521 | |
522 | |
523 | dx=(*shipsNextPositions[playerNumber])[0].x-(*shipsNextPositions[opponentNumber])[0].x; |
524 | dy=(*shipsNextPositions[playerNumber])[0].y-(*shipsNextPositions[opponentNumber])[0].y; |
525 | dist=dx*dx+dy*dy; |
526 | |
527 | listsize = myShots.size(); |
528 | for (j=0; j<listsize; j++) |
529 | { |
530 | s = myShots[j]; |
531 | fuel=(100-(ship[playerNumber]->getEnergy()-cfg->shotEnergyNeed)); |
532 | s->score=fuel*fuel/10 + s->hit.distance+s->hit.hitTime; |
533 | if(dist > (75*75)) |
534 | s->score+=waitShot*8; |
535 | else |
536 | s->score+=waitShot*4; |
537 | |
538 | if(s->hit.object==HMINE) |
539 | { |
540 | found=false; |
541 | for (i=0; i<objectsHitByShip.size() && !found; i++) |
542 | { |
543 | h=objectsHitByShip[i]; |
544 | if((h->object==HMINE)&&(h->playerNumber==s->hit.playerNumber) |
545 | &&(h->objectNumber==s->hit.objectNumber)) |
546 | //ship will hit a mine that will be hitten by the shot |
547 | { |
548 | found=true; |
549 | //ship hits earlier than shot |
550 | if(h->hitTime<s->hit.hitTime) |
551 | s->score+=1000; |
552 | else |
553 | { |
554 | foundmh=false; |
555 | listsize2 = minesHitByShot.size(); |
556 | for (k=0; k<listsize2 && !foundmh; k++) |
557 | { |
558 | mh = minesHitByShot[k]; |
559 | if((mh->playerNumber==s->hit.playerNumber) |
560 | &&(mh->objectNumber==s->hit.objectNumber)) |
561 | //another shot will hit the mine |
562 | { |
563 | /* FIXME: check (and understand) this function |
564 | original version: "found" in for-clause, bot was never true |
565 | foundmh set to false before but never changed */ |
566 | foundmh = true; |
567 | if(mh->hitTime<s->hit.hitTime) |
568 | s->score+=500; |
569 | else |
570 | s->score-=300; |
571 | } |
572 | } |
573 | } |
574 | } |
575 | } |
576 | if(!found) |
577 | s->score+=1000; |
578 | } |
579 | } |
580 | } |
581 | |
582 | void Ai::chooseAction() |
583 | { |
584 | double bestScore=1e10; |
585 | Shot *bestShot=NULL,*s; |
586 | AiSprite actualpos; |
587 | double posangle,movephi,phiright,phileft,torotate=0,velangle; |
588 | int framesleft,framesright; |
589 | bool rotateAndAccelerate=false; |
590 | Hit *nextHit=0; |
591 | int shotHitTime; |
592 | int i; |
593 | |
594 | shotHitTime=1000000; |
595 | nextHit=0; |
596 | /* for(h=objectsHitByShip.first();h;h=objectsHitByShip.next()) |
597 | if(h->object==HSHOT) |
598 | if(h->hitTime<shotHitTime) |
599 | { |
600 | nextHit=h; |
601 | shotHitTime=h->hitTime; |
602 | }*/ |
603 | if((borderTime>0) || (sunTime>0) || (nextHit)) |
604 | { |
605 | actualpos=ship[playerNumber]->toAiSprite(); |
606 | posangle=rectToAngle(actualpos.x,actualpos.y); |
607 | |
608 | movephi=rectToAngle((*shipsNextPositions[playerNumber])[0].x, |
609 | (*shipsNextPositions[playerNumber])[0].y) - posangle; |
610 | |
611 | phileft=movephi+cfg->rotationSpeed; |
612 | phiright=movephi-cfg->rotationSpeed; |
613 | |
614 | if((borderTime>0)&& !((sunTime>0)&&(sunTime<borderTime))) |
615 | { |
616 | bestScore=borderTime/cfg->gamespeed; |
617 | if(score>bestScore) |
618 | { |
619 | velangle=rectToAngle(actualpos.dx,actualpos.dy); |
620 | if(fabs(difference(posangle+3*M_PI/4,velangle)) |
621 | < fabs(difference(posangle-3*M_PI/4,velangle))) |
622 | torotate=posangle-3*M_PI/4-ship[playerNumber]->getRotation(); |
623 | else |
624 | torotate=posangle+3*M_PI/4-ship[playerNumber]->getRotation(); |
625 | rotateAndAccelerate=true; |
626 | score=bestScore; |
627 | accelerateFramesNumber=(int)(8/cfg->gamespeed); |
628 | } |
629 | } |
630 | else if(sunTime>0) |
631 | { |
632 | bestScore=sunTime/(cfg->gamespeed*10) |
633 | +(actualpos.x*actualpos.x+actualpos.y*actualpos.y)/5000; |
634 | if(score>bestScore) |
635 | { |
636 | velangle=rectToAngle(actualpos.dx,actualpos.dy); |
637 | if(fabs(difference(posangle+2*M_PI/5,velangle)) |
638 | < fabs(difference(posangle-2*M_PI/5,velangle))) |
639 | torotate=posangle+2*M_PI/5-ship[playerNumber]->getRotation(); |
640 | else |
641 | torotate=posangle-2*M_PI/5-ship[playerNumber]->getRotation(); |
642 | rotateAndAccelerate=true; |
643 | score=bestScore; |
644 | accelerateFramesNumber=(int)(8/cfg->gamespeed); |
645 | } |
646 | } |
647 | else |
648 | { |
649 | /* bestScore=abs(nextHit->hitTime-90)*4/cfg->gamespeed + nextHit->distance*2 |
650 | + (100-(ship[playerNumber]->getEnergy()-cfg->shotEnergyNeed))*4; |
651 | if((score>bestScore)&&(bestScore<400)) |
652 | { |
653 | velangle=rectToAngle(actualpos.dx,actualpos.dy); |
654 | if(fabs(difference(posangle+2*M_PI/5,velangle)) |
655 | < fabs(difference(posangle-2*M_PI/5,velangle))) |
656 | torotate=posangle+2*M_PI/5-ship[playerNumber]->getRotation(); |
657 | else |
658 | torotate=posangle-2*M_PI/5-ship[playerNumber]->getRotation(); |
659 | rotateAndAccelerate=true; |
660 | score=bestScore; |
661 | accelerateFramesNumber=(int)(4/cfg->gamespeed); |
662 | }*/ |
663 | } |
664 | |
665 | if(rotateAndAccelerate) |
666 | { |
667 | if(phileft<0) |
668 | framesleft=1000; |
669 | else |
670 | { |
671 | while(torotate<0) |
672 | torotate+=2*M_PI; |
673 | while(torotate>=2*M_PI) |
674 | torotate-=2*M_PI; |
675 | framesleft=(int)(torotate/phileft+0.5); |
676 | } |
677 | |
678 | if(phiright>0) |
679 | framesright=1000; |
680 | else |
681 | { |
682 | while(torotate>0) |
683 | torotate-=2*M_PI; |
684 | while(torotate<=-2*M_PI) |
685 | torotate+=2*M_PI; |
686 | framesright=(int)(torotate/phiright+0.5); |
687 | } |
688 | |
689 | if(framesright<framesleft) |
690 | { |
691 | rotation=RRIGHT; |
692 | rotateFramesNumber=framesright; |
693 | } |
694 | else |
695 | { |
696 | rotation=RLEFT; |
697 | rotateFramesNumber=framesleft; |
698 | } |
699 | shoot=false; |
700 | |
701 | } |
702 | } |
703 | else |
704 | { |
705 | int listsize; // used for caching QtList::size() |
706 | bestShot=0; |
707 | listsize = myShots.size(); |
708 | for (i=0; i<listsize; i++) |
709 | { |
710 | s=myShots[i]; |
711 | if(s->score<bestScore) |
712 | { |
713 | bestScore=s->score; |
714 | bestShot=s; |
715 | } |
716 | } |
717 | if(bestShot) |
718 | { |
719 | if((bestScore<score)&&(bestScore<400)) |
720 | { |
721 | rotation=bestShot->rotation; |
722 | rotateFramesNumber=bestShot->rotationFrames; |
723 | accelerateFramesNumber=0; |
724 | shoot=true; |
725 | score=bestScore; |
726 | calculateCollisions = 0; |
727 | waitShot=(int) rint( random.getDouble() * |
728 | calcNextShot[Options::aiDifficulty(playerNumber)] |
729 | /cfg->gamespeed); |
730 | } |
731 | } |
732 | } |
733 | } |
734 | |
735 | void Ai::setSpriteFieldSize() |
736 | { |
737 | sfwidth=(double)(ship[playerNumber]->spriteFieldWidth()); |
738 | sfheight=(double)(ship[playerNumber]->spriteFieldHeight()); |
739 | sfwidth_2=sfwidth/2.0; |
740 | sfheight_2=sfheight/2.0; |
741 | } |
742 | |
743 | |