More type cleanup.
[super-star-trek.git] / src / ai.c
1 #include "sst.h"
2
3 static int tryexit(int lookx, int looky, int ienm, int loccom, int irun) 
4 {
5     int l;
6     coord iq;
7
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! */
15     if (irun == 0) {
16         /* avoid intruding on another commander's territory */
17         if (ienm == IHC) {
18             for_commanders(l)
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;
22         }
23         /* don't leave if over 1000 units of energy */
24         if (game.kpower[loccom] > 1000.) return 0;
25     }
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));
33     }
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];
40     game.klhere--;
41     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++;
46     if (ienm==IHS) {
47         game.ishere=0;
48         game.iscate=0;
49         game.ientesc=0;
50         game.isatb=0;
51         schedule(FSCMOVE, 0.2777);
52         unschedule(FSCDBAS);
53         game.state.kscmdr.x=iq.x;
54         game.state.kscmdr.y=iq.y;
55     }
56     else {
57         for_commanders(l) {
58             if (same(game.state.kcmdr[l], game.quadrant)) {
59                 game.state.kcmdr[l]=iq;
60                 break;
61             }
62         }
63         game.comhere = 0;
64     }
65     return 1; /* success */
66 }
67
68
69 static void movebaddy(coord com, int loccom, int ienm) 
70 {
71     int motion, mdist, nsteps, mx, my, lookx, looky, ll;
72     coord next;
73     int irun = 0;
74     int krawlx, krawly;
75     int success;
76     int attempts;
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);
81     double dist1, forces;
82
83     dist1 = game.kdist[loccom];
84     mdist = dist1 + 0.5; /* Nearest integer distance */
85
86     /* If SC, check with spy to see if should hi-tail it */
87     if (ienm==IHS &&
88         (game.kpower[loccom] <= 500.0 || (game.condit==IHDOCKED && game.damage[DPHOTON]==0))) {
89         irun = 1;
90         motion = -QUADSIZE;
91     }
92     else {
93         /* decide whether to advance, retreat, or hold position */
94 /* Algorithm:
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.
98
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.
102
103  Attacker Initial energy levels (nominal):
104  Klingon   Romulan   Commander   Super-Commander
105  Novice    400        700        1200        
106  Fair      425        750        1250
107  Good      450        800        1300        1750
108  Expert    475        850        1350        1875
109  Emeritus  500        900        1400        2000
110  VARIANCE   75        200         200         200
111
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.
115
116  *  If Enterprise is not docked, an agressive action is taken if enemy
117  forces are 1000 greater than Enterprise.
118
119  Agressive action on average cuts the distance between the ship and
120  the enemy to 1/4 the original.
121
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.
125
126  If docked, is reduced by roughly 1.75*game.skill, generally forcing a
127  retreat, especially at high skill levels.
128
129  *  Motion is limited to skill level, except for SC hi-tailing it out.
130  */
131
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 */
136                 forces += 300.0;
137             else
138                 forces -= 0.2*(game.energy - 2500.0);
139             if (game.damage[DPHOTON] != 0) /* photon torpedoes damaged */
140                 forces += 300.0;
141             else
142                 forces -= 50.0*game.torps;
143         }
144         else {
145             /* phasers and photon tubes both out! */
146             forces += 1000.0;
147         }
148         motion = 0;
149         if (forces <= 1000.0 && game.condit != IHDOCKED) /* Typical situation */
150             motion = ((forces+200.0*Rand())/150.0) - 5.0;
151         else {
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()));
156         }
157 #ifdef DEBUG
158         if (game.idebug) {
159             proutn("MOTION = %1.2f", motion);
160             proutn("  FORCES = %1.2f", forces);
161         }
162 #endif
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;
167     }
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 */
173 #ifdef DEBUG
174     if (game.idebug) {
175         prout("NSTEPS = %d", nsteps);
176     }
177 #endif
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;
185     next = com;
186     /* main move loop */
187     for (ll = 0; ll < nsteps; ll++) {
188 #ifdef DEBUG
189         if (game.idebug) {
190             prout("%d", ll+1);
191         }
192 #endif
193         /* Check if preferred position available */
194         lookx = next.x + mx;
195         looky = next.y + my;
196         krawlx = mx < 0 ? 1 : -1;
197         krawly = my < 0 ? 1 : -1;
198         success = 0;
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))
203                     return;
204                 if (krawlx == mx || my == 0) break;
205                 lookx = next.x + krawlx;
206                 krawlx = -krawlx;
207             }
208             else if (looky < 1 || looky > QUADSIZE) {
209                 if (motion < 0 && tryexit(lookx, looky, ienm, loccom, irun))
210                     return;
211                 if (krawly == my || mx == 0) break;
212                 looky = next.y + krawly;
213                 krawly = -krawly;
214             }
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)) {
219                     ram(1, ienm, com);
220                     return;
221                 }
222                 if (krawlx != mx && my != 0) {
223                     lookx = next.x + krawlx;
224                     krawlx = -krawlx;
225                 }
226                 else if (krawly != my && mx != 0) {
227                     looky = next.y + krawly;
228                     krawly = -krawly;
229                 }
230                 else break; /* we have failed */
231             }
232             else success = 1;
233         }
234         if (success) {
235             next.x = lookx;
236             next.y = looky;
237 #ifdef DEBUG
238             if (game.idebug) {
239                 prout(cramlc(neither, next));
240             }
241 #endif
242         }
243         else break; /* done early */
244     }
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) {
249         /* it moved */
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) {
255             proutn("***");
256             cramen(ienm);
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));
261         }
262     }
263 }
264
265 void movcom(void) 
266 {
267     coord w; 
268     int i;
269
270 #ifdef DEBUG
271     if (game.idebug) prout("MOVCOM");
272 #endif
273
274     /* Figure out which Klingon is the commander (or Supercommander)
275        and do move */
276     if (game.comhere) 
277         for_local_enemies(i) {
278             w = game.ks[i];
279             if (game.quad[w.x][w.y] == IHC) {
280                 movebaddy(w, i, IHC);
281                 break;
282             }
283         }
284     if (game.ishere) 
285         for_local_enemies(i) {
286             w = game.ks[i];
287             if (game.quad[w.x][w.y] == IHS) {
288                 movebaddy(w, i, IHS);
289                 break;
290             }
291         }
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
294        commander(s) do. */
295     if (game.skill >= SKILL_EXPERT && (game.options & OPTION_MVBADDY)) 
296         for_local_enemies(i) {
297             w = game.ks[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]);
300         }
301
302     sortkl();
303 }
304
305 static int movescom(coord iq, int flag, int *ipage) 
306 {
307     int i;
308
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) 
312         return 1;
313     if (flag) {
314         /* Avoid quadrants with bases if we want to avoid Enterprise */
315         for_starbases(i)
316             if (game.state.baseq[i].x==iq.x && game.state.baseq[i].y==iq.y) return 1;
317     }
318     if (game.justin && !game.iscate) return 1;
319     /* do the move */
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++;
323     if (game.ishere) {
324         /* SC has scooted, Remove him from current quadrant */
325         game.iscate=0;
326         game.isatb=0;
327         game.ishere=0;
328         game.ientesc=0;
329         unschedule(FSCDBAS);
330         for_local_enemies(i) 
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];
337         game.klhere--;
338         game.nenhere--;
339         if (game.condit!=IHDOCKED) newcnd();
340         sortkl();
341     }
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);
351                 *ipage = 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.\""));
357             }
358             break;
359         }
360     }
361     return 0; /* looks good! */
362 }
363                         
364 void scom(int *ipage)
365 {
366     int i, i2, j, ideltax, ideltay, ifindit, iwhichb;
367     coord iq, sc, ibq;
368     int basetbl[BASEMAX+1];
369     double bdist[BASEMAX+1];
370     int flag;
371 #ifdef DEBUG
372     if (game.idebug) prout("SCOM");
373 #endif
374
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;
386         }
387     }
388     else {
389         /* compute distances to starbases */
390         if (game.state.rembase <= 0) {
391             /* nothing left to do */
392             unschedule(FSCMOVE);
393             return;
394         }
395         sc = game.state.kscmdr;
396         for_starbases(i) {
397             basetbl[i] = i;
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));
401         }
402         if (game.state.rembase > 1) {
403             /* sort into nearest first order */
404             int iswitch;
405             do {
406                 iswitch = 0;
407                 for (i=1; i < game.state.rembase-1; i++) {
408                     if (bdist[i] > bdist[i+1]) {
409                         int ti = basetbl[i];
410                         double t = bdist[i];
411                         bdist[i] = bdist[i+1];
412                         bdist[i+1] = t;
413                         basetbl[i] = basetbl[i+1];
414                         basetbl[i+1] =ti;
415                         iswitch = 1;
416                     }
417                 }
418             } while (iswitch);
419         }
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;
423
424         for_starbases(i2) {
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) 
430                 continue;
431             /* if there is a commander, an no other base is appropriate,
432                we will take the one with the commander */
433             for_commanders (j) {
434                 if (ibq.x==game.state.kcmdr[j].x && ibq.y==game.state.kcmdr[j].y && ifindit!= 2) {
435                     ifindit = 2;
436                     iwhichb = i;
437                     break;
438                 }
439             }
440             if (j > game.state.remcom) { /* no commander -- use this one */
441                 ifindit = 1;
442                 iwhichb = i;
443                 break;
444             }
445         }
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;
451     }
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;
457
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 */
465             if (ideltax != 0) {
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);
470                 }
471             }
472             else {
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);
477                 }
478             }
479         }
480         else {
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);
487             }
488         }
489     }
490     /* check for a base */
491     if (game.state.rembase == 0) {
492         unschedule(FSCMOVE);
493     }
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! */
499             game.iseenit = 0;
500             game.isatb=1;
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 */
506             game.iseenit = 1;
507             if (*ipage == 0)  pause_game(1);
508             *ipage=1;
509             proutn(_("Lt. Uhura-  \"Captain, the starbase in "));
510             proutn(cramlc(quadrant, game.state.kscmdr));
511             skip(1);
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?\""));
517             if (ja()==0) return;
518             game.resting = 0;
519             game.optime = 0.0; /* actually finished */
520             return;
521         }
522     }
523     /* Check for intelligence report */
524     if (
525 #ifdef DEBUG
526         game.idebug==0 &&
527 #endif
528         (Rand() > 0.2 ||
529          (game.damage[DRADIO] > 0.0 && game.condit != IHDOCKED) ||
530          !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
531         return;
532     if (*ipage==0) pause_game(1);
533     *ipage = 1;
534     prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"));
535     proutn(_("   the Super-commander is in "));
536     proutn(cramlc(quadrant, game.state.kscmdr));
537     prout(".\"");
538     return;
539 }
540
541 void movetho(void)
542 {
543     int idx, idy, im, i;
544     coord dummy;
545     /* Move the Tholian */
546     if (game.ithere==0 || game.justin == 1) return;
547
548     if (game.tholian.x == 1 && game.tholian.y == 1) {
549         idx = 1; idy = QUADSIZE;
550     }
551     else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
552         idx = QUADSIZE; idy = QUADSIZE;
553     }
554     else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
555         idx = QUADSIZE; idy = 1;
556     }
557     else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
558         idx = 1; idy = 1;
559     }
560     else {
561         /* something is wrong! */
562         game.ithere = 0;
563         return;
564     }
565
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;
569
570     if (game.tholian.x != idx) {
571         /* move in x axis */
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;
576         }
577     }
578     else if (game.tholian.y != idy) {
579         /* move in y axis */
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;
584         }
585     }
586     game.quad[game.tholian.x][game.tholian.y] = IHT;
587     game.ks[game.nenhere] = game.tholian;
588
589     /* check to see if all holes plugged */
590     for_sectors(i) {
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;
595     }
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;
602     game.nenhere--;
603     return;
604 }