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 > 8)
14 return false; /* no can do -- neg energy, supernovae, or >8 Klingons */
15 if (ienm == IHR) return false; /* Romulans cannot escape! */
17 /* avoid intruding on another commander's territory */
20 if (same(game.state.kcmdr[n],iq)) return false;
21 /* refuse to leave if currently attacking starbase */
22 if (same(game.battle, game.quadrant)) return false;
24 /* don't leave if over 1000 units of energy */
25 if (game.kpower[loccom] > 1000.) return false;
27 /* print escape message and move out of quadrant.
28 We know this if either short or long range sensors are working */
29 if (!damaged(DSRSENS) || !damaged(DLRSENS) ||
30 game.condition == docked) {
31 crmena(true, ienm, sector, game.ks[loccom]);
32 prout(_(" escapes to %s (and regains strength)."),
33 cramlc(quadrant, iq));
35 /* handle local matters related to escape */
36 game.quad[game.ks[loccom].x][game.ks[loccom].y] = IHDOT;
37 game.ks[loccom] = game.ks[game.nenhere];
38 game.kavgd[loccom] = game.kavgd[game.nenhere];
39 game.kpower[loccom] = game.kpower[game.nenhere];
40 game.kdist[loccom] = game.kdist[game.nenhere];
43 if (game.condition != docked) newcnd();
44 /* Handle global matters related to escape */
45 game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons--;
46 game.state.galaxy[iq.x][iq.y].klingons++;
52 schedule(FSCMOVE, 0.2777);
58 if (same(game.state.kcmdr[n], game.quadrant)) {
59 game.state.kcmdr[n]=iq;
65 return true; /* success */
69 static void movebaddy(coord com, int loccom, feature ienm)
70 /* tactical movement for the bad guys */
72 int motion, mdist, nsteps, mx, my, ll;
75 bool success, irun = false;
77 /* This should probably be just game.comhere + game.ishere */
78 int nbaddys = game.skill >= SKILL_EXPERT ?
79 (int)((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0):
80 (game.comhere + game.ishere);
83 dist1 = game.kdist[loccom];
84 mdist = dist1 + 0.5; /* Nearest integer distance */
86 /* If SC, check with spy to see if should hi-tail it */
88 (game.kpower[loccom] <= 500.0 || (game.condition==docked && !damaged(DPHOTON)))) {
93 /* decide whether to advance, retreat, or hold position */
95 * Enterprise has "force" based on condition of phaser and photon torpedoes.
96 If both are operating full strength, force is 1000. If both are damaged,
97 force is -1000. Having shields down subtracts an additional 1000.
99 * Enemy has forces equal to the energy of the attacker plus
100 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
101 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
103 Attacker Initial energy levels (nominal):
104 Klingon Romulan Commander Super-Commander
107 Good 450 800 1300 1750
108 Expert 475 850 1350 1875
109 Emeritus 500 900 1400 2000
110 VARIANCE 75 200 200 200
112 Enemy vessels only move prior to their attack. In Novice - Good games
113 only commanders move. In Expert games, all enemy vessels move if there
114 is a commander present. In Emeritus games all enemy vessels move.
116 * If Enterprise is not docked, an agressive action is taken if enemy
117 forces are 1000 greater than Enterprise.
119 Agressive action on average cuts the distance between the ship and
120 the enemy to 1/4 the original.
122 * At lower energy advantage, movement units are proportional to the
123 advantage with a 650 advantage being to hold ground, 800 to move forward
124 1, 950 for two, 150 for back 4, etc. Variance of 100.
126 If docked, is reduced by roughly 1.75*game.skill, generally forcing a
127 retreat, especially at high skill levels.
129 * Motion is limited to skill level, except for SC hi-tailing it out.
132 forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1);
133 if (!game.shldup) forces += 1000; /* Good for enemy if shield is down! */
134 if (!damaged(DPHASER) || !damaged(DPHOTON)) {
135 if (damaged(DPHASER)) /* phasers damaged */
138 forces -= 0.2*(game.energy - 2500.0);
139 if (damaged(DPHOTON)) /* photon torpedoes damaged */
142 forces -= 50.0*game.torps;
145 /* phasers and photon tubes both out! */
149 if (forces <= 1000.0 && game.condition != docked) /* Typical situation */
150 motion = ((forces+200.0*Rand())/150.0) - 5.0;
152 if (forces > 1000.0) /* Very strong -- move in for kill */
153 motion = (1.0-square(Rand()))*dist1 + 1.0;
154 if (game.condition==docked && (game.options & OPTION_BASE)) /* protected by base -- back off ! */
155 motion -= game.skill*(2.0-square(Rand()));
158 proutn("=== MOTION = %1.2f, FORCES = %1.2f, ", motion, forces);
159 /* don't move if no motion */
160 if (motion==0) return;
161 /* Limit motion according to skill */
162 if (abs(motion) > game.skill) motion = (motion < 0) ? -game.skill : game.skill;
164 /* calculate preferred number of steps */
165 nsteps = motion < 0 ? -motion : motion;
166 if (motion > 0 && nsteps > mdist) nsteps = mdist; /* don't overshoot */
167 if (nsteps > QUADSIZE) nsteps = QUADSIZE; /* This shouldn't be necessary */
168 if (nsteps < 1) nsteps = 1; /* This shouldn't be necessary */
170 proutn("NSTEPS = %d:", nsteps);
172 /* Compute preferred values of delta X and Y */
173 mx = game.sector.x - com.x;
174 my = game.sector.y - com.y;
175 if (2.0 * abs(mx) < abs(my)) mx = 0;
176 if (2.0 * abs(my) < abs(game.sector.x-com.x)) my = 0;
177 if (mx != 0) mx = mx*motion < 0 ? -1 : 1;
178 if (my != 0) my = my*motion < 0 ? -1 : 1;
181 for (ll = 0; ll < nsteps; ll++) {
184 /* Check if preferred position available */
185 look.x = next.x + mx;
186 look.y = next.y + my;
187 krawlx = mx < 0 ? 1 : -1;
188 krawly = my < 0 ? 1 : -1;
190 attempts = 0; /* Settle mysterious hang problem */
191 while (attempts++ < 20 && !success) {
192 if (look.x < 1 || look.x > QUADSIZE) {
193 if (motion < 0 && tryexit(look, ienm, loccom, irun))
195 if (krawlx == mx || my == 0) break;
196 look.x = next.x + krawlx;
199 else if (look.y < 1 || look.y > QUADSIZE) {
200 if (motion < 0 && tryexit(look, ienm, loccom, irun))
202 if (krawly == my || mx == 0) break;
203 look.y = next.y + krawly;
206 else if ((game.options & OPTION_RAMMING) && game.quad[look.x][look.y] != IHDOT) {
207 /* See if we should ram ship */
208 if (game.quad[look.x][look.y] == game.ship &&
209 (ienm == IHC || ienm == IHS)) {
210 ram(true, ienm, com);
213 if (krawlx != mx && my != 0) {
214 look.x = next.x + krawlx;
217 else if (krawly != my && mx != 0) {
218 look.y = next.y + krawly;
221 else break; /* we have failed */
228 proutn(cramlc(neither, next));
230 else break; /* done early */
235 /* Put commander in place within same quadrant */
236 game.quad[com.x][com.y] = IHDOT;
237 game.quad[next.x][next.y] = ienm;
238 if (!same(next, com)) {
240 game.ks[loccom] = next;
241 game.kdist[loccom] = game.kavgd[loccom] = distance(game.sector, next);
242 if (!damaged(DSRSENS) || game.condition == docked) {
245 proutn(_(" from %s"), cramlc(2, com));
246 if (game.kdist[loccom] < dist1) proutn(_(" advances to "));
247 else proutn(_(" retreats to "));
248 prout(cramlc(sector, next));
254 /* move a commander */
259 if (idebug) prout("== MOVCOM");
261 /* Figure out which Klingon is the commander (or Supercommander)
264 for_local_enemies(i) {
266 if (game.quad[w.x][w.y] == IHC) {
267 movebaddy(w, i, IHC);
272 for_local_enemies(i) {
274 if (game.quad[w.x][w.y] == IHS) {
275 movebaddy(w, i, IHS);
279 /* if skill level is high, move other Klingons and Romulans too!
280 Move these last so they can base their actions on what the
282 if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY))
283 for_local_enemies(i) {
285 if (game.quad[w.x][w.y] == IHK || game.quad[w.x][w.y] == IHR)
286 movebaddy(w, i, game.quad[w.x][w.y]);
292 static bool movescom(coord iq, bool flag, bool *ipage)
293 /* commander movement helper */
297 if (same(iq, game.quadrant) || !VALID_QUADRANT(iq.x, iq.y) ||
298 game.state.galaxy[iq.x][iq.y].supernova ||
299 game.state.galaxy[iq.x][iq.y].klingons > 8)
302 /* Avoid quadrants with bases if we want to avoid Enterprise */
304 if (same(game.state.baseq[i], iq))
307 if (game.justin && !game.iscate) return true;
309 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons--;
310 game.state.kscmdr = iq;
311 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons++;
313 /* SC has scooted, Remove him from current quadrant */
317 game.ientesc = false;
320 if (game.quad[game.ks[i].x][game.ks[i].y] == IHS) break;
321 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT;
322 game.ks[i] = game.ks[game.nenhere];
323 game.kdist[i] = game.kdist[game.nenhere];
324 game.kavgd[i] = game.kavgd[game.nenhere];
325 game.kpower[i] = game.kpower[game.nenhere];
328 if (game.condition!=docked) newcnd();
331 /* check for a helpful planet */
332 for (i = 0; i < game.inplan; i++) {
333 if (same(game.state.plnets[i].w, game.state.kscmdr) &&
334 game.state.plnets[i].crystals == 1) {
335 /* destroy the planet */
336 DESTROY(&game.state.plnets[i]);
337 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET;
338 if (!damaged(DRADIO) || game.condition == docked) {
339 if (!*ipage) pause_game(true);
341 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
342 proutn(_(" a planet in "));
343 proutn(cramlc(quadrant, game.state.kscmdr));
344 prout(_(" has been destroyed"));
345 prout(_(" by the Super-commander.\""));
350 return false; /* looks good! */
353 void scom(bool *ipage)
354 /* move the Super Commander */
356 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
358 int basetbl[BASEMAX+1];
359 double bdist[BASEMAX+1];
362 if (idebug) prout("== SCOM");
364 /* Decide on being active or passive */
365 flag = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
366 (game.state.date-game.indate) < 3.0);
367 if (!game.iscate && flag) {
368 /* compute move away from Enterprise */
369 ideltax = game.state.kscmdr.x-game.quadrant.x;
370 ideltay = game.state.kscmdr.y-game.quadrant.y;
371 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
372 /* circulate in space */
373 ideltax = game.state.kscmdr.y-game.quadrant.y;
374 ideltay = game.quadrant.x-game.state.kscmdr.x;
378 /* compute distances to starbases */
379 if (game.state.rembase <= 0) {
380 /* nothing left to do */
384 sc = game.state.kscmdr;
387 bdist[i] = distance(game.state.baseq[i], sc);
389 if (game.state.rembase > 1) {
390 /* sort into nearest first order */
394 for (i=1; i < game.state.rembase-1; i++) {
395 if (bdist[i] > bdist[i+1]) {
398 bdist[i] = bdist[i+1];
400 basetbl[i] = basetbl[i+1];
407 /* look for nearest base without a commander, no Enterprise, and
408 without too many Klingons, and not already under attack. */
409 ifindit = iwhichb = 0;
412 i = basetbl[i2]; /* bug in original had it not finding nearest*/
413 ibq = game.state.baseq[i];
414 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
415 game.state.galaxy[ibq.x][ibq.y].supernova ||
416 game.state.galaxy[ibq.x][ibq.y].klingons > 8)
418 /* if there is a commander, an no other base is appropriate,
419 we will take the one with the commander */
421 if (same(ibq, game.state.kcmdr[j]) && ifindit!= 2) {
427 if (j > game.state.remcom) { /* no commander -- use this one */
433 if (ifindit==0) return; /* Nothing suitable -- wait until next time*/
434 ibq = game.state.baseq[iwhichb];
435 /* decide how to move toward base */
436 ideltax = ibq.x - game.state.kscmdr.x;
437 ideltay = ibq.y - game.state.kscmdr.y;
439 /* Maximum movement is 1 quadrant in either or both axis */
440 if (ideltax > 1) ideltax = 1;
441 if (ideltax < -1) ideltax = -1;
442 if (ideltay > 1) ideltay = 1;
443 if (ideltay < -1) ideltay = -1;
445 /* try moving in both x and y directions */
446 iq.x = game.state.kscmdr.x + ideltax;
447 iq.y = game.state.kscmdr.y + ideltax;
448 if (movescom(iq, flag, ipage)) {
449 /* failed -- try some other maneuvers */
450 if (ideltax==0 || ideltay==0) {
451 /* attempt angle move */
453 iq.y = game.state.kscmdr.y + 1;
454 if (movescom(iq, flag, ipage)) {
455 iq.y = game.state.kscmdr.y - 1;
456 movescom(iq, flag, ipage);
460 iq.x = game.state.kscmdr.x + 1;
461 if (movescom(iq, flag, ipage)) {
462 iq.x = game.state.kscmdr.x - 1;
463 movescom(iq, flag, ipage);
468 /* try moving just in x or y */
469 iq.y = game.state.kscmdr.y;
470 if (movescom(iq, flag, ipage)) {
471 iq.y = game.state.kscmdr.y + ideltay;
472 iq.x = game.state.kscmdr.x;
473 movescom(iq, flag, ipage);
477 /* check for a base */
478 if (game.state.rembase == 0) {
481 else for_starbases(i) {
482 ibq = game.state.baseq[i];
483 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
484 /* attack the base */
485 if (flag) return; /* no, don't attack base! */
486 game.iseenit = false;
488 schedule(FSCDBAS, 1.0 +2.0*Rand());
489 if (is_scheduled(FCDBAS))
490 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
491 if (damaged(DRADIO) && game.condition != docked)
492 return; /* no warning */
494 if (!*ipage) pause_game(true);
496 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
497 proutn(cramlc(quadrant, game.state.kscmdr));
499 prout(_(" reports that it is under attack from the Klingon Super-commander."));
500 proutn(_(" It can survive until stardate %d.\""),
501 (int)scheduled(FSCDBAS));
502 if (!game.resting) return;
503 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
504 if (ja() == false) return;
505 game.resting = false;
506 game.optime = 0.0; /* actually finished */
510 /* Check for intelligence report */
514 (damaged(DRADIO) && game.condition != docked) ||
515 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
517 if (!*ipage) pause_game(true);
519 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
520 proutn(_(" the Super-commander is in "));
521 proutn(cramlc(quadrant, game.state.kscmdr));
527 /* move the Tholian */
530 /* Move the Tholian */
531 if (!game.ithere || game.justin) return;
533 if (game.tholian.x == 1 && game.tholian.y == 1) {
534 idx = 1; idy = QUADSIZE;
536 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
537 idx = QUADSIZE; idy = QUADSIZE;
539 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
540 idx = QUADSIZE; idy = 1;
542 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
546 /* something is wrong! */
551 /* Do nothing if we are blocked */
552 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB) return;
553 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
555 if (game.tholian.x != idx) {
557 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
558 while (game.tholian.x != idx) {
559 game.tholian.x += im;
560 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
563 else if (game.tholian.y != idy) {
565 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
566 while (game.tholian.y != idy) {
567 game.tholian.y += im;
568 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
571 game.quad[game.tholian.x][game.tholian.y] = IHT;
572 game.ks[game.nenhere] = game.tholian;
574 /* check to see if all holes plugged */
576 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT) return;
577 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT) return;
578 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT) return;
579 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT) return;
581 /* All plugged up -- Tholian splits */
582 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
584 crmena(true, IHT, sector, game.tholian);
585 prout(_(" completes web."));