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 */
74 static void movebaddy(coord com, int loccom, feature ienm)
75 /* tactical movement for the bad guys */
77 int motion, mdist, nsteps, mx, my, ll;
80 bool success, irun = false;
82 /* This should probably be just game.comhere + game.ishere */
83 int nbaddys = game.skill >= SKILL_EXPERT ?
84 (int)((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0):
85 (game.comhere + game.ishere);
88 dist1 = game.kdist[loccom];
89 mdist = dist1 + 0.5; /* Nearest integer distance */
91 /* If SC, check with spy to see if should hi-tail it */
93 (game.kpower[loccom] <= 500.0 || (game.condition==docked && !damaged(DPHOTON)))) {
98 /* decide whether to advance, retreat, or hold position */
100 * Enterprise has "force" based on condition of phaser and photon torpedoes.
101 If both are operating full strength, force is 1000. If both are damaged,
102 force is -1000. Having shields down subtracts an additional 1000.
104 * Enemy has forces equal to the energy of the attacker plus
105 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
106 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
108 Attacker Initial energy levels (nominal):
109 Klingon Romulan Commander Super-Commander
112 Good 450 800 1300 1750
113 Expert 475 850 1350 1875
114 Emeritus 500 900 1400 2000
115 VARIANCE 75 200 200 200
117 Enemy vessels only move prior to their attack. In Novice - Good games
118 only commanders move. In Expert games, all enemy vessels move if there
119 is a commander present. In Emeritus games all enemy vessels move.
121 * If Enterprise is not docked, an agressive action is taken if enemy
122 forces are 1000 greater than Enterprise.
124 Agressive action on average cuts the distance between the ship and
125 the enemy to 1/4 the original.
127 * At lower energy advantage, movement units are proportional to the
128 advantage with a 650 advantage being to hold ground, 800 to move forward
129 1, 950 for two, 150 for back 4, etc. Variance of 100.
131 If docked, is reduced by roughly 1.75*game.skill, generally forcing a
132 retreat, especially at high skill levels.
134 * Motion is limited to skill level, except for SC hi-tailing it out.
137 forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1);
139 forces += 1000; /* Good for enemy if shield is down! */
140 if (!damaged(DPHASER) || !damaged(DPHOTON)) {
141 if (damaged(DPHASER)) /* phasers damaged */
144 forces -= 0.2*(game.energy - 2500.0);
145 if (damaged(DPHOTON)) /* photon torpedoes damaged */
148 forces -= 50.0*game.torps;
151 /* phasers and photon tubes both out! */
155 if (forces <= 1000.0 && game.condition != docked) /* Typical situation */
156 motion = ((forces+200.0*Rand())/150.0) - 5.0;
158 if (forces > 1000.0) /* Very strong -- move in for kill */
159 motion = (1.0-square(Rand()))*dist1 + 1.0;
160 if (game.condition==docked && (game.options & OPTION_BASE)) /* protected by base -- back off ! */
161 motion -= game.skill*(2.0-square(Rand()));
164 proutn("=== MOTION = %d, FORCES = %1.2f, ", motion, forces);
165 /* don't move if no motion */
168 /* Limit motion according to skill */
169 if (abs(motion) > game.skill)
170 motion = (motion < 0) ? -game.skill : game.skill;
172 /* calculate preferred number of steps */
173 nsteps = motion < 0 ? -motion : motion;
174 if (motion > 0 && nsteps > mdist)
175 nsteps = mdist; /* don't overshoot */
176 if (nsteps > QUADSIZE)
177 nsteps = QUADSIZE; /* This shouldn't be necessary */
179 nsteps = 1; /* This shouldn't be necessary */
181 proutn("NSTEPS = %d:", nsteps);
183 /* Compute preferred values of delta X and Y */
184 mx = game.sector.x - com.x;
185 my = game.sector.y - com.y;
186 if (2.0 * abs(mx) < abs(my))
188 if (2.0 * abs(my) < abs(game.sector.x-com.x))
191 mx = mx*motion < 0 ? -1 : 1;
193 my = my*motion < 0 ? -1 : 1;
196 for (ll = 0; ll < nsteps; ll++) {
199 /* Check if preferred position available */
200 look.x = next.x + mx;
201 look.y = next.y + my;
202 krawlx = mx < 0 ? 1 : -1;
203 krawly = my < 0 ? 1 : -1;
205 attempts = 0; /* Settle mysterious hang problem */
206 while (attempts++ < 20 && !success) {
207 if (look.x < 1 || look.x > QUADSIZE) {
208 if (motion < 0 && tryexit(look, ienm, loccom, irun))
210 if (krawlx == mx || my == 0)
212 look.x = next.x + krawlx;
215 else if (look.y < 1 || look.y > QUADSIZE) {
216 if (motion < 0 && tryexit(look, ienm, loccom, irun))
218 if (krawly == my || mx == 0)
220 look.y = next.y + krawly;
223 else if ((game.options & OPTION_RAMMING) && game.quad[look.x][look.y] != IHDOT) {
224 /* See if we should ram ship */
225 if (game.quad[look.x][look.y] == game.ship &&
226 (ienm == IHC || ienm == IHS)) {
227 ram(true, ienm, com);
230 if (krawlx != mx && my != 0) {
231 look.x = next.x + krawlx;
234 else if (krawly != my && mx != 0) {
235 look.y = next.y + krawly;
239 break; /* we have failed */
247 proutn(cramlc(neither, next));
250 break; /* done early */
255 /* Put commander in place within same quadrant */
256 game.quad[com.x][com.y] = IHDOT;
257 game.quad[next.x][next.y] = ienm;
258 if (!same(next, com)) {
260 game.ks[loccom] = next;
261 game.kdist[loccom] = game.kavgd[loccom] = distance(game.sector, next);
262 if (!damaged(DSRSENS) || game.condition == docked) {
265 proutn(_(" from %s"), cramlc(2, com));
266 if (game.kdist[loccom] < dist1)
267 proutn(_(" advances to "));
269 proutn(_(" retreats to "));
270 prout(cramlc(sector, next));
275 void moveklings(void)
276 /* move a commander */
284 /* Figure out which Klingon is the commander (or Supercommander)
287 for_local_enemies(i) {
289 if (game.quad[w.x][w.y] == IHC) {
290 movebaddy(w, i, IHC);
295 for_local_enemies(i) {
297 if (game.quad[w.x][w.y] == IHS) {
298 movebaddy(w, i, IHS);
302 /* if skill level is high, move other Klingons and Romulans too!
303 Move these last so they can base their actions on what the
305 if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY))
306 for_local_enemies(i) {
308 if (game.quad[w.x][w.y] == IHK || game.quad[w.x][w.y] == IHR)
309 movebaddy(w, i, game.quad[w.x][w.y]);
315 static bool movescom(coord iq, bool flag)
316 /* commander movement helper */
320 if (same(iq, game.quadrant) || !VALID_QUADRANT(iq.x, iq.y) ||
321 game.state.galaxy[iq.x][iq.y].supernova ||
322 game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1)
325 /* Avoid quadrants with bases if we want to avoid Enterprise */
327 if (same(game.state.baseq[i], iq))
330 if (game.justin && !game.iscate)
333 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons--;
334 game.state.kscmdr = iq;
335 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons++;
337 /* SC has scooted, Remove him from current quadrant */
341 game.ientesc = false;
344 if (game.quad[game.ks[i].x][game.ks[i].y] == IHS)
346 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT;
347 game.ks[i] = game.ks[game.nenhere];
348 game.kdist[i] = game.kdist[game.nenhere];
349 game.kavgd[i] = game.kavgd[game.nenhere];
350 game.kpower[i] = game.kpower[game.nenhere];
353 if (game.condition!=docked)
357 /* check for a helpful planet */
358 for (i = 0; i < game.inplan; i++) {
359 if (same(game.state.planets[i].w, game.state.kscmdr) &&
360 game.state.planets[i].crystals == present) {
361 /* destroy the planet */
362 DESTROY(&game.state.planets[i]);
363 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET;
364 if (!damaged(DRADIO) || game.condition == docked) {
366 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
367 proutn(_(" a planet in "));
368 proutn(cramlc(quadrant, game.state.kscmdr));
369 prout(_(" has been destroyed"));
370 prout(_(" by the Super-commander.\""));
375 return false; /* looks good! */
379 /* move the Super Commander */
381 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
383 int basetbl[BASEMAX+1];
384 double bdist[BASEMAX+1];
390 /* Decide on being active or passive */
391 flag = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
392 (game.state.date-game.indate) < 3.0);
393 if (!game.iscate && flag) {
394 /* compute move away from Enterprise */
395 ideltax = game.state.kscmdr.x-game.quadrant.x;
396 ideltay = game.state.kscmdr.y-game.quadrant.y;
397 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
398 /* circulate in space */
399 ideltax = game.state.kscmdr.y-game.quadrant.y;
400 ideltay = game.quadrant.x-game.state.kscmdr.x;
404 /* compute distances to starbases */
405 if (game.state.rembase <= 0) {
406 /* nothing left to do */
410 sc = game.state.kscmdr;
413 bdist[i] = distance(game.state.baseq[i], sc);
415 if (game.state.rembase > 1) {
416 /* sort into nearest first order */
420 for (i=1; i < game.state.rembase-1; i++) {
421 if (bdist[i] > bdist[i+1]) {
424 bdist[i] = bdist[i+1];
426 basetbl[i] = basetbl[i+1];
433 /* look for nearest base without a commander, no Enterprise, and
434 without too many Klingons, and not already under attack. */
435 ifindit = iwhichb = 0;
438 i = basetbl[i2]; /* bug in original had it not finding nearest*/
439 ibq = game.state.baseq[i];
440 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
441 game.state.galaxy[ibq.x][ibq.y].supernova ||
442 game.state.galaxy[ibq.x][ibq.y].klingons > MAXKLQUAD-1)
444 /* if there is a commander, an no other base is appropriate,
445 we will take the one with the commander */
447 if (same(ibq, game.state.kcmdr[j]) && ifindit!= 2) {
453 if (j > game.state.remcom) { /* no commander -- use this one */
460 return; /* Nothing suitable -- wait until next time*/
461 ibq = game.state.baseq[iwhichb];
462 /* decide how to move toward base */
463 ideltax = ibq.x - game.state.kscmdr.x;
464 ideltay = ibq.y - game.state.kscmdr.y;
466 /* Maximum movement is 1 quadrant in either or both axis */
476 /* try moving in both x and y directions */
477 iq.x = game.state.kscmdr.x + ideltax;
478 iq.y = game.state.kscmdr.y + ideltax;
479 if (movescom(iq, flag)) {
480 /* failed -- try some other maneuvers */
481 if (ideltax==0 || ideltay==0) {
482 /* attempt angle move */
484 iq.y = game.state.kscmdr.y + 1;
485 if (movescom(iq, flag)) {
486 iq.y = game.state.kscmdr.y - 1;
491 iq.x = game.state.kscmdr.x + 1;
492 if (movescom(iq, flag)) {
493 iq.x = game.state.kscmdr.x - 1;
499 /* try moving just in x or y */
500 iq.y = game.state.kscmdr.y;
501 if (movescom(iq, flag)) {
502 iq.y = game.state.kscmdr.y + ideltay;
503 iq.x = game.state.kscmdr.x;
508 /* check for a base */
509 if (game.state.rembase == 0) {
512 else for_starbases(i) {
513 ibq = game.state.baseq[i];
514 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
515 /* attack the base */
517 return; /* no, don't attack base! */
518 game.iseenit = false;
520 schedule(FSCDBAS, 1.0 +2.0*Rand());
521 if (is_scheduled(FCDBAS))
522 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
523 if (damaged(DRADIO) && game.condition != docked)
524 return; /* no warning */
527 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
528 proutn(cramlc(quadrant, game.state.kscmdr));
530 prout(_(" reports that it is under attack from the Klingon Super-commander."));
531 proutn(_(" It can survive until stardate %d.\""),
532 (int)scheduled(FSCDBAS));
535 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
538 game.resting = false;
539 game.optime = 0.0; /* actually finished */
543 /* Check for intelligence report */
547 (damaged(DRADIO) && game.condition != docked) ||
548 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
551 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
552 proutn(_(" the Super-commander is in "));
553 proutn(cramlc(quadrant, game.state.kscmdr));
559 /* move the Tholian */
562 /* 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."));