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.x = game.state.baseq[i].x;
427 ibq.y = game.state.baseq[i].y;
428 if ((ibq.x == game.quadrant.x && ibq.y == game.quadrant.y) ||
429 (ibq.x == game.battle.x && ibq.y == game.battle.y) ||
430 game.state.galaxy[ibq.x][ibq.y].supernova ||
431 game.state.galaxy[ibq.x][ibq.y].klingons > 8)
433 /* if there is a commander, an no other base is appropriate,
434 we will take the one with the commander */
436 if (ibq.x==game.state.kcmdr[j].x && ibq.y==game.state.kcmdr[j].y && ifindit!= 2) {
442 if (j > game.state.remcom) { /* no commander -- use this one */
448 if (ifindit==0) return; /* Nothing suitable -- wait until next time*/
449 ibq.x = game.state.baseq[iwhichb].x;
450 ibq.y = game.state.baseq[iwhichb].y;
451 /* decide how to move toward base */
452 ideltax = ibq.x - game.state.kscmdr.x;
453 ideltay = ibq.y - game.state.kscmdr.y;
455 /* Maximum movement is 1 quadrant in either or both axis */
456 if (ideltax > 1) ideltax = 1;
457 if (ideltax < -1) ideltax = -1;
458 if (ideltay > 1) ideltay = 1;
459 if (ideltay < -1) ideltay = -1;
461 /* try moving in both x and y directions */
462 iq.x = game.state.kscmdr.x + ideltax;
463 iq.y = game.state.kscmdr.y + ideltax;
464 if (movescom(iq, flag, ipage)) {
465 /* failed -- try some other maneuvers */
466 if (ideltax==0 || ideltay==0) {
467 /* attempt angle move */
469 iq.y = game.state.kscmdr.y + 1;
470 if (movescom(iq, flag, ipage)) {
471 iq.y = game.state.kscmdr.y - 1;
472 movescom(iq, flag, ipage);
476 iq.x = game.state.kscmdr.x + 1;
477 if (movescom(iq, flag, ipage)) {
478 iq.x = game.state.kscmdr.x - 1;
479 movescom(iq, flag, ipage);
484 /* try moving just in x or y */
485 iq.y = game.state.kscmdr.y;
486 if (movescom(iq, flag, ipage)) {
487 iq.y = game.state.kscmdr.y + ideltay;
488 iq.x = game.state.kscmdr.x;
489 movescom(iq, flag, ipage);
493 /* check for a base */
494 if (game.state.rembase == 0) {
497 else for_starbases(i) {
498 ibq = game.state.baseq[i];
499 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
500 /* attack the base */
501 if (flag) return; /* no, don't attack base! */
504 schedule(FSCDBAS, 1.0 +2.0*Rand());
505 if (is_scheduled(FCDBAS))
506 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
507 if (game.damage[DRADIO] > 0 && game.condit != IHDOCKED)
508 return; /* no warning */
510 if (*ipage == 0) pause_game(1);
512 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
513 proutn(cramlc(quadrant, game.state.kscmdr));
515 prout(_(" reports that it is under attack from the Klingon Super-commander."));
516 proutn(_(" It can survive until stardate %d.\""),
517 (int)scheduled(FSCDBAS));
518 if (game.resting==0) return;
519 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
522 game.optime = 0.0; /* actually finished */
526 /* Check for intelligence report */
532 (game.damage[DRADIO] > 0.0 && game.condit != IHDOCKED) ||
533 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
535 if (*ipage==0) pause_game(1);
537 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
538 proutn(_(" the Super-commander is in "));
539 proutn(cramlc(quadrant, game.state.kscmdr));
548 /* Move the Tholian */
549 if (game.ithere==0 || game.justin == 1) return;
551 if (game.tholian.x == 1 && game.tholian.y == 1) {
552 idx = 1; idy = QUADSIZE;
554 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
555 idx = QUADSIZE; idy = QUADSIZE;
557 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
558 idx = QUADSIZE; idy = 1;
560 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
564 /* something is wrong! */
569 /* Do nothing if we are blocked */
570 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB) return;
571 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
573 if (game.tholian.x != idx) {
575 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
576 while (game.tholian.x != idx) {
577 game.tholian.x += im;
578 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
581 else if (game.tholian.y != idy) {
583 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
584 while (game.tholian.y != idy) {
585 game.tholian.y += im;
586 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT) game.quad[game.tholian.x][game.tholian.y] = IHWEB;
589 game.quad[game.tholian.x][game.tholian.y] = IHT;
590 game.ks[game.nenhere].x=game.tholian.x;
591 game.ks[game.nenhere].y=game.tholian.y;
593 /* check to see if all holes plugged */
595 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT) return;
596 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT) return;
597 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT) return;
598 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT) return;
600 /* All plugged up -- Tholian splits */
601 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
602 dropin(IHBLANK, &dummy);
603 crmena(1,IHT, 2, game.tholian);
604 prout(_(" completes web."));
605 game.ithere = game.tholian.x = game.tholian.y = 0;