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] = distance(game.sector, next);
245 if (!damaged(DSRSENS) || game.condit == IHDOCKED) {
248 proutn(_(" from %s"), cramlc(2, com));
249 if (game.kdist[loccom] < dist1) proutn(_(" advances to "));
250 else proutn(_(" retreats to "));
251 prout(cramlc(sector, next));
257 /* move a commander */
262 if (idebug) prout("== MOVCOM");
264 /* Figure out which Klingon is the commander (or Supercommander)
267 for_local_enemies(i) {
269 if (game.quad[w.x][w.y] == IHC) {
270 movebaddy(w, i, IHC);
275 for_local_enemies(i) {
277 if (game.quad[w.x][w.y] == IHS) {
278 movebaddy(w, i, IHS);
282 /* if skill level is high, move other Klingons and Romulans too!
283 Move these last so they can base their actions on what the
285 if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY))
286 for_local_enemies(i) {
288 if (game.quad[w.x][w.y] == IHK || game.quad[w.x][w.y] == IHR)
289 movebaddy(w, i, game.quad[w.x][w.y]);
295 static bool movescom(coord iq, bool flag, bool *ipage)
296 /* commander movement helper */
300 if (same(iq, game.quadrant) || !VALID_QUADRANT(iq.x, iq.y) ||
301 game.state.galaxy[iq.x][iq.y].supernova ||
302 game.state.galaxy[iq.x][iq.y].klingons > 8)
305 /* Avoid quadrants with bases if we want to avoid Enterprise */
307 if (game.state.baseq[i].x==iq.x && game.state.baseq[i].y==iq.y) return 1;
309 if (game.justin && !game.iscate) return true;
311 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons--;
312 game.state.kscmdr = iq;
313 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons++;
315 /* SC has scooted, Remove him from current quadrant */
319 game.ientesc = false;
322 if (game.quad[game.ks[i].x][game.ks[i].y] == IHS) break;
323 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT;
324 game.ks[i] = game.ks[game.nenhere];
325 game.kdist[i] = game.kdist[game.nenhere];
326 game.kavgd[i] = game.kavgd[game.nenhere];
327 game.kpower[i] = game.kpower[game.nenhere];
330 if (game.condit!=IHDOCKED) newcnd();
333 /* check for a helpful planet */
334 for (i = 0; i < game.inplan; i++) {
335 if (game.state.plnets[i].w.x==game.state.kscmdr.x && game.state.plnets[i].w.y==game.state.kscmdr.y &&
336 game.state.plnets[i].crystals == 1) {
337 /* destroy the planet */
338 DESTROY(&game.state.plnets[i]);
339 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET;
340 if (!damaged(DRADIO) || game.condit == IHDOCKED) {
341 if (*ipage==0) pause_game(1);
343 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
344 proutn(_(" a planet in "));
345 proutn(cramlc(quadrant, game.state.kscmdr));
346 prout(_(" has been destroyed"));
347 prout(_(" by the Super-commander.\""));
352 return false; /* looks good! */
355 void scom(bool *ipage)
356 /* move the Super Commander */
358 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
360 int basetbl[BASEMAX+1];
361 double bdist[BASEMAX+1];
364 if (idebug) prout("== SCOM");
366 /* Decide on being active or passive */
367 flag = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
368 (game.state.date-game.indate) < 3.0);
369 if (game.iscate==0 && flag) {
370 /* compute move away from Enterprise */
371 ideltax = game.state.kscmdr.x-game.quadrant.x;
372 ideltay = game.state.kscmdr.y-game.quadrant.y;
373 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
374 /* circulate in space */
375 ideltax = game.state.kscmdr.y-game.quadrant.y;
376 ideltay = game.quadrant.x-game.state.kscmdr.x;
380 /* compute distances to starbases */
381 if (game.state.rembase <= 0) {
382 /* nothing left to do */
386 sc = game.state.kscmdr;
389 bdist[i] = distance(game.state.baseq[i], sc);
391 if (game.state.rembase > 1) {
392 /* sort into nearest first order */
396 for (i=1; i < game.state.rembase-1; i++) {
397 if (bdist[i] > bdist[i+1]) {
400 bdist[i] = bdist[i+1];
402 basetbl[i] = basetbl[i+1];
409 /* look for nearest base without a commander, no Enterprise, and
410 without too many Klingons, and not already under attack. */
411 ifindit = iwhichb = 0;
414 i = basetbl[i2]; /* bug in original had it not finding nearest*/
415 ibq = game.state.baseq[i];
416 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
417 game.state.galaxy[ibq.x][ibq.y].supernova ||
418 game.state.galaxy[ibq.x][ibq.y].klingons > 8)
420 /* if there is a commander, an no other base is appropriate,
421 we will take the one with the commander */
423 if (ibq.x==game.state.kcmdr[j].x && ibq.y==game.state.kcmdr[j].y && ifindit!= 2) {
429 if (j > game.state.remcom) { /* no commander -- use this one */
435 if (ifindit==0) return; /* Nothing suitable -- wait until next time*/
436 ibq = game.state.baseq[iwhichb];
437 /* decide how to move toward base */
438 ideltax = ibq.x - game.state.kscmdr.x;
439 ideltay = ibq.y - game.state.kscmdr.y;
441 /* Maximum movement is 1 quadrant in either or both axis */
442 if (ideltax > 1) ideltax = 1;
443 if (ideltax < -1) ideltax = -1;
444 if (ideltay > 1) ideltay = 1;
445 if (ideltay < -1) ideltay = -1;
447 /* try moving in both x and y directions */
448 iq.x = game.state.kscmdr.x + ideltax;
449 iq.y = game.state.kscmdr.y + ideltax;
450 if (movescom(iq, flag, ipage)) {
451 /* failed -- try some other maneuvers */
452 if (ideltax==0 || ideltay==0) {
453 /* attempt angle move */
455 iq.y = game.state.kscmdr.y + 1;
456 if (movescom(iq, flag, ipage)) {
457 iq.y = game.state.kscmdr.y - 1;
458 movescom(iq, flag, ipage);
462 iq.x = game.state.kscmdr.x + 1;
463 if (movescom(iq, flag, ipage)) {
464 iq.x = game.state.kscmdr.x - 1;
465 movescom(iq, flag, ipage);
470 /* try moving just in x or y */
471 iq.y = game.state.kscmdr.y;
472 if (movescom(iq, flag, ipage)) {
473 iq.y = game.state.kscmdr.y + ideltay;
474 iq.x = game.state.kscmdr.x;
475 movescom(iq, flag, ipage);
479 /* check for a base */
480 if (game.state.rembase == 0) {
483 else for_starbases(i) {
484 ibq = game.state.baseq[i];
485 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
486 /* attack the base */
487 if (flag) return; /* no, don't attack base! */
490 schedule(FSCDBAS, 1.0 +2.0*Rand());
491 if (is_scheduled(FCDBAS))
492 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
493 if (damaged(DRADIO) && game.condit != IHDOCKED)
494 return; /* no warning */
496 if (*ipage == 0) pause_game(1);
498 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
499 proutn(cramlc(quadrant, game.state.kscmdr));
501 prout(_(" reports that it is under attack from the Klingon Super-commander."));
502 proutn(_(" It can survive until stardate %d.\""),
503 (int)scheduled(FSCDBAS));
504 if (!game.resting) return;
505 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
506 if (ja() == false) return;
507 game.resting = false;
508 game.optime = 0.0; /* actually finished */
512 /* Check for intelligence report */
516 (damaged(DRADIO) && game.condit != IHDOCKED) ||
517 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
519 if (*ipage==0) pause_game(1);
521 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
522 proutn(_(" the Super-commander is in "));
523 proutn(cramlc(quadrant, game.state.kscmdr));
529 /* move the Tholian */
532 /* Move the Tholian */
533 if (!game.ithere || game.justin) return;
535 if (game.tholian.x == 1 && game.tholian.y == 1) {
536 idx = 1; idy = QUADSIZE;
538 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
539 idx = QUADSIZE; idy = QUADSIZE;
541 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
542 idx = QUADSIZE; idy = 1;
544 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
548 /* something is wrong! */
553 /* Do nothing if we are blocked */
554 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB) return;
555 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
557 if (game.tholian.x != idx) {
559 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
560 while (game.tholian.x != idx) {
561 game.tholian.x += im;
562 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
565 else if (game.tholian.y != idy) {
567 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
568 while (game.tholian.y != idy) {
569 game.tholian.y += im;
570 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
573 game.quad[game.tholian.x][game.tholian.y] = IHT;
574 game.ks[game.nenhere] = game.tholian;
576 /* check to see if all holes plugged */
578 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT) return;
579 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT) return;
580 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT) return;
581 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT) return;
583 /* All plugged up -- Tholian splits */
584 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
586 crmena(true, IHT, sector, game.tholian);
587 prout(_(" completes web."));
589 game.tholian.x = game.tholian.y = 0;