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 */
20 for (n = 1; n <= game.state.remcom; n++)
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);
62 for (n = 1; n <= game.state.remcom; n++) {
63 if (same(game.state.kcmdr[n], game.quadrant)) {
64 game.state.kcmdr[n]=iq;
70 return true; /* success */
73 /*************************************************************************
74 The bad-guy movement algorithm:
76 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
77 If both are operating full strength, force is 1000. If both are damaged,
78 force is -1000. Having shields down subtracts an additional 1000.
80 2. Enemy has forces equal to the energy of the attacker plus
81 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
82 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
84 Attacker Initial energy levels (nominal):
85 Klingon Romulan Commander Super-Commander
88 Good 450 800 1300 1750
89 Expert 475 850 1350 1875
90 Emeritus 500 900 1400 2000
91 VARIANCE 75 200 200 200
93 Enemy vessels only move prior to their attack. In Novice - Good games
94 only commanders move. In Expert games, all enemy vessels move if there
95 is a commander present. In Emeritus games all enemy vessels move.
97 3. If Enterprise is not docked, an agressive action is taken if enemy
98 forces are 1000 greater than Enterprise.
100 Agressive action on average cuts the distance between the ship and
101 the enemy to 1/4 the original.
103 4. At lower energy advantage, movement units are proportional to the
104 advantage with a 650 advantage being to hold ground, 800 to move forward
105 1, 950 for two, 150 for back 4, etc. Variance of 100.
107 If docked, is reduced by roughly 1.75*game.skill, generally forcing a
108 retreat, especially at high skill levels.
110 5. Motion is limited to skill level, except for SC hi-tailing it out.
111 **************************************************************************/
113 static void movebaddy(coord com, int loccom, feature ienm)
114 /* tactical movement for the bad guys */
116 int motion, mdist, nsteps, mx, my, ll;
119 bool success, irun = false;
121 /* This should probably be just game.comhere + game.ishere */
122 int nbaddys = game.skill >= SKILL_EXPERT ?
123 (int)((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0):
124 (game.comhere + game.ishere);
125 double dist1, forces;
127 dist1 = game.kdist[loccom];
128 mdist = dist1 + 0.5; /* Nearest integer distance */
130 /* If SC, check with spy to see if should hi-tail it */
132 (game.kpower[loccom] <= 500.0 || (game.condition==docked && !damaged(DPHOTON)))) {
137 /* decide whether to advance, retreat, or hold position */
138 forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1);
140 forces += 1000; /* Good for enemy if shield is down! */
141 if (!damaged(DPHASER) || !damaged(DPHOTON)) {
142 if (damaged(DPHASER)) /* phasers damaged */
145 forces -= 0.2*(game.energy - 2500.0);
146 if (damaged(DPHOTON)) /* photon torpedoes damaged */
149 forces -= 50.0*game.torps;
152 /* phasers and photon tubes both out! */
156 if (forces <= 1000.0 && game.condition != docked) /* Typical situation */
157 motion = ((forces+200.0*Rand())/150.0) - 5.0;
159 if (forces > 1000.0) /* Very strong -- move in for kill */
160 motion = (1.0-square(Rand()))*dist1 + 1.0;
161 if (game.condition==docked && (game.options & OPTION_BASE)) /* protected by base -- back off ! */
162 motion -= game.skill*(2.0-square(Rand()));
165 proutn("=== MOTION = %d, FORCES = %1.2f, ", motion, forces);
166 /* don't move if no motion */
169 /* Limit motion according to skill */
170 if (abs(motion) > game.skill)
171 motion = (motion < 0) ? -game.skill : game.skill;
173 /* calculate preferred number of steps */
174 nsteps = motion < 0 ? -motion : motion;
175 if (motion > 0 && nsteps > mdist)
176 nsteps = mdist; /* don't overshoot */
177 if (nsteps > QUADSIZE)
178 nsteps = QUADSIZE; /* This shouldn't be necessary */
180 nsteps = 1; /* This shouldn't be necessary */
182 proutn("NSTEPS = %d:", nsteps);
184 /* Compute preferred values of delta X and Y */
185 mx = game.sector.x - com.x;
186 my = game.sector.y - com.y;
187 if (2.0 * abs(mx) < abs(my))
189 if (2.0 * abs(my) < abs(game.sector.x-com.x))
192 mx = mx*motion < 0 ? -1 : 1;
194 my = my*motion < 0 ? -1 : 1;
197 for (ll = 0; ll < nsteps; ll++) {
200 /* Check if preferred position available */
201 look.x = next.x + mx;
202 look.y = next.y + my;
203 krawlx = mx < 0 ? 1 : -1;
204 krawly = my < 0 ? 1 : -1;
206 attempts = 0; /* Settle mysterious hang problem */
207 while (attempts++ < 20 && !success) {
208 if (look.x < 1 || look.x > QUADSIZE) {
209 if (motion < 0 && tryexit(look, ienm, loccom, irun))
211 if (krawlx == mx || my == 0)
213 look.x = next.x + krawlx;
216 else if (look.y < 1 || look.y > QUADSIZE) {
217 if (motion < 0 && tryexit(look, ienm, loccom, irun))
219 if (krawly == my || mx == 0)
221 look.y = next.y + krawly;
224 else if ((game.options & OPTION_RAMMING) && game.quad[look.x][look.y] != IHDOT) {
225 /* See if we should ram ship */
226 if (game.quad[look.x][look.y] == game.ship &&
227 (ienm == IHC || ienm == IHS)) {
228 ram(true, ienm, com);
231 if (krawlx != mx && my != 0) {
232 look.x = next.x + krawlx;
235 else if (krawly != my && mx != 0) {
236 look.y = next.y + krawly;
240 break; /* we have failed */
248 proutn(cramlc(neither, next));
251 break; /* done early */
256 /* Put commander in place within same quadrant */
257 game.quad[com.x][com.y] = IHDOT;
258 game.quad[next.x][next.y] = ienm;
259 if (!same(next, com)) {
261 game.ks[loccom] = next;
262 game.kdist[loccom] = game.kavgd[loccom] = distance(game.sector, next);
263 if (!damaged(DSRSENS) || game.condition == docked) {
266 proutn(_(" from %s"), cramlc(sector, com));
267 if (game.kdist[loccom] < dist1)
268 proutn(_(" advances to "));
270 proutn(_(" retreats to "));
271 prout(cramlc(sector, next));
276 void moveklings(void)
277 /* Klingon tactical movement */
285 // Figure out which Klingon is the commander (or Supercommander)
288 for (i = 1; i <= game.nenhere; i++) {
290 if (game.quad[w.x][w.y] == IHC) {
291 movebaddy(w, i, IHC);
296 for (i = 1; i <= game.nenhere; i++) {
298 if (game.quad[w.x][w.y] == IHS) {
299 movebaddy(w, i, IHS);
303 // if skill level is high, move other Klingons and Romulans too!
304 // Move these last so they can base their actions on what the
306 if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY))
307 for (i = 1; i <= game.nenhere; i++) {
309 if (game.quad[w.x][w.y] == IHK || game.quad[w.x][w.y] == IHR)
310 movebaddy(w, i, game.quad[w.x][w.y]);
316 static bool movescom(coord iq, bool avoid)
317 /* commander movement helper */
321 if (same(iq, game.quadrant) || !VALID_QUADRANT(iq.x, iq.y) ||
322 game.state.galaxy[iq.x][iq.y].supernova ||
323 game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1)
326 /* Avoid quadrants with bases if we want to avoid Enterprise */
327 for (i = 1; i <= game.state.rembase; i++)
328 if (same(game.state.baseq[i], iq))
331 if (game.justin && !game.iscate)
334 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons--;
335 game.state.kscmdr = iq;
336 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons++;
338 /* SC has scooted, Remove him from current quadrant */
342 game.ientesc = false;
344 for (i = 1; i <= game.nenhere; i++)
345 if (game.quad[game.ks[i].x][game.ks[i].y] == IHS)
347 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT;
348 game.ks[i] = game.ks[game.nenhere];
349 game.kdist[i] = game.kdist[game.nenhere];
350 game.kavgd[i] = game.kavgd[game.nenhere];
351 game.kpower[i] = game.kpower[game.nenhere];
354 if (game.condition!=docked)
358 /* check for a helpful planet */
359 for (i = 0; i < game.inplan; i++) {
360 if (same(game.state.planets[i].w, game.state.kscmdr) &&
361 game.state.planets[i].crystals == present) {
362 /* destroy the planet */
363 game.state.planets[i].pclass = destroyed;
364 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET;
365 if (!damaged(DRADIO) || game.condition == docked) {
367 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
368 proutn(_(" a planet in "));
369 proutn(cramlc(quadrant, game.state.kscmdr));
370 prout(_(" has been destroyed"));
371 prout(_(" by the Super-commander.\""));
376 return false; /* looks good! */
379 void supercommander(void)
380 /* move the Super Commander */
382 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
384 int basetbl[BASEMAX+1];
385 double bdist[BASEMAX+1];
389 prout("== SUPERCOMMANDER");
391 /* Decide on being active or passive */
392 avoid = ((game.incom - game.state.remcom + game.inkling - game.state.remkl)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
393 (game.state.date-game.indate) < 3.0);
394 if (!game.iscate && avoid) {
395 /* compute move away from Enterprise */
396 ideltax = game.state.kscmdr.x-game.quadrant.x;
397 ideltay = game.state.kscmdr.y-game.quadrant.y;
398 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
399 /* circulate in space */
400 ideltax = game.state.kscmdr.y-game.quadrant.y;
401 ideltay = game.quadrant.x-game.state.kscmdr.x;
405 /* compute distances to starbases */
406 if (game.state.rembase <= 0) {
407 /* nothing left to do */
411 sc = game.state.kscmdr;
412 for (i = 1; i <= game.state.rembase; i++) {
414 bdist[i] = distance(game.state.baseq[i], sc);
416 if (game.state.rembase > 1) {
417 /* sort into nearest first order */
421 for (i=1; i < game.state.rembase-1; i++) {
422 if (bdist[i] > bdist[i+1]) {
425 bdist[i] = bdist[i+1];
427 basetbl[i] = basetbl[i+1];
434 /* look for nearest base without a commander, no Enterprise, and
435 without too many Klingons, and not already under attack. */
436 ifindit = iwhichb = 0;
438 for (i2 = 1; i2 <= game.state.rembase; i2++) {
439 i = basetbl[i2]; /* bug in original had it not finding nearest*/
440 ibq = game.state.baseq[i];
441 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
442 game.state.galaxy[ibq.x][ibq.y].supernova ||
443 game.state.galaxy[ibq.x][ibq.y].klingons > MAXKLQUAD-1)
445 // if there is a commander, and no other base is appropriate,
446 // we will take the one with the commander
447 for (j = 1; j <= game.state.remcom; j++) {
448 if (same(ibq, game.state.kcmdr[j]) && ifindit!= 2) {
454 if (j > game.state.remcom) { /* no commander -- use this one */
461 return; /* Nothing suitable -- wait until next time*/
462 ibq = game.state.baseq[iwhichb];
463 /* decide how to move toward base */
464 ideltax = ibq.x - game.state.kscmdr.x;
465 ideltay = ibq.y - game.state.kscmdr.y;
467 /* Maximum movement is 1 quadrant in either or both axis */
477 /* try moving in both x and y directions */
478 iq.x = game.state.kscmdr.x + ideltax;
479 iq.y = game.state.kscmdr.y + ideltax;
480 if (movescom(iq, avoid)) {
481 /* failed -- try some other maneuvers */
482 if (ideltax==0 || ideltay==0) {
483 /* attempt angle move */
485 iq.y = game.state.kscmdr.y + 1;
486 if (movescom(iq, avoid)) {
487 iq.y = game.state.kscmdr.y - 1;
492 iq.x = game.state.kscmdr.x + 1;
493 if (movescom(iq, avoid)) {
494 iq.x = game.state.kscmdr.x - 1;
500 /* try moving just in x or y */
501 iq.y = game.state.kscmdr.y;
502 if (movescom(iq, avoid)) {
503 iq.y = game.state.kscmdr.y + ideltay;
504 iq.x = game.state.kscmdr.x;
509 /* check for a base */
510 if (game.state.rembase == 0) {
514 for (i = 1; i <= game.state.rembase; i++) {
515 ibq = game.state.baseq[i];
516 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
517 /* attack the base */
519 return; /* no, don't attack base! */
520 game.iseenit = false;
522 schedule(FSCDBAS, 1.0 +2.0*Rand());
523 if (is_scheduled(FCDBAS))
524 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
525 if (damaged(DRADIO) && game.condition != docked)
526 return; /* no warning */
529 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
530 proutn(cramlc(quadrant, game.state.kscmdr));
532 prout(_(" reports that it is under attack from the Klingon Super-commander."));
533 proutn(_(" It can survive until stardate %d.\""),
534 (int)scheduled(FSCDBAS));
537 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
540 game.resting = false;
541 game.optime = 0.0; /* actually finished */
546 /* Check for intelligence report */
550 (damaged(DRADIO) && game.condition != docked) ||
551 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
554 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
555 proutn(_(" the Super-commander is in "));
556 proutn(cramlc(quadrant, game.state.kscmdr));
561 void movetholian(void)
562 /* move the Tholian */
565 if (!game.ithere || game.justin)
568 if (game.tholian.x == 1 && game.tholian.y == 1) {
569 idx = 1; idy = QUADSIZE;
571 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
572 idx = QUADSIZE; idy = QUADSIZE;
574 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
575 idx = QUADSIZE; idy = 1;
577 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
581 /* something is wrong! */
586 /* do nothing if we are blocked */
587 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB)
589 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
591 if (game.tholian.x != idx) {
593 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
594 while (game.tholian.x != idx) {
595 game.tholian.x += im;
596 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT)
597 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
600 else if (game.tholian.y != idy) {
602 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
603 while (game.tholian.y != idy) {
604 game.tholian.y += im;
605 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT)
606 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
609 game.quad[game.tholian.x][game.tholian.y] = IHT;
610 game.ks[game.nenhere] = game.tholian;
612 /* check to see if all holes plugged */
613 for (i = 1; i <= QUADSIZE; i++) {
614 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT)
616 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT)
618 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT)
620 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT)
623 /* All plugged up -- Tholian splits */
624 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
626 crmena(true, IHT, sector, game.tholian);
627 prout(_(" completes web."));