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(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.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, int 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 (game.state.baseq[i].x==iq.x && game.state.baseq[i].y==iq.y) return 1;
306 if (game.justin && !game.iscate) return true;
308 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons--;
309 game.state.kscmdr = iq;
310 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons++;
312 /* SC has scooted, Remove him from current quadrant */
316 game.ientesc = false;
319 if (game.quad[game.ks[i].x][game.ks[i].y] == IHS) break;
320 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT;
321 game.ks[i] = game.ks[game.nenhere];
322 game.kdist[i] = game.kdist[game.nenhere];
323 game.kavgd[i] = game.kavgd[game.nenhere];
324 game.kpower[i] = game.kpower[game.nenhere];
327 if (game.condition!=docked) newcnd();
330 /* check for a helpful planet */
331 for (i = 0; i < game.inplan; i++) {
332 if (game.state.plnets[i].w.x==game.state.kscmdr.x && game.state.plnets[i].w.y==game.state.kscmdr.y &&
333 game.state.plnets[i].crystals == 1) {
334 /* destroy the planet */
335 DESTROY(&game.state.plnets[i]);
336 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET;
337 if (!damaged(DRADIO) || game.condition == docked) {
338 if (*ipage==0) pause_game(1);
340 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
341 proutn(_(" a planet in "));
342 proutn(cramlc(quadrant, game.state.kscmdr));
343 prout(_(" has been destroyed"));
344 prout(_(" by the Super-commander.\""));
349 return false; /* looks good! */
352 void scom(bool *ipage)
353 /* move the Super Commander */
355 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
357 int basetbl[BASEMAX+1];
358 double bdist[BASEMAX+1];
361 if (idebug) prout("== SCOM");
363 /* Decide on being active or passive */
364 flag = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
365 (game.state.date-game.indate) < 3.0);
366 if (game.iscate==0 && flag) {
367 /* compute move away from Enterprise */
368 ideltax = game.state.kscmdr.x-game.quadrant.x;
369 ideltay = game.state.kscmdr.y-game.quadrant.y;
370 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
371 /* circulate in space */
372 ideltax = game.state.kscmdr.y-game.quadrant.y;
373 ideltay = game.quadrant.x-game.state.kscmdr.x;
377 /* compute distances to starbases */
378 if (game.state.rembase <= 0) {
379 /* nothing left to do */
383 sc = game.state.kscmdr;
386 bdist[i] = distance(game.state.baseq[i], sc);
388 if (game.state.rembase > 1) {
389 /* sort into nearest first order */
393 for (i=1; i < game.state.rembase-1; i++) {
394 if (bdist[i] > bdist[i+1]) {
397 bdist[i] = bdist[i+1];
399 basetbl[i] = basetbl[i+1];
406 /* look for nearest base without a commander, no Enterprise, and
407 without too many Klingons, and not already under attack. */
408 ifindit = iwhichb = 0;
411 i = basetbl[i2]; /* bug in original had it not finding nearest*/
412 ibq = game.state.baseq[i];
413 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
414 game.state.galaxy[ibq.x][ibq.y].supernova ||
415 game.state.galaxy[ibq.x][ibq.y].klingons > 8)
417 /* if there is a commander, an no other base is appropriate,
418 we will take the one with the commander */
420 if (ibq.x==game.state.kcmdr[j].x && ibq.y==game.state.kcmdr[j].y && ifindit!= 2) {
426 if (j > game.state.remcom) { /* no commander -- use this one */
432 if (ifindit==0) return; /* Nothing suitable -- wait until next time*/
433 ibq = game.state.baseq[iwhichb];
434 /* decide how to move toward base */
435 ideltax = ibq.x - game.state.kscmdr.x;
436 ideltay = ibq.y - game.state.kscmdr.y;
438 /* Maximum movement is 1 quadrant in either or both axis */
439 if (ideltax > 1) ideltax = 1;
440 if (ideltax < -1) ideltax = -1;
441 if (ideltay > 1) ideltay = 1;
442 if (ideltay < -1) ideltay = -1;
444 /* try moving in both x and y directions */
445 iq.x = game.state.kscmdr.x + ideltax;
446 iq.y = game.state.kscmdr.y + ideltax;
447 if (movescom(iq, flag, ipage)) {
448 /* failed -- try some other maneuvers */
449 if (ideltax==0 || ideltay==0) {
450 /* attempt angle move */
452 iq.y = game.state.kscmdr.y + 1;
453 if (movescom(iq, flag, ipage)) {
454 iq.y = game.state.kscmdr.y - 1;
455 movescom(iq, flag, ipage);
459 iq.x = game.state.kscmdr.x + 1;
460 if (movescom(iq, flag, ipage)) {
461 iq.x = game.state.kscmdr.x - 1;
462 movescom(iq, flag, ipage);
467 /* try moving just in x or y */
468 iq.y = game.state.kscmdr.y;
469 if (movescom(iq, flag, ipage)) {
470 iq.y = game.state.kscmdr.y + ideltay;
471 iq.x = game.state.kscmdr.x;
472 movescom(iq, flag, ipage);
476 /* check for a base */
477 if (game.state.rembase == 0) {
480 else for_starbases(i) {
481 ibq = game.state.baseq[i];
482 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
483 /* attack the base */
484 if (flag) return; /* no, don't attack base! */
485 game.iseenit = false;
487 schedule(FSCDBAS, 1.0 +2.0*Rand());
488 if (is_scheduled(FCDBAS))
489 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
490 if (damaged(DRADIO) && game.condition != docked)
491 return; /* no warning */
493 if (*ipage == 0) pause_game(1);
495 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
496 proutn(cramlc(quadrant, game.state.kscmdr));
498 prout(_(" reports that it is under attack from the Klingon Super-commander."));
499 proutn(_(" It can survive until stardate %d.\""),
500 (int)scheduled(FSCDBAS));
501 if (!game.resting) return;
502 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
503 if (ja() == false) return;
504 game.resting = false;
505 game.optime = 0.0; /* actually finished */
509 /* Check for intelligence report */
513 (damaged(DRADIO) && game.condition != docked) ||
514 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
516 if (*ipage==0) pause_game(1);
518 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
519 proutn(_(" the Super-commander is in "));
520 proutn(cramlc(quadrant, game.state.kscmdr));
526 /* move the Tholian */
529 /* Move the Tholian */
530 if (!game.ithere || game.justin) return;
532 if (game.tholian.x == 1 && game.tholian.y == 1) {
533 idx = 1; idy = QUADSIZE;
535 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
536 idx = QUADSIZE; idy = QUADSIZE;
538 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
539 idx = QUADSIZE; idy = 1;
541 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
545 /* something is wrong! */
550 /* Do nothing if we are blocked */
551 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB) return;
552 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
554 if (game.tholian.x != idx) {
556 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
557 while (game.tholian.x != idx) {
558 game.tholian.x += im;
559 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
562 else if (game.tholian.y != idy) {
564 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
565 while (game.tholian.y != idy) {
566 game.tholian.y += im;
567 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
570 game.quad[game.tholian.x][game.tholian.y] = IHT;
571 game.ks[game.nenhere] = game.tholian;
573 /* check to see if all holes plugged */
575 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT) return;
576 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT) return;
577 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT) return;
578 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT) return;
580 /* All plugged up -- Tholian splits */
581 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
583 crmena(true, IHT, sector, game.tholian);
584 prout(_(" completes web."));