3 static bool tryexit(int lookx, int looky, int ienm, int loccom, int irun)
4 /* a bad guy attempts to bug out */
9 iq.x = game.quadrant.x+(lookx+(QUADSIZE-1))/QUADSIZE - 1;
10 iq.y = game.quadrant.y+(looky+(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.condit == IHDOCKED) {
31 crmena(1, 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.condit != IHDOCKED) 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, int ienm)
70 /* tactical movement for the bad guys */
72 int motion, mdist, nsteps, mx, my, lookx, looky, ll;
78 /* This should probably be just game.comhere + game.ishere */
79 int nbaddys = game.skill >= SKILL_EXPERT ?
80 (int)((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0):
81 (game.comhere + game.ishere);
84 dist1 = game.kdist[loccom];
85 mdist = dist1 + 0.5; /* Nearest integer distance */
87 /* If SC, check with spy to see if should hi-tail it */
89 (game.kpower[loccom] <= 500.0 || (game.condit==IHDOCKED && !damaged(DPHOTON)))) {
94 /* decide whether to advance, retreat, or hold position */
96 * Enterprise has "force" based on condition of phaser and photon torpedoes.
97 If both are operating full strength, force is 1000. If both are damaged,
98 force is -1000. Having shields down subtracts an additional 1000.
100 * Enemy has forces equal to the energy of the attacker plus
101 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
102 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
104 Attacker Initial energy levels (nominal):
105 Klingon Romulan Commander Super-Commander
108 Good 450 800 1300 1750
109 Expert 475 850 1350 1875
110 Emeritus 500 900 1400 2000
111 VARIANCE 75 200 200 200
113 Enemy vessels only move prior to their attack. In Novice - Good games
114 only commanders move. In Expert games, all enemy vessels move if there
115 is a commander present. In Emeritus games all enemy vessels move.
117 * If Enterprise is not docked, an agressive action is taken if enemy
118 forces are 1000 greater than Enterprise.
120 Agressive action on average cuts the distance between the ship and
121 the enemy to 1/4 the original.
123 * At lower energy advantage, movement units are proportional to the
124 advantage with a 650 advantage being to hold ground, 800 to move forward
125 1, 950 for two, 150 for back 4, etc. Variance of 100.
127 If docked, is reduced by roughly 1.75*game.skill, generally forcing a
128 retreat, especially at high skill levels.
130 * Motion is limited to skill level, except for SC hi-tailing it out.
133 forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1);
134 if (!game.shldup) forces += 1000; /* Good for enemy if shield is down! */
135 if (!damaged(DPHASER) || !damaged(DPHOTON)) {
136 if (damaged(DPHASER)) /* phasers damaged */
139 forces -= 0.2*(game.energy - 2500.0);
140 if (damaged(DPHOTON)) /* photon torpedoes damaged */
143 forces -= 50.0*game.torps;
146 /* phasers and photon tubes both out! */
150 if (forces <= 1000.0 && game.condit != IHDOCKED) /* Typical situation */
151 motion = ((forces+200.0*Rand())/150.0) - 5.0;
153 if (forces > 1000.0) /* Very strong -- move in for kill */
154 motion = (1.0-square(Rand()))*dist1 + 1.0;
155 if (game.condit==IHDOCKED && (game.options & OPTION_BASE)) /* protected by base -- back off ! */
156 motion -= game.skill*(2.0-square(Rand()));
159 proutn("=== MOTION = %1.2f, FORCES = %1.2f, ", motion, forces);
160 /* don't move if no motion */
161 if (motion==0) return;
162 /* Limit motion according to skill */
163 if (abs(motion) > game.skill) motion = (motion < 0) ? -game.skill : game.skill;
165 /* calculate preferred number of steps */
166 nsteps = motion < 0 ? -motion : motion;
167 if (motion > 0 && nsteps > mdist) nsteps = mdist; /* don't overshoot */
168 if (nsteps > QUADSIZE) nsteps = QUADSIZE; /* This shouldn't be necessary */
169 if (nsteps < 1) nsteps = 1; /* This shouldn't be necessary */
171 proutn("NSTEPS = %d:", nsteps);
173 /* Compute preferred values of delta X and Y */
174 mx = game.sector.x - com.x;
175 my = game.sector.y - com.y;
176 if (2.0 * abs(mx) < abs(my)) mx = 0;
177 if (2.0 * abs(my) < abs(game.sector.x-com.x)) my = 0;
178 if (mx != 0) mx = mx*motion < 0 ? -1 : 1;
179 if (my != 0) my = my*motion < 0 ? -1 : 1;
182 for (ll = 0; ll < nsteps; ll++) {
185 /* Check if preferred position available */
188 krawlx = mx < 0 ? 1 : -1;
189 krawly = my < 0 ? 1 : -1;
191 attempts = 0; /* Settle mysterious hang problem */
192 while (attempts++ < 20 && !success) {
193 if (lookx < 1 || lookx > QUADSIZE) {
194 if (motion < 0 && tryexit(lookx, looky, ienm, loccom, irun))
196 if (krawlx == mx || my == 0) break;
197 lookx = next.x + krawlx;
200 else if (looky < 1 || looky > QUADSIZE) {
201 if (motion < 0 && tryexit(lookx, looky, ienm, loccom, irun))
203 if (krawly == my || mx == 0) break;
204 looky = next.y + krawly;
207 else if ((game.options & OPTION_RAMMING) && game.quad[lookx][looky] != IHDOT) {
208 /* See if we should ram ship */
209 if (game.quad[lookx][looky] == game.ship &&
210 (ienm == IHC || ienm == IHS)) {
214 if (krawlx != mx && my != 0) {
215 lookx = next.x + krawlx;
218 else if (krawly != my && mx != 0) {
219 looky = next.y + krawly;
222 else break; /* we have failed */
230 proutn(cramlc(neither, next));
232 else break; /* done early */
237 /* Put commander in place within same quadrant */
238 game.quad[com.x][com.y] = IHDOT;
239 game.quad[next.x][next.y] = ienm;
240 if (next.x != com.x || next.y != com.y) {
242 game.ks[loccom].x = next.x;
243 game.ks[loccom].y = next.y;
244 game.kdist[loccom] = game.kavgd[loccom] =
245 sqrt(square(game.sector.x-next.x)+square(game.sector.y-next.y));
246 if (!damaged(DSRSENS) || game.condit == IHDOCKED) {
249 proutn(_(" from %s"), cramlc(2, com));
250 if (game.kdist[loccom] < dist1) proutn(_(" advances to "));
251 else proutn(_(" retreats to "));
252 prout(cramlc(sector, next));
258 /* move a commander */
263 if (idebug) prout("== MOVCOM");
265 /* Figure out which Klingon is the commander (or Supercommander)
268 for_local_enemies(i) {
270 if (game.quad[w.x][w.y] == IHC) {
271 movebaddy(w, i, IHC);
276 for_local_enemies(i) {
278 if (game.quad[w.x][w.y] == IHS) {
279 movebaddy(w, i, IHS);
283 /* if skill level is high, move other Klingons and Romulans too!
284 Move these last so they can base their actions on what the
286 if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY))
287 for_local_enemies(i) {
289 if (game.quad[w.x][w.y] == IHK || game.quad[w.x][w.y] == IHR)
290 movebaddy(w, i, game.quad[w.x][w.y]);
296 static bool movescom(coord iq, bool flag, bool *ipage)
297 /* commander movement helper */
301 if (same(iq, game.quadrant) || !VALID_QUADRANT(iq.x, iq.y) ||
302 game.state.galaxy[iq.x][iq.y].supernova ||
303 game.state.galaxy[iq.x][iq.y].klingons > 8)
306 /* Avoid quadrants with bases if we want to avoid Enterprise */
308 if (game.state.baseq[i].x==iq.x && game.state.baseq[i].y==iq.y) return 1;
310 if (game.justin && !game.iscate) return true;
312 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons--;
313 game.state.kscmdr = iq;
314 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons++;
316 /* SC has scooted, Remove him from current quadrant */
320 game.ientesc = false;
323 if (game.quad[game.ks[i].x][game.ks[i].y] == IHS) break;
324 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT;
325 game.ks[i] = game.ks[game.nenhere];
326 game.kdist[i] = game.kdist[game.nenhere];
327 game.kavgd[i] = game.kavgd[game.nenhere];
328 game.kpower[i] = game.kpower[game.nenhere];
331 if (game.condit!=IHDOCKED) newcnd();
334 /* check for a helpful planet */
335 for (i = 0; i < game.inplan; i++) {
336 if (game.state.plnets[i].w.x==game.state.kscmdr.x && game.state.plnets[i].w.y==game.state.kscmdr.y &&
337 game.state.plnets[i].crystals == 1) {
338 /* destroy the planet */
339 DESTROY(&game.state.plnets[i]);
340 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET;
341 if (!damaged(DRADIO) || game.condit == IHDOCKED) {
342 if (*ipage==0) pause_game(1);
344 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
345 proutn(_(" a planet in "));
346 proutn(cramlc(quadrant, game.state.kscmdr));
347 prout(_(" has been destroyed"));
348 prout(_(" by the Super-commander.\""));
353 return false; /* looks good! */
356 void scom(bool *ipage)
357 /* move the Super Commander */
359 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
361 int basetbl[BASEMAX+1];
362 double bdist[BASEMAX+1];
365 if (idebug) prout("== SCOM");
367 /* Decide on being active or passive */
368 flag = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
369 (game.state.date-game.indate) < 3.0);
370 if (game.iscate==0 && flag) {
371 /* compute move away from Enterprise */
372 ideltax = game.state.kscmdr.x-game.quadrant.x;
373 ideltay = game.state.kscmdr.y-game.quadrant.y;
374 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
375 /* circulate in space */
376 ideltax = game.state.kscmdr.y-game.quadrant.y;
377 ideltay = game.quadrant.x-game.state.kscmdr.x;
381 /* compute distances to starbases */
382 if (game.state.rembase <= 0) {
383 /* nothing left to do */
387 sc = game.state.kscmdr;
390 ibq.x = game.state.baseq[i].x;
391 ibq.y = game.state.baseq[i].y;
392 bdist[i] = sqrt(square(ibq.x-sc.x) + square(ibq.y-sc.y));
394 if (game.state.rembase > 1) {
395 /* sort into nearest first order */
399 for (i=1; i < game.state.rembase-1; i++) {
400 if (bdist[i] > bdist[i+1]) {
403 bdist[i] = bdist[i+1];
405 basetbl[i] = basetbl[i+1];
412 /* look for nearest base without a commander, no Enterprise, and
413 without too many Klingons, and not already under attack. */
414 ifindit = iwhichb = 0;
417 i = basetbl[i2]; /* bug in original had it not finding nearest*/
418 ibq = game.state.baseq[i];
419 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
420 game.state.galaxy[ibq.x][ibq.y].supernova ||
421 game.state.galaxy[ibq.x][ibq.y].klingons > 8)
423 /* if there is a commander, an no other base is appropriate,
424 we will take the one with the commander */
426 if (ibq.x==game.state.kcmdr[j].x && ibq.y==game.state.kcmdr[j].y && ifindit!= 2) {
432 if (j > game.state.remcom) { /* no commander -- use this one */
438 if (ifindit==0) return; /* Nothing suitable -- wait until next time*/
439 ibq = game.state.baseq[iwhichb];
440 /* decide how to move toward base */
441 ideltax = ibq.x - game.state.kscmdr.x;
442 ideltay = ibq.y - game.state.kscmdr.y;
444 /* Maximum movement is 1 quadrant in either or both axis */
445 if (ideltax > 1) ideltax = 1;
446 if (ideltax < -1) ideltax = -1;
447 if (ideltay > 1) ideltay = 1;
448 if (ideltay < -1) ideltay = -1;
450 /* try moving in both x and y directions */
451 iq.x = game.state.kscmdr.x + ideltax;
452 iq.y = game.state.kscmdr.y + ideltax;
453 if (movescom(iq, flag, ipage)) {
454 /* failed -- try some other maneuvers */
455 if (ideltax==0 || ideltay==0) {
456 /* attempt angle move */
458 iq.y = game.state.kscmdr.y + 1;
459 if (movescom(iq, flag, ipage)) {
460 iq.y = game.state.kscmdr.y - 1;
461 movescom(iq, flag, ipage);
465 iq.x = game.state.kscmdr.x + 1;
466 if (movescom(iq, flag, ipage)) {
467 iq.x = game.state.kscmdr.x - 1;
468 movescom(iq, flag, ipage);
473 /* try moving just in x or y */
474 iq.y = game.state.kscmdr.y;
475 if (movescom(iq, flag, ipage)) {
476 iq.y = game.state.kscmdr.y + ideltay;
477 iq.x = game.state.kscmdr.x;
478 movescom(iq, flag, ipage);
482 /* check for a base */
483 if (game.state.rembase == 0) {
486 else for_starbases(i) {
487 ibq = game.state.baseq[i];
488 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
489 /* attack the base */
490 if (flag) return; /* no, don't attack base! */
493 schedule(FSCDBAS, 1.0 +2.0*Rand());
494 if (is_scheduled(FCDBAS))
495 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
496 if (damaged(DRADIO) && game.condit != IHDOCKED)
497 return; /* no warning */
499 if (*ipage == 0) pause_game(1);
501 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
502 proutn(cramlc(quadrant, game.state.kscmdr));
504 prout(_(" reports that it is under attack from the Klingon Super-commander."));
505 proutn(_(" It can survive until stardate %d.\""),
506 (int)scheduled(FSCDBAS));
507 if (!game.resting) return;
508 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
509 if (ja() == false) return;
510 game.resting = false;
511 game.optime = 0.0; /* actually finished */
515 /* Check for intelligence report */
519 (damaged(DRADIO) && game.condit != IHDOCKED) ||
520 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
522 if (*ipage==0) pause_game(1);
524 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
525 proutn(_(" the Super-commander is in "));
526 proutn(cramlc(quadrant, game.state.kscmdr));
532 /* move the Tholian */
536 /* Move the Tholian */
537 if (!game.ithere || game.justin) return;
539 if (game.tholian.x == 1 && game.tholian.y == 1) {
540 idx = 1; idy = QUADSIZE;
542 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
543 idx = QUADSIZE; idy = QUADSIZE;
545 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
546 idx = QUADSIZE; idy = 1;
548 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
552 /* something is wrong! */
557 /* Do nothing if we are blocked */
558 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB) return;
559 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
561 if (game.tholian.x != idx) {
563 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
564 while (game.tholian.x != idx) {
565 game.tholian.x += im;
566 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
569 else if (game.tholian.y != idy) {
571 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
572 while (game.tholian.y != idy) {
573 game.tholian.y += im;
574 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
577 game.quad[game.tholian.x][game.tholian.y] = IHT;
578 game.ks[game.nenhere] = game.tholian;
580 /* check to see if all holes plugged */
582 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT) return;
583 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT) return;
584 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT) return;
585 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT) return;
587 /* All plugged up -- Tholian splits */
588 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
589 dropin(IHBLANK, &dummy);
590 crmena(true, IHT, sector, game.tholian);
591 prout(_(" completes web."));
593 game.tholian.x = game.tholian.y = 0;