3 static int tryexit(int lookx, int looky, int ienm, int loccom, int irun)
8 iq.x = game.quadrant.x+(lookx+(QUADSIZE-1))/QUADSIZE - 1;
9 iq.y = game.quadrant.y+(looky+(QUADSIZE-1))/QUADSIZE - 1;
10 if (!VALID_QUADRANT(iq.x,iq.y) ||
11 game.state.galaxy[iq.x][iq.y].supernova ||
12 game.state.galaxy[iq.x][iq.y].klingons > 8)
13 return 0; /* no can do -- neg energy, supernovae, or >8 Klingons */
14 if (ienm == IHR) return 0; /* Romulans cannot escape! */
16 /* avoid intruding on another commander's territory */
19 if (same(game.state.kcmdr[l],iq)) return 0;
20 /* refuse to leave if currently attacking starbase */
21 if (same(game.battle, game.quadrant)) return 0;
23 /* don't leave if over 1000 units of energy */
24 if (game.kpower[loccom] > 1000.) return 0;
26 /* print escape message and move out of quadrant.
27 We know this if either short or long range sensors are working */
28 if (game.damage[DSRSENS] == 0.0 || game.damage[DLRSENS] == 0.0 ||
29 game.condit == IHDOCKED) {
30 crmena(1, ienm, 2, game.ks[loccom]);
31 prout(_(" escapes to %s (and regains strength)."),
32 cramlc(quadrant, iq));
34 /* handle local matters related to escape */
35 game.quad[game.ks[loccom].x][game.ks[loccom].y] = IHDOT;
36 game.ks[loccom] = game.ks[game.nenhere];
37 game.kavgd[loccom] = game.kavgd[game.nenhere];
38 game.kpower[loccom] = game.kpower[game.nenhere];
39 game.kdist[loccom] = game.kdist[game.nenhere];
42 if (game.condit != IHDOCKED) newcnd();
43 /* Handle global matters related to escape */
44 game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons--;
45 game.state.galaxy[iq.x][iq.y].klingons++;
51 schedule(FSCMOVE, 0.2777);
53 game.state.kscmdr.x=iq.x;
54 game.state.kscmdr.y=iq.y;
58 if (same(game.state.kcmdr[l], game.quadrant)) {
59 game.state.kcmdr[l]=iq;
65 return 1; /* success */
69 static void movebaddy(coord com, int loccom, int ienm)
71 int motion, mdist, nsteps, mx, my, lookx, looky, ll;
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.condit==IHDOCKED && game.damage[DPHOTON]==0))) {
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 (game.damage[DPHASER] == 0.0 || game.damage[DPHOTON] == 0.0) {
135 if (game.damage[DPHASER] != 0) /* phasers damaged */
138 forces -= 0.2*(game.energy - 2500.0);
139 if (game.damage[DPHOTON] != 0) /* photon torpedoes damaged */
142 forces -= 50.0*game.torps;
145 /* phasers and photon tubes both out! */
149 if (forces <= 1000.0 && game.condit != IHDOCKED) /* 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.condit==IHDOCKED && (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 */
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 (lookx < 1 || lookx > QUADSIZE) {
193 if (motion < 0 && tryexit(lookx, looky, ienm, loccom, irun))
195 if (krawlx == mx || my == 0) break;
196 lookx = next.x + krawlx;
199 else if (looky < 1 || looky > QUADSIZE) {
200 if (motion < 0 && tryexit(lookx, looky, ienm, loccom, irun))
202 if (krawly == my || mx == 0) break;
203 looky = next.y + krawly;
206 else if ((game.options & OPTION_RAMMING) && game.quad[lookx][looky] != IHDOT) {
207 /* See if we should ram ship */
208 if (game.quad[lookx][looky] == game.ship &&
209 (ienm == IHC || ienm == IHS)) {
213 if (krawlx != mx && my != 0) {
214 lookx = next.x + krawlx;
217 else if (krawly != my && mx != 0) {
218 looky = next.y + krawly;
221 else break; /* we have failed */
229 proutn(cramlc(neither, next));
231 else break; /* done early */
236 /* Put commander in place within same quadrant */
237 game.quad[com.x][com.y] = IHDOT;
238 game.quad[next.x][next.y] = ienm;
239 if (next.x != com.x || next.y != com.y) {
241 game.ks[loccom].x = next.x;
242 game.ks[loccom].y = next.y;
243 game.kdist[loccom] = game.kavgd[loccom] =
244 sqrt(square(game.sector.x-next.x)+square(game.sector.y-next.y));
245 if (game.damage[DSRSENS] == 0 || 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));
261 if (idebug) prout("== MOVCOM");
263 /* Figure out which Klingon is the commander (or Supercommander)
266 for_local_enemies(i) {
268 if (game.quad[w.x][w.y] == IHC) {
269 movebaddy(w, i, IHC);
274 for_local_enemies(i) {
276 if (game.quad[w.x][w.y] == IHS) {
277 movebaddy(w, i, IHS);
281 /* if skill level is high, move other Klingons and Romulans too!
282 Move these last so they can base their actions on what the
284 if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY))
285 for_local_enemies(i) {
287 if (game.quad[w.x][w.y] == IHK || game.quad[w.x][w.y] == IHR)
288 movebaddy(w, i, game.quad[w.x][w.y]);
294 static bool movescom(coord iq, int flag, int *ipage)
298 if (same(iq, game.quadrant) || !VALID_QUADRANT(iq.x, iq.y) ||
299 game.state.galaxy[iq.x][iq.y].supernova ||
300 game.state.galaxy[iq.x][iq.y].klingons > 8)
303 /* Avoid quadrants with bases if we want to avoid Enterprise */
305 if (game.state.baseq[i].x==iq.x && game.state.baseq[i].y==iq.y) return 1;
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 */
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.condit!=IHDOCKED) newcnd();
331 /* check for a helpful planet */
332 for (i = 0; i < game.inplan; i++) {
333 if (game.state.plnets[i].w.x==game.state.kscmdr.x && game.state.plnets[i].w.y==game.state.kscmdr.y &&
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 (game.damage[DRADIO] == 0.0 || game.condit == IHDOCKED) {
339 if (*ipage==0) pause_game(1);
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(int *ipage)
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 ibq.x = game.state.baseq[i].x;
387 ibq.y = game.state.baseq[i].y;
388 bdist[i] = sqrt(square(ibq.x-sc.x) + square(ibq.y-sc.y));
390 if (game.state.rembase > 1) {
391 /* sort into nearest first order */
395 for (i=1; i < game.state.rembase-1; i++) {
396 if (bdist[i] > bdist[i+1]) {
399 bdist[i] = bdist[i+1];
401 basetbl[i] = basetbl[i+1];
408 /* look for nearest base without a commander, no Enterprise, and
409 without too many Klingons, and not already under attack. */
410 ifindit = iwhichb = 0;
413 i = basetbl[i2]; /* bug in original had it not finding nearest*/
414 ibq = game.state.baseq[i];
415 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
416 game.state.galaxy[ibq.x][ibq.y].supernova ||
417 game.state.galaxy[ibq.x][ibq.y].klingons > 8)
419 /* if there is a commander, an no other base is appropriate,
420 we will take the one with the commander */
422 if (ibq.x==game.state.kcmdr[j].x && ibq.y==game.state.kcmdr[j].y && ifindit!= 2) {
428 if (j > game.state.remcom) { /* no commander -- use this one */
434 if (ifindit==0) return; /* Nothing suitable -- wait until next time*/
435 ibq = game.state.baseq[iwhichb];
436 /* decide how to move toward base */
437 ideltax = ibq.x - game.state.kscmdr.x;
438 ideltay = ibq.y - game.state.kscmdr.y;
440 /* Maximum movement is 1 quadrant in either or both axis */
441 if (ideltax > 1) ideltax = 1;
442 if (ideltax < -1) ideltax = -1;
443 if (ideltay > 1) ideltay = 1;
444 if (ideltay < -1) ideltay = -1;
446 /* try moving in both x and y directions */
447 iq.x = game.state.kscmdr.x + ideltax;
448 iq.y = game.state.kscmdr.y + ideltax;
449 if (movescom(iq, flag, ipage)) {
450 /* failed -- try some other maneuvers */
451 if (ideltax==0 || ideltay==0) {
452 /* attempt angle move */
454 iq.y = game.state.kscmdr.y + 1;
455 if (movescom(iq, flag, ipage)) {
456 iq.y = game.state.kscmdr.y - 1;
457 movescom(iq, flag, ipage);
461 iq.x = game.state.kscmdr.x + 1;
462 if (movescom(iq, flag, ipage)) {
463 iq.x = game.state.kscmdr.x - 1;
464 movescom(iq, flag, ipage);
469 /* try moving just in x or y */
470 iq.y = game.state.kscmdr.y;
471 if (movescom(iq, flag, ipage)) {
472 iq.y = game.state.kscmdr.y + ideltay;
473 iq.x = game.state.kscmdr.x;
474 movescom(iq, flag, ipage);
478 /* check for a base */
479 if (game.state.rembase == 0) {
482 else for_starbases(i) {
483 ibq = game.state.baseq[i];
484 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
485 /* attack the base */
486 if (flag) return; /* no, don't attack base! */
489 schedule(FSCDBAS, 1.0 +2.0*Rand());
490 if (is_scheduled(FCDBAS))
491 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
492 if (game.damage[DRADIO] > 0 && game.condit != IHDOCKED)
493 return; /* no warning */
495 if (*ipage == 0) pause_game(1);
497 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
498 proutn(cramlc(quadrant, game.state.kscmdr));
500 prout(_(" reports that it is under attack from the Klingon Super-commander."));
501 proutn(_(" It can survive until stardate %d.\""),
502 (int)scheduled(FSCDBAS));
503 if (!game.resting) return;
504 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
506 game.resting = false;
507 game.optime = 0.0; /* actually finished */
511 /* Check for intelligence report */
515 (game.damage[DRADIO] > 0.0 && game.condit != IHDOCKED) ||
516 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
518 if (*ipage==0) pause_game(1);
520 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
521 proutn(_(" the Super-commander is in "));
522 proutn(cramlc(quadrant, game.state.kscmdr));
531 /* Move the Tholian */
532 if (game.ithere==0 || game.justin == 1) return;
534 if (game.tholian.x == 1 && game.tholian.y == 1) {
535 idx = 1; idy = QUADSIZE;
537 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
538 idx = QUADSIZE; idy = QUADSIZE;
540 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
541 idx = QUADSIZE; idy = 1;
543 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
547 /* something is wrong! */
552 /* Do nothing if we are blocked */
553 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB) return;
554 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
556 if (game.tholian.x != idx) {
558 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
559 while (game.tholian.x != idx) {
560 game.tholian.x += im;
561 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
564 else if (game.tholian.y != idy) {
566 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
567 while (game.tholian.y != idy) {
568 game.tholian.y += im;
569 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
572 game.quad[game.tholian.x][game.tholian.y] = IHT;
573 game.ks[game.nenhere] = game.tholian;
575 /* check to see if all holes plugged */
577 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT) return;
578 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT) return;
579 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT) return;
580 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT) return;
582 /* All plugged up -- Tholian splits */
583 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
584 dropin(IHBLANK, &dummy);
585 crmena(1,IHT, 2, game.tholian);
586 prout(_(" completes web."));
587 game.ithere = game.tholian.x = game.tholian.y = 0;