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==0) 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()));
159 proutn("MOTION = %1.2f", motion);
160 proutn(" FORCES = %1.2f", forces);
163 /* don't move if no motion */
164 if (motion==0) return;
165 /* Limit motion according to skill */
166 if (abs(motion) > game.skill) motion = (motion < 0) ? -game.skill : game.skill;
168 /* calculate preferred number of steps */
169 nsteps = motion < 0 ? -motion : motion;
170 if (motion > 0 && nsteps > mdist) nsteps = mdist; /* don't overshoot */
171 if (nsteps > QUADSIZE) nsteps = QUADSIZE; /* This shouldn't be necessary */
172 if (nsteps < 1) nsteps = 1; /* This shouldn't be necessary */
175 prout("NSTEPS = %d", nsteps);
178 /* Compute preferred values of delta X and Y */
179 mx = game.sector.x - com.x;
180 my = game.sector.y - com.y;
181 if (2.0 * abs(mx) < abs(my)) mx = 0;
182 if (2.0 * abs(my) < abs(game.sector.x-com.x)) my = 0;
183 if (mx != 0) mx = mx*motion < 0 ? -1 : 1;
184 if (my != 0) my = my*motion < 0 ? -1 : 1;
187 for (ll = 0; ll < nsteps; ll++) {
193 /* Check if preferred position available */
196 krawlx = mx < 0 ? 1 : -1;
197 krawly = my < 0 ? 1 : -1;
199 attempts = 0; /* Settle mysterious hang problem */
200 while (attempts++ < 20 && !success) {
201 if (lookx < 1 || lookx > QUADSIZE) {
202 if (motion < 0 && tryexit(lookx, looky, ienm, loccom, irun))
204 if (krawlx == mx || my == 0) break;
205 lookx = next.x + krawlx;
208 else if (looky < 1 || looky > QUADSIZE) {
209 if (motion < 0 && tryexit(lookx, looky, ienm, loccom, irun))
211 if (krawly == my || mx == 0) break;
212 looky = next.y + krawly;
215 else if ((game.options & OPTION_RAMMING) && game.quad[lookx][looky] != IHDOT) {
216 /* See if we should ram ship */
217 if (game.quad[lookx][looky] == game.ship &&
218 (ienm == IHC || ienm == IHS)) {
222 if (krawlx != mx && my != 0) {
223 lookx = next.x + krawlx;
226 else if (krawly != my && mx != 0) {
227 looky = next.y + krawly;
230 else break; /* we have failed */
239 prout(cramlc(neither, next));
243 else break; /* done early */
245 /* Put commander in place within same quadrant */
246 game.quad[com.x][com.y] = IHDOT;
247 game.quad[next.x][next.y] = ienm;
248 if (next.x != com.x || next.y != com.y) {
250 game.ks[loccom].x = next.x;
251 game.ks[loccom].y = next.y;
252 game.kdist[loccom] = game.kavgd[loccom] =
253 sqrt(square(game.sector.x-next.x)+square(game.sector.y-next.y));
254 if (game.damage[DSRSENS] == 0 || game.condit == IHDOCKED) {
257 proutn(_(" from %s"), cramlc(2, com));
258 if (game.kdist[loccom] < dist1) proutn(_(" advances to "));
259 else proutn(_(" retreats to "));
260 prout(cramlc(sector, next));
271 if (game.idebug) prout("MOVCOM");
274 /* Figure out which Klingon is the commander (or Supercommander)
277 for_local_enemies(i) {
279 if (game.quad[w.x][w.y] == IHC) {
280 movebaddy(w, i, IHC);
285 for_local_enemies(i) {
287 if (game.quad[w.x][w.y] == IHS) {
288 movebaddy(w, i, IHS);
292 /* if skill level is high, move other Klingons and Romulans too!
293 Move these last so they can base their actions on what the
295 if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY))
296 for_local_enemies(i) {
298 if (game.quad[w.x][w.y] == IHK || game.quad[w.x][w.y] == IHR)
299 movebaddy(w, i, game.quad[w.x][w.y]);
305 static int movescom(coord iq, int flag, int *ipage)
309 if (same(iq, game.quadrant) || !VALID_QUADRANT(iq.x, iq.y) ||
310 game.state.galaxy[iq.x][iq.y].supernova ||
311 game.state.galaxy[iq.x][iq.y].klingons > 8)
314 /* Avoid quadrants with bases if we want to avoid Enterprise */
316 if (game.state.baseq[i].x==iq.x && game.state.baseq[i].y==iq.y) return 1;
318 if (game.justin && !game.iscate) return 1;
320 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons--;
321 game.state.kscmdr = iq;
322 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons++;
324 /* SC has scooted, Remove him from current quadrant */
331 if (game.quad[game.ks[i].x][game.ks[i].y] == IHS) break;
332 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT;
333 game.ks[i] = game.ks[game.nenhere];
334 game.kdist[i] = game.kdist[game.nenhere];
335 game.kavgd[i] = game.kavgd[game.nenhere];
336 game.kpower[i] = game.kpower[game.nenhere];
339 if (game.condit!=IHDOCKED) newcnd();
342 /* check for a helpful planet */
343 for (i = 0; i < game.inplan; i++) {
344 if (game.state.plnets[i].w.x==game.state.kscmdr.x && game.state.plnets[i].w.y==game.state.kscmdr.y &&
345 game.state.plnets[i].crystals == 1) {
346 /* destroy the planet */
347 DESTROY(&game.state.plnets[i]);
348 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NULL;
349 if (game.damage[DRADIO] == 0.0 || game.condit == IHDOCKED) {
350 if (*ipage==0) pause_game(1);
352 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
353 proutn(_(" a planet in "));
354 proutn(cramlc(quadrant, game.state.kscmdr));
355 prout(_(" has been destroyed"));
356 prout(_(" by the Super-commander.\""));
361 return 0; /* looks good! */
364 void scom(int *ipage)
366 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
368 int basetbl[BASEMAX+1];
369 double bdist[BASEMAX+1];
372 if (game.idebug) prout("SCOM");
375 /* Decide on being active or passive */
376 flag = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
377 (game.state.date-game.indate) < 3.0);
378 if (game.iscate==0 && flag) {
379 /* compute move away from Enterprise */
380 ideltax = game.state.kscmdr.x-game.quadrant.x;
381 ideltay = game.state.kscmdr.y-game.quadrant.y;
382 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
383 /* circulate in space */
384 ideltax = game.state.kscmdr.y-game.quadrant.y;
385 ideltay = game.quadrant.x-game.state.kscmdr.x;
389 /* compute distances to starbases */
390 if (game.state.rembase <= 0) {
391 /* nothing left to do */
395 sc = game.state.kscmdr;
398 ibq.x = game.state.baseq[i].x;
399 ibq.y = game.state.baseq[i].y;
400 bdist[i] = sqrt(square(ibq.x-sc.x) + square(ibq.y-sc.y));
402 if (game.state.rembase > 1) {
403 /* sort into nearest first order */
407 for (i=1; i < game.state.rembase-1; i++) {
408 if (bdist[i] > bdist[i+1]) {
411 bdist[i] = bdist[i+1];
413 basetbl[i] = basetbl[i+1];
420 /* look for nearest base without a commander, no Enterprise, and
421 without too many Klingons, and not already under attack. */
422 ifindit = iwhichb = 0;
425 i = basetbl[i2]; /* bug in original had it not finding nearest*/
426 ibq = game.state.baseq[i];
427 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
428 game.state.galaxy[ibq.x][ibq.y].supernova ||
429 game.state.galaxy[ibq.x][ibq.y].klingons > 8)
431 /* if there is a commander, an no other base is appropriate,
432 we will take the one with the commander */
434 if (ibq.x==game.state.kcmdr[j].x && ibq.y==game.state.kcmdr[j].y && ifindit!= 2) {
440 if (j > game.state.remcom) { /* no commander -- use this one */
446 if (ifindit==0) return; /* Nothing suitable -- wait until next time*/
447 ibq = game.state.baseq[iwhichb];
448 /* decide how to move toward base */
449 ideltax = ibq.x - game.state.kscmdr.x;
450 ideltay = ibq.y - game.state.kscmdr.y;
452 /* Maximum movement is 1 quadrant in either or both axis */
453 if (ideltax > 1) ideltax = 1;
454 if (ideltax < -1) ideltax = -1;
455 if (ideltay > 1) ideltay = 1;
456 if (ideltay < -1) ideltay = -1;
458 /* try moving in both x and y directions */
459 iq.x = game.state.kscmdr.x + ideltax;
460 iq.y = game.state.kscmdr.y + ideltax;
461 if (movescom(iq, flag, ipage)) {
462 /* failed -- try some other maneuvers */
463 if (ideltax==0 || ideltay==0) {
464 /* attempt angle move */
466 iq.y = game.state.kscmdr.y + 1;
467 if (movescom(iq, flag, ipage)) {
468 iq.y = game.state.kscmdr.y - 1;
469 movescom(iq, flag, ipage);
473 iq.x = game.state.kscmdr.x + 1;
474 if (movescom(iq, flag, ipage)) {
475 iq.x = game.state.kscmdr.x - 1;
476 movescom(iq, flag, ipage);
481 /* try moving just in x or y */
482 iq.y = game.state.kscmdr.y;
483 if (movescom(iq, flag, ipage)) {
484 iq.y = game.state.kscmdr.y + ideltay;
485 iq.x = game.state.kscmdr.x;
486 movescom(iq, flag, ipage);
490 /* check for a base */
491 if (game.state.rembase == 0) {
494 else for_starbases(i) {
495 ibq = game.state.baseq[i];
496 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
497 /* attack the base */
498 if (flag) return; /* no, don't attack base! */
501 schedule(FSCDBAS, 1.0 +2.0*Rand());
502 if (is_scheduled(FCDBAS))
503 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
504 if (game.damage[DRADIO] > 0 && game.condit != IHDOCKED)
505 return; /* no warning */
507 if (*ipage == 0) pause_game(1);
509 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
510 proutn(cramlc(quadrant, game.state.kscmdr));
512 prout(_(" reports that it is under attack from the Klingon Super-commander."));
513 proutn(_(" It can survive until stardate %d.\""),
514 (int)scheduled(FSCDBAS));
515 if (game.resting==0) return;
516 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
519 game.optime = 0.0; /* actually finished */
523 /* Check for intelligence report */
529 (game.damage[DRADIO] > 0.0 && game.condit != IHDOCKED) ||
530 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
532 if (*ipage==0) pause_game(1);
534 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
535 proutn(_(" the Super-commander is in "));
536 proutn(cramlc(quadrant, game.state.kscmdr));
545 /* Move the Tholian */
546 if (game.ithere==0 || game.justin == 1) return;
548 if (game.tholian.x == 1 && game.tholian.y == 1) {
549 idx = 1; idy = QUADSIZE;
551 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
552 idx = QUADSIZE; idy = QUADSIZE;
554 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
555 idx = QUADSIZE; idy = 1;
557 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
561 /* something is wrong! */
566 /* Do nothing if we are blocked */
567 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB) return;
568 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
570 if (game.tholian.x != idx) {
572 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
573 while (game.tholian.x != idx) {
574 game.tholian.x += im;
575 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
578 else if (game.tholian.y != idy) {
580 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
581 while (game.tholian.y != idy) {
582 game.tholian.y += im;
583 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
586 game.quad[game.tholian.x][game.tholian.y] = IHT;
587 game.ks[game.nenhere] = game.tholian;
589 /* check to see if all holes plugged */
591 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT) return;
592 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT) return;
593 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT) return;
594 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT) return;
596 /* All plugged up -- Tholian splits */
597 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
598 dropin(IHBLANK, &dummy);
599 crmena(1,IHT, 2, game.tholian);
600 prout(_(" completes web."));
601 game.ithere = game.tholian.x = game.tholian.y = 0;