3 static bool tryexit(coord look, int ienm, int loccom, bool irun)
4 /* a bad guy attempts to bug out */
9 iq.x = game.quadrant.x+(look.x+(QUADSIZE-1))/QUADSIZE - 1;
10 iq.y = game.quadrant.y+(look.y+(QUADSIZE-1))/QUADSIZE - 1;
11 if (!VALID_QUADRANT(iq.x,iq.y) ||
12 game.state.galaxy[iq.x][iq.y].supernova ||
13 game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1)
14 return false; /* no can do -- neg energy, supernovae, or >MAXKLQUAD-1 Klingons */
16 return false; /* Romulans cannot escape! */
18 /* avoid intruding on another commander's territory */
21 if (same(game.state.kcmdr[n],iq))
23 /* refuse to leave if currently attacking starbase */
24 if (same(game.battle, game.quadrant))
27 /* don't leave if over 1000 units of energy */
28 if (game.kpower[loccom] > 1000.0)
31 // print escape message and move out of quadrant.
32 // We know this if either short or long range sensors are working
33 if (!damaged(DSRSENS) || !damaged(DLRSENS) ||
34 game.condition == docked) {
35 crmena(true, ienm, sector, game.ks[loccom]);
36 prout(_(" escapes to %s (and regains strength)."),
37 cramlc(quadrant, iq));
39 /* handle local matters related to escape */
40 game.quad[game.ks[loccom].x][game.ks[loccom].y] = IHDOT;
41 game.ks[loccom] = game.ks[game.nenhere];
42 game.kavgd[loccom] = game.kavgd[game.nenhere];
43 game.kpower[loccom] = game.kpower[game.nenhere];
44 game.kdist[loccom] = game.kdist[game.nenhere];
47 if (game.condition != docked)
49 /* Handle global matters related to escape */
50 game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons--;
51 game.state.galaxy[iq.x][iq.y].klingons++;
57 schedule(FSCMOVE, 0.2777);
63 if (same(game.state.kcmdr[n], game.quadrant)) {
64 game.state.kcmdr[n]=iq;
70 return true; /* success */
73 /*************************************************************************
74 The bad-guy movement algorithm:
76 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
77 If both are operating full strength, force is 1000. If both are damaged,
78 force is -1000. Having shields down subtracts an additional 1000.
80 2. Enemy has forces equal to the energy of the attacker plus
81 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
82 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
84 Attacker Initial energy levels (nominal):
85 Klingon Romulan Commander Super-Commander
88 Good 450 800 1300 1750
89 Expert 475 850 1350 1875
90 Emeritus 500 900 1400 2000
91 VARIANCE 75 200 200 200
93 Enemy vessels only move prior to their attack. In Novice - Good games
94 only commanders move. In Expert games, all enemy vessels move if there
95 is a commander present. In Emeritus games all enemy vessels move.
97 3. If Enterprise is not docked, an agressive action is taken if enemy
98 forces are 1000 greater than Enterprise.
100 Agressive action on average cuts the distance between the ship and
101 the enemy to 1/4 the original.
103 4. At lower energy advantage, movement units are proportional to the
104 advantage with a 650 advantage being to hold ground, 800 to move forward
105 1, 950 for two, 150 for back 4, etc. Variance of 100.
107 If docked, is reduced by roughly 1.75*game.skill, generally forcing a
108 retreat, especially at high skill levels.
110 5. Motion is limited to skill level, except for SC hi-tailing it out.
111 **************************************************************************/
113 static void movebaddy(coord com, int loccom, feature ienm)
114 /* tactical movement for the bad guys */
116 int motion, mdist, nsteps, mx, my, ll;
119 bool success, irun = false;
121 /* This should probably be just game.comhere + game.ishere */
122 int nbaddys = game.skill >= SKILL_EXPERT ?
123 (int)((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0):
124 (game.comhere + game.ishere);
125 double dist1, forces;
127 dist1 = game.kdist[loccom];
128 mdist = dist1 + 0.5; /* Nearest integer distance */
130 /* If SC, check with spy to see if should hi-tail it */
132 (game.kpower[loccom] <= 500.0 || (game.condition==docked && !damaged(DPHOTON)))) {
137 /* decide whether to advance, retreat, or hold position */
138 forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1);
140 forces += 1000; /* Good for enemy if shield is down! */
141 if (!damaged(DPHASER) || !damaged(DPHOTON)) {
142 if (damaged(DPHASER)) /* phasers damaged */
145 forces -= 0.2*(game.energy - 2500.0);
146 if (damaged(DPHOTON)) /* photon torpedoes damaged */
149 forces -= 50.0*game.torps;
152 /* phasers and photon tubes both out! */
156 if (forces <= 1000.0 && game.condition != docked) /* Typical situation */
157 motion = ((forces+200.0*Rand())/150.0) - 5.0;
159 if (forces > 1000.0) /* Very strong -- move in for kill */
160 motion = (1.0-square(Rand()))*dist1 + 1.0;
161 if (game.condition==docked && (game.options & OPTION_BASE)) /* protected by base -- back off ! */
162 motion -= game.skill*(2.0-square(Rand()));
165 proutn("=== MOTION = %d, FORCES = %1.2f, ", motion, forces);
166 /* don't move if no motion */
169 /* Limit motion according to skill */
170 if (abs(motion) > game.skill)
171 motion = (motion < 0) ? -game.skill : game.skill;
173 /* calculate preferred number of steps */
174 nsteps = motion < 0 ? -motion : motion;
175 if (motion > 0 && nsteps > mdist)
176 nsteps = mdist; /* don't overshoot */
177 if (nsteps > QUADSIZE)
178 nsteps = QUADSIZE; /* This shouldn't be necessary */
180 nsteps = 1; /* This shouldn't be necessary */
182 proutn("NSTEPS = %d:", nsteps);
184 /* Compute preferred values of delta X and Y */
185 mx = game.sector.x - com.x;
186 my = game.sector.y - com.y;
187 if (2.0 * abs(mx) < abs(my))
189 if (2.0 * abs(my) < abs(game.sector.x-com.x))
192 mx = mx*motion < 0 ? -1 : 1;
194 my = my*motion < 0 ? -1 : 1;
197 for (ll = 0; ll < nsteps; ll++) {
200 /* Check if preferred position available */
201 look.x = next.x + mx;
202 look.y = next.y + my;
203 krawlx = mx < 0 ? 1 : -1;
204 krawly = my < 0 ? 1 : -1;
206 attempts = 0; /* Settle mysterious hang problem */
207 while (attempts++ < 20 && !success) {
208 if (look.x < 1 || look.x > QUADSIZE) {
209 if (motion < 0 && tryexit(look, ienm, loccom, irun))
211 if (krawlx == mx || my == 0)
213 look.x = next.x + krawlx;
216 else if (look.y < 1 || look.y > QUADSIZE) {
217 if (motion < 0 && tryexit(look, ienm, loccom, irun))
219 if (krawly == my || mx == 0)
221 look.y = next.y + krawly;
224 else if ((game.options & OPTION_RAMMING) && game.quad[look.x][look.y] != IHDOT) {
225 /* See if we should ram ship */
226 if (game.quad[look.x][look.y] == game.ship &&
227 (ienm == IHC || ienm == IHS)) {
228 ram(true, ienm, com);
231 if (krawlx != mx && my != 0) {
232 look.x = next.x + krawlx;
235 else if (krawly != my && mx != 0) {
236 look.y = next.y + krawly;
240 break; /* we have failed */
248 proutn(cramlc(neither, next));
251 break; /* done early */
256 /* Put commander in place within same quadrant */
257 game.quad[com.x][com.y] = IHDOT;
258 game.quad[next.x][next.y] = ienm;
259 if (!same(next, com)) {
261 game.ks[loccom] = next;
262 game.kdist[loccom] = game.kavgd[loccom] = distance(game.sector, next);
263 if (!damaged(DSRSENS) || game.condition == docked) {
266 proutn(_(" from %s"), cramlc(2, com));
267 if (game.kdist[loccom] < dist1)
268 proutn(_(" advances to "));
270 proutn(_(" retreats to "));
271 prout(cramlc(sector, next));
276 void moveklings(void)
277 /* Klingon tactical movement */
285 // Figure out which Klingon is the commander (or Supercommander)
288 for_local_enemies(i) {
290 if (game.quad[w.x][w.y] == IHC) {
291 movebaddy(w, i, IHC);
296 for_local_enemies(i) {
298 if (game.quad[w.x][w.y] == IHS) {
299 movebaddy(w, i, IHS);
303 // if skill level is high, move other Klingons and Romulans too!
304 // Move these last so they can base their actions on what the
306 if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY))
307 for_local_enemies(i) {
309 if (game.quad[w.x][w.y] == IHK || game.quad[w.x][w.y] == IHR)
310 movebaddy(w, i, game.quad[w.x][w.y]);
316 static bool movescom(coord iq, bool avoid)
317 /* commander movement helper */
321 if (same(iq, game.quadrant) || !VALID_QUADRANT(iq.x, iq.y) ||
322 game.state.galaxy[iq.x][iq.y].supernova ||
323 game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1)
326 /* Avoid quadrants with bases if we want to avoid Enterprise */
328 if (same(game.state.baseq[i], iq))
331 if (game.justin && !game.iscate)
334 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons--;
335 game.state.kscmdr = iq;
336 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons++;
338 /* SC has scooted, Remove him from current quadrant */
342 game.ientesc = false;
345 if (game.quad[game.ks[i].x][game.ks[i].y] == IHS)
347 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT;
348 game.ks[i] = game.ks[game.nenhere];
349 game.kdist[i] = game.kdist[game.nenhere];
350 game.kavgd[i] = game.kavgd[game.nenhere];
351 game.kpower[i] = game.kpower[game.nenhere];
354 if (game.condition!=docked)
358 /* check for a helpful planet */
359 for (i = 0; i < game.inplan; i++) {
360 if (same(game.state.planets[i].w, game.state.kscmdr) &&
361 game.state.planets[i].crystals == present) {
362 /* destroy the planet */
363 DESTROY(&game.state.planets[i]);
364 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET;
365 if (!damaged(DRADIO) || game.condition == docked) {
367 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
368 proutn(_(" a planet in "));
369 proutn(cramlc(quadrant, game.state.kscmdr));
370 prout(_(" has been destroyed"));
371 prout(_(" by the Super-commander.\""));
376 return false; /* looks good! */
379 void supercommander(void)
380 /* move the Super Commander */
382 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
384 int basetbl[BASEMAX+1];
385 double bdist[BASEMAX+1];
389 prout("== SUPERCOMMANDER");
391 /* Decide on being active or passive */
392 avoid = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
393 (game.state.date-game.indate) < 3.0);
394 if (!game.iscate && avoid) {
395 /* compute move away from Enterprise */
396 ideltax = game.state.kscmdr.x-game.quadrant.x;
397 ideltay = game.state.kscmdr.y-game.quadrant.y;
398 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
399 /* circulate in space */
400 ideltax = game.state.kscmdr.y-game.quadrant.y;
401 ideltay = game.quadrant.x-game.state.kscmdr.x;
405 /* compute distances to starbases */
406 if (game.state.rembase <= 0) {
407 /* nothing left to do */
411 sc = game.state.kscmdr;
414 bdist[i] = distance(game.state.baseq[i], sc);
416 if (game.state.rembase > 1) {
417 /* sort into nearest first order */
421 for (i=1; i < game.state.rembase-1; i++) {
422 if (bdist[i] > bdist[i+1]) {
425 bdist[i] = bdist[i+1];
427 basetbl[i] = basetbl[i+1];
434 /* look for nearest base without a commander, no Enterprise, and
435 without too many Klingons, and not already under attack. */
436 ifindit = iwhichb = 0;
439 i = basetbl[i2]; /* bug in original had it not finding nearest*/
440 ibq = game.state.baseq[i];
441 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
442 game.state.galaxy[ibq.x][ibq.y].supernova ||
443 game.state.galaxy[ibq.x][ibq.y].klingons > MAXKLQUAD-1)
445 /* if there is a commander, and no other base is appropriate,
446 we will take the one with the commander */
448 if (same(ibq, game.state.kcmdr[j]) && ifindit!= 2) {
454 if (j > game.state.remcom) { /* no commander -- use this one */
461 return; /* Nothing suitable -- wait until next time*/
462 ibq = game.state.baseq[iwhichb];
463 /* decide how to move toward base */
464 ideltax = ibq.x - game.state.kscmdr.x;
465 ideltay = ibq.y - game.state.kscmdr.y;
467 /* Maximum movement is 1 quadrant in either or both axis */
477 /* try moving in both x and y directions */
478 iq.x = game.state.kscmdr.x + ideltax;
479 iq.y = game.state.kscmdr.y + ideltax;
480 if (movescom(iq, avoid)) {
481 /* failed -- try some other maneuvers */
482 if (ideltax==0 || ideltay==0) {
483 /* attempt angle move */
485 iq.y = game.state.kscmdr.y + 1;
486 if (movescom(iq, avoid)) {
487 iq.y = game.state.kscmdr.y - 1;
492 iq.x = game.state.kscmdr.x + 1;
493 if (movescom(iq, avoid)) {
494 iq.x = game.state.kscmdr.x - 1;
500 /* try moving just in x or y */
501 iq.y = game.state.kscmdr.y;
502 if (movescom(iq, avoid)) {
503 iq.y = game.state.kscmdr.y + ideltay;
504 iq.x = game.state.kscmdr.x;
509 /* check for a base */
510 if (game.state.rembase == 0) {
513 else for_starbases(i) {
514 ibq = game.state.baseq[i];
515 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
516 /* attack the base */
518 return; /* no, don't attack base! */
519 game.iseenit = false;
521 schedule(FSCDBAS, 1.0 +2.0*Rand());
522 if (is_scheduled(FCDBAS))
523 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
524 if (damaged(DRADIO) && game.condition != docked)
525 return; /* no warning */
528 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
529 proutn(cramlc(quadrant, game.state.kscmdr));
531 prout(_(" reports that it is under attack from the Klingon Super-commander."));
532 proutn(_(" It can survive until stardate %d.\""),
533 (int)scheduled(FSCDBAS));
536 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
539 game.resting = false;
540 game.optime = 0.0; /* actually finished */
544 /* Check for intelligence report */
548 (damaged(DRADIO) && game.condition != docked) ||
549 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
552 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
553 proutn(_(" the Super-commander is in "));
554 proutn(cramlc(quadrant, game.state.kscmdr));
559 void movetholian(void)
560 /* move the Tholian */
563 if (!game.ithere || game.justin)
566 if (game.tholian.x == 1 && game.tholian.y == 1) {
567 idx = 1; idy = QUADSIZE;
569 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
570 idx = QUADSIZE; idy = QUADSIZE;
572 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
573 idx = QUADSIZE; idy = 1;
575 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
579 /* something is wrong! */
584 /* do nothing if we are blocked */
585 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB)
587 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
589 if (game.tholian.x != idx) {
591 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
592 while (game.tholian.x != idx) {
593 game.tholian.x += im;
594 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT)
595 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
598 else if (game.tholian.y != idy) {
600 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
601 while (game.tholian.y != idy) {
602 game.tholian.y += im;
603 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT)
604 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
607 game.quad[game.tholian.x][game.tholian.y] = IHT;
608 game.ks[game.nenhere] = game.tholian;
610 /* check to see if all holes plugged */
612 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT)
614 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT)
616 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT)
618 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT)
621 /* All plugged up -- Tholian splits */
622 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
624 crmena(true, IHT, sector, game.tholian);
625 prout(_(" completes web."));