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));
270 void moveklings(void)
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)
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.planets[i].w, game.state.kscmdr) &&
355 game.state.planets[i].crystals == present) {
356 /* destroy the planet */
357 DESTROY(&game.state.planets[i]);
358 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET;
359 if (!damaged(DRADIO) || game.condition == docked) {
361 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
362 proutn(_(" a planet in "));
363 proutn(cramlc(quadrant, game.state.kscmdr));
364 prout(_(" has been destroyed"));
365 prout(_(" by the Super-commander.\""));
370 return false; /* looks good! */
374 /* move the Super Commander */
376 int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
378 int basetbl[BASEMAX+1];
379 double bdist[BASEMAX+1];
385 /* Decide on being active or passive */
386 flag = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) ||
387 (game.state.date-game.indate) < 3.0);
388 if (!game.iscate && flag) {
389 /* compute move away from Enterprise */
390 ideltax = game.state.kscmdr.x-game.quadrant.x;
391 ideltay = game.state.kscmdr.y-game.quadrant.y;
392 if (sqrt(ideltax*(double)ideltax+ideltay*(double)ideltay) > 2.0) {
393 /* circulate in space */
394 ideltax = game.state.kscmdr.y-game.quadrant.y;
395 ideltay = game.quadrant.x-game.state.kscmdr.x;
399 /* compute distances to starbases */
400 if (game.state.rembase <= 0) {
401 /* nothing left to do */
405 sc = game.state.kscmdr;
408 bdist[i] = distance(game.state.baseq[i], sc);
410 if (game.state.rembase > 1) {
411 /* sort into nearest first order */
415 for (i=1; i < game.state.rembase-1; i++) {
416 if (bdist[i] > bdist[i+1]) {
419 bdist[i] = bdist[i+1];
421 basetbl[i] = basetbl[i+1];
428 /* look for nearest base without a commander, no Enterprise, and
429 without too many Klingons, and not already under attack. */
430 ifindit = iwhichb = 0;
433 i = basetbl[i2]; /* bug in original had it not finding nearest*/
434 ibq = game.state.baseq[i];
435 if (same(ibq, game.quadrant) || same(ibq, game.battle) ||
436 game.state.galaxy[ibq.x][ibq.y].supernova ||
437 game.state.galaxy[ibq.x][ibq.y].klingons > MAXKLQUAD-1)
439 /* if there is a commander, an no other base is appropriate,
440 we will take the one with the commander */
442 if (same(ibq, game.state.kcmdr[j]) && ifindit!= 2) {
448 if (j > game.state.remcom) { /* no commander -- use this one */
454 if (ifindit==0) return; /* Nothing suitable -- wait until next time*/
455 ibq = game.state.baseq[iwhichb];
456 /* decide how to move toward base */
457 ideltax = ibq.x - game.state.kscmdr.x;
458 ideltay = ibq.y - game.state.kscmdr.y;
460 /* Maximum movement is 1 quadrant in either or both axis */
470 /* try moving in both x and y directions */
471 iq.x = game.state.kscmdr.x + ideltax;
472 iq.y = game.state.kscmdr.y + ideltax;
473 if (movescom(iq, flag)) {
474 /* failed -- try some other maneuvers */
475 if (ideltax==0 || ideltay==0) {
476 /* attempt angle move */
478 iq.y = game.state.kscmdr.y + 1;
479 if (movescom(iq, flag)) {
480 iq.y = game.state.kscmdr.y - 1;
485 iq.x = game.state.kscmdr.x + 1;
486 if (movescom(iq, flag)) {
487 iq.x = game.state.kscmdr.x - 1;
493 /* try moving just in x or y */
494 iq.y = game.state.kscmdr.y;
495 if (movescom(iq, flag)) {
496 iq.y = game.state.kscmdr.y + ideltay;
497 iq.x = game.state.kscmdr.x;
502 /* check for a base */
503 if (game.state.rembase == 0) {
506 else for_starbases(i) {
507 ibq = game.state.baseq[i];
508 if (same(ibq, game.state.kscmdr) && same(game.state.kscmdr, game.battle)) {
509 /* attack the base */
510 if (flag) return; /* no, don't attack base! */
511 game.iseenit = false;
513 schedule(FSCDBAS, 1.0 +2.0*Rand());
514 if (is_scheduled(FCDBAS))
515 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date);
516 if (damaged(DRADIO) && game.condition != docked)
517 return; /* no warning */
520 proutn(_("Lt. Uhura- \"Captain, the starbase in "));
521 proutn(cramlc(quadrant, game.state.kscmdr));
523 prout(_(" reports that it is under attack from the Klingon Super-commander."));
524 proutn(_(" It can survive until stardate %d.\""),
525 (int)scheduled(FSCDBAS));
528 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""));
531 game.resting = false;
532 game.optime = 0.0; /* actually finished */
536 /* Check for intelligence report */
540 (damaged(DRADIO) && game.condition != docked) ||
541 !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
544 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"));
545 proutn(_(" the Super-commander is in "));
546 proutn(cramlc(quadrant, game.state.kscmdr));
552 /* move the Tholian */
555 /* Move the Tholian */
556 if (!game.ithere || game.justin)
559 if (game.tholian.x == 1 && game.tholian.y == 1) {
560 idx = 1; idy = QUADSIZE;
562 else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
563 idx = QUADSIZE; idy = QUADSIZE;
565 else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
566 idx = QUADSIZE; idy = 1;
568 else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
572 /* something is wrong! */
577 /* Do nothing if we are blocked */
578 if (game.quad[idx][idy]!= IHDOT && game.quad[idx][idy]!= IHWEB)
580 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
582 if (game.tholian.x != idx) {
584 im = fabs((double)idx - game.tholian.x)/((double)idx - game.tholian.x);
585 while (game.tholian.x != idx) {
586 game.tholian.x += im;
587 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT)
588 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
591 else if (game.tholian.y != idy) {
593 im = fabs((double)idy - game.tholian.y)/((double)idy - game.tholian.y);
594 while (game.tholian.y != idy) {
595 game.tholian.y += im;
596 if (game.quad[game.tholian.x][game.tholian.y]==IHDOT)
597 game.quad[game.tholian.x][game.tholian.y] = IHWEB;
600 game.quad[game.tholian.x][game.tholian.y] = IHT;
601 game.ks[game.nenhere] = game.tholian;
603 /* check to see if all holes plugged */
605 if (game.quad[1][i]!=IHWEB && game.quad[1][i]!=IHT)
607 if (game.quad[QUADSIZE][i]!=IHWEB && game.quad[QUADSIZE][i]!=IHT)
609 if (game.quad[i][1]!=IHWEB && game.quad[i][1]!=IHT)
611 if (game.quad[i][QUADSIZE]!=IHWEB && game.quad[i][QUADSIZE]!=IHT)
614 /* All plugged up -- Tholian splits */
615 game.quad[game.tholian.x][game.tholian.y]=IHWEB;
617 crmena(true, IHT, sector, game.tholian);
618 prout(_(" completes web."));