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 */
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))
22 /* refuse to leave if currently attacking starbase */
23 if (same(game.battle, game.quadrant))
26 /* don't leave if over 1000 units of energy */
27 if (game.kpower[loccom] > 1000.0)
30 /* print escape message and move out of quadrant.
31 We know this if either short or long range sensors are working */
32 if (!damaged(DSRSENS) || !damaged(DLRSENS) ||
33 game.condition == docked) {
34 crmena(true, ienm, sector, game.ks[loccom]);
35 prout(_(" escapes to %s (and regains strength)."),
36 cramlc(quadrant, iq));
38 /* handle local matters related to escape */
39 game.quad[game.ks[loccom].x][game.ks[loccom].y] = IHDOT;
40 game.ks[loccom] = game.ks[game.nenhere];
41 game.kavgd[loccom] = game.kavgd[game.nenhere];
42 game.kpower[loccom] = game.kpower[game.nenhere];
43 game.kdist[loccom] = game.kdist[game.nenhere];
46 if (game.condition != docked)
48 /* Handle global matters related to escape */
49 game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons--;
50 game.state.galaxy[iq.x][iq.y].klingons++;
56 schedule(FSCMOVE, 0.2777);
62 if (same(game.state.kcmdr[n], game.quadrant)) {
63 game.state.kcmdr[n]=iq;
69 return true; /* success */
73 static void movebaddy(coord com, int loccom, feature ienm)
74 /* tactical movement for the bad guys */
76 int motion, mdist, nsteps, mx, my, ll;
79 bool success, irun = false;
81 /* This should probably be just game.comhere + game.ishere */
82 int nbaddys = game.skill >= SKILL_EXPERT ?
83 (int)((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0):
84 (game.comhere + game.ishere);
87 dist1 = game.kdist[loccom];
88 mdist = dist1 + 0.5; /* Nearest integer distance */
90 /* If SC, check with spy to see if should hi-tail it */
92 (game.kpower[loccom] <= 500.0 || (game.condition==docked && !damaged(DPHOTON)))) {
97 /* decide whether to advance, retreat, or hold position */
99 * Enterprise has "force" based on condition of phaser and photon torpedoes.
100 If both are operating full strength, force is 1000. If both are damaged,
101 force is -1000. Having shields down subtracts an additional 1000.
103 * Enemy has forces equal to the energy of the attacker plus
104 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
105 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
107 Attacker Initial energy levels (nominal):
108 Klingon Romulan Commander Super-Commander
111 Good 450 800 1300 1750
112 Expert 475 850 1350 1875
113 Emeritus 500 900 1400 2000
114 VARIANCE 75 200 200 200
116 Enemy vessels only move prior to their attack. In Novice - Good games
117 only commanders move. In Expert games, all enemy vessels move if there
118 is a commander present. In Emeritus games all enemy vessels move.
120 * If Enterprise is not docked, an agressive action is taken if enemy
121 forces are 1000 greater than Enterprise.
123 Agressive action on average cuts the distance between the ship and
124 the enemy to 1/4 the original.
126 * At lower energy advantage, movement units are proportional to the
127 advantage with a 650 advantage being to hold ground, 800 to move forward
128 1, 950 for two, 150 for back 4, etc. Variance of 100.
130 If docked, is reduced by roughly 1.75*game.skill, generally forcing a
131 retreat, especially at high skill levels.
133 * Motion is limited to skill level, except for SC hi-tailing it out.
136 forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1);
137 if (!game.shldup) forces += 1000; /* Good for enemy if shield is down! */
138 if (!damaged(DPHASER) || !damaged(DPHOTON)) {
139 if (damaged(DPHASER)) /* phasers damaged */
142 forces -= 0.2*(game.energy - 2500.0);
143 if (damaged(DPHOTON)) /* photon torpedoes damaged */
146 forces -= 50.0*game.torps;
149 /* phasers and photon tubes both out! */
153 if (forces <= 1000.0 && game.condition != docked) /* Typical situation */
154 motion = ((forces+200.0*Rand())/150.0) - 5.0;
156 if (forces > 1000.0) /* Very strong -- move in for kill */
157 motion = (1.0-square(Rand()))*dist1 + 1.0;
158 if (game.condition==docked && (game.options & OPTION_BASE)) /* protected by base -- back off ! */
159 motion -= game.skill*(2.0-square(Rand()));
162 proutn("=== MOTION = %d, FORCES = %1.2f, ", motion, forces);
163 /* don't move if no motion */
166 /* Limit motion according to skill */
167 if (abs(motion) > game.skill)
168 motion = (motion < 0) ? -game.skill : game.skill;
170 /* calculate preferred number of steps */
171 nsteps = motion < 0 ? -motion : motion;
172 if (motion > 0 && nsteps > mdist) nsteps = mdist; /* don't overshoot */
173 if (nsteps > QUADSIZE) nsteps = QUADSIZE; /* This shouldn't be necessary */
174 if (nsteps < 1) nsteps = 1; /* This shouldn't be necessary */
176 proutn("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))
183 if (2.0 * abs(my) < abs(game.sector.x-com.x))
186 mx = mx*motion < 0 ? -1 : 1;
188 my = my*motion < 0 ? -1 : 1;
191 for (ll = 0; ll < nsteps; ll++) {
194 /* Check if preferred position available */
195 look.x = next.x + mx;
196 look.y = next.y + my;
197 krawlx = mx < 0 ? 1 : -1;
198 krawly = my < 0 ? 1 : -1;
200 attempts = 0; /* Settle mysterious hang problem */
201 while (attempts++ < 20 && !success) {
202 if (look.x < 1 || look.x > QUADSIZE) {
203 if (motion < 0 && tryexit(look, ienm, loccom, irun))
205 if (krawlx == mx || my == 0)
207 look.x = next.x + krawlx;
210 else if (look.y < 1 || look.y > QUADSIZE) {
211 if (motion < 0 && tryexit(look, ienm, loccom, irun))
213 if (krawly == my || mx == 0)
215 look.y = next.y + krawly;
218 else if ((game.options & OPTION_RAMMING) && game.quad[look.x][look.y] != IHDOT) {
219 /* See if we should ram ship */
220 if (game.quad[look.x][look.y] == game.ship &&
221 (ienm == IHC || ienm == IHS)) {
222 ram(true, ienm, com);
225 if (krawlx != mx && my != 0) {
226 look.x = next.x + krawlx;
229 else if (krawly != my && mx != 0) {
230 look.y = next.y + krawly;
234 break; /* we have failed */
242 proutn(cramlc(neither, next));
245 break; /* done early */
250 /* Put commander in place within same quadrant */
251 game.quad[com.x][com.y] = IHDOT;
252 game.quad[next.x][next.y] = ienm;
253 if (!same(next, com)) {
255 game.ks[loccom] = next;
256 game.kdist[loccom] = game.kavgd[loccom] = distance(game.sector, next);
257 if (!damaged(DSRSENS) || game.condition == docked) {
260 proutn(_(" from %s"), cramlc(2, com));
261 if (game.kdist[loccom] < dist1)
262 proutn(_(" advances to "));
264 proutn(_(" retreats to "));
265 prout(cramlc(sector, next));
271 /* move a commander */
279 /* Figure out which Klingon is the commander (or Supercommander)
282 for_local_enemies(i) {
284 if (game.quad[w.x][w.y] == IHC) {
285 movebaddy(w, i, IHC);
290 for_local_enemies(i) {
292 if (game.quad[w.x][w.y] == IHS) {
293 movebaddy(w, i, IHS);
297 /* if skill level is high, move other Klingons and Romulans too!
298 Move these last so they can base their actions on what the
300 if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY))
301 for_local_enemies(i) {
303 if (game.quad[w.x][w.y] == IHK || game.quad[w.x][w.y] == IHR)
304 movebaddy(w, i, game.quad[w.x][w.y]);
310 static bool movescom(coord iq, bool flag, bool *ipage)
311 /* commander movement helper */
315 if (same(iq, game.quadrant) || !VALID_QUADRANT(iq.x, iq.y) ||
316 game.state.galaxy[iq.x][iq.y].supernova ||
317 game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1)
320 /* Avoid quadrants with bases if we want to avoid Enterprise */
322 if (same(game.state.baseq[i], iq))
325 if (game.justin && !game.iscate)
328 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons--;
329 game.state.kscmdr = iq;
330 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons++;
332 /* SC has scooted, Remove him from current quadrant */
336 game.ientesc = false;
339 if (game.quad[game.ks[i].x][game.ks[i].y] == IHS)
341 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT;
342 game.ks[i] = game.ks[game.nenhere];
343 game.kdist[i] = game.kdist[game.nenhere];
344 game.kavgd[i] = game.kavgd[game.nenhere];
345 game.kpower[i] = game.kpower[game.nenhere];
348 if (game.condition!=docked)
352 /* check for a helpful planet */
353 for (i = 0; i < game.inplan; i++) {
354 if (same(game.state.plnets[i].w, game.state.kscmdr) &&
355 game.state.plnets[i].crystals == present) {
356 /* destroy the planet */
357 DESTROY(&game.state.plnets[i]);
358 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET;
359 if (!damaged(DRADIO) || game.condition == docked) {
363 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
364 proutn(_(" a planet in "));
365 proutn(cramlc(quadrant, game.state.kscmdr));
366 prout(_(" has been destroyed"));
367 prout(_(" by the Super-commander.\""));
372 return false; /* looks good! */
375 void scom(bool *ipage)
376 /* move the Super Commander */
378 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
380 int basetbl[BASEMAX+1];
381 double bdist[BASEMAX+1];
387 /* Decide on being active or passive */
388 flag = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
389 (game.state.date-game.indate) < 3.0);
390 if (!game.iscate && flag) {
391 /* compute move away from Enterprise */
392 ideltax = game.state.kscmdr.x-game.quadrant.x;
393 ideltay = game.state.kscmdr.y-game.quadrant.y;
394 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
395 /* circulate in space */
396 ideltax = game.state.kscmdr.y-game.quadrant.y;
397 ideltay = game.quadrant.x-game.state.kscmdr.x;
401 /* compute distances to starbases */
402 if (game.state.rembase <= 0) {
403 /* nothing left to do */
407 sc = game.state.kscmdr;
410 bdist[i] = distance(game.state.baseq[i], sc);
412 if (game.state.rembase > 1) {
413 /* sort into nearest first order */
417 for (i=1; i < game.state.rembase-1; i++) {
418 if (bdist[i] > bdist[i+1]) {
421 bdist[i] = bdist[i+1];
423 basetbl[i] = basetbl[i+1];
430 /* look for nearest base without a commander, no Enterprise, and
431 without too many Klingons, and not already under attack. */
432 ifindit = iwhichb = 0;
435 i = basetbl[i2]; /* bug in original had it not finding nearest*/
436 ibq = game.state.baseq[i];
437 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
438 game.state.galaxy[ibq.x][ibq.y].supernova ||
439 game.state.galaxy[ibq.x][ibq.y].klingons > MAXKLQUAD-1)
441 /* if there is a commander, an no other base is appropriate,
442 we will take the one with the commander */
444 if (same(ibq, game.state.kcmdr[j]) && ifindit!= 2) {
450 if (j > game.state.remcom) { /* no commander -- use this one */
456 if (ifindit==0) return; /* Nothing suitable -- wait until next time*/
457 ibq = game.state.baseq[iwhichb];
458 /* decide how to move toward base */
459 ideltax = ibq.x - game.state.kscmdr.x;
460 ideltay = ibq.y - game.state.kscmdr.y;
462 /* Maximum movement is 1 quadrant in either or both axis */
472 /* try moving in both x and y directions */
473 iq.x = game.state.kscmdr.x + ideltax;
474 iq.y = game.state.kscmdr.y + ideltax;
475 if (movescom(iq, flag, ipage)) {
476 /* failed -- try some other maneuvers */
477 if (ideltax==0 || ideltay==0) {
478 /* attempt angle move */
480 iq.y = game.state.kscmdr.y + 1;
481 if (movescom(iq, flag, ipage)) {
482 iq.y = game.state.kscmdr.y - 1;
483 movescom(iq, flag, ipage);
487 iq.x = game.state.kscmdr.x + 1;
488 if (movescom(iq, flag, ipage)) {
489 iq.x = game.state.kscmdr.x - 1;
490 movescom(iq, flag, ipage);
495 /* try moving just in x or y */
496 iq.y = game.state.kscmdr.y;
497 if (movescom(iq, flag, ipage)) {
498 iq.y = game.state.kscmdr.y + ideltay;
499 iq.x = game.state.kscmdr.x;
500 movescom(iq, flag, ipage);
504 /* check for a base */
505 if (game.state.rembase == 0) {
508 else for_starbases(i) {
509 ibq = game.state.baseq[i];
510 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
511 /* attack the base */
512 if (flag) return; /* no, don't attack base! */
513 game.iseenit = false;
515 schedule(FSCDBAS, 1.0 +2.0*Rand());
516 if (is_scheduled(FCDBAS))
517 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
518 if (damaged(DRADIO) && game.condition != docked)
519 return; /* no warning */
524 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
525 proutn(cramlc(quadrant, game.state.kscmdr));
527 prout(_(" reports that it is under attack from the Klingon Super-commander."));
528 proutn(_(" It can survive until stardate %d.\""),
529 (int)scheduled(FSCDBAS));
532 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
535 game.resting = false;
536 game.optime = 0.0; /* actually finished */
540 /* Check for intelligence report */
544 (damaged(DRADIO) && game.condition != docked) ||
545 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
550 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
551 proutn(_(" the Super-commander is in "));
552 proutn(cramlc(quadrant, game.state.kscmdr));
558 /* move the Tholian */
561 /* Move the Tholian */
562 if (!game.ithere || game.justin)
565 if (game.tholian.x == 1 && game.tholian.y == 1) {
566 idx = 1; idy = QUADSIZE;
568 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
569 idx = QUADSIZE; idy = QUADSIZE;
571 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
572 idx = QUADSIZE; idy = 1;
574 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
578 /* something is wrong! */
583 /* Do nothing if we are blocked */
584 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB)
586 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
588 if (game.tholian.x != idx) {
590 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
591 while (game.tholian.x != idx) {
592 game.tholian.x += im;
593 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT)
594 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
597 else if (game.tholian.y != idy) {
599 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
600 while (game.tholian.y != idy) {
601 game.tholian.y += im;
602 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT)
603 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
606 game.quad[game.tholian.x][game.tholian.y] = IHT;
607 game.ks[game.nenhere] = game.tholian;
609 /* check to see if all holes plugged */
611 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT)
613 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT)
615 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT)
617 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT)
620 /* All plugged up -- Tholian splits */
621 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
623 crmena(true, IHT, sector, game.tholian);
624 prout(_(" completes web."));