Introduce 'coord' data structure, an (x,y) tuple.
[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.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) 
432                 continue;
433             /* if there is a commander, an no other base is appropriate,
434                we will take the one with the commander */
435             for_commanders (j) {
436                 if (ibq.x==game.state.kcmdr[j].x && ibq.y==game.state.kcmdr[j].y && ifindit!= 2) {
437                     ifindit = 2;
438                     iwhichb = i;
439                     break;
440                 }
441             }
442             if (j > game.state.remcom) { /* no commander -- use this one */
443                 ifindit = 1;
444                 iwhichb = i;
445                 break;
446             }
447         }
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;
454     }
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;
460
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 */
468             if (ideltax != 0) {
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);
473                 }
474             }
475             else {
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);
480                 }
481             }
482         }
483         else {
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);
490             }
491         }
492     }
493     /* check for a base */
494     if (game.state.rembase == 0) {
495         unschedule(FSCMOVE);
496     }
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! */
502             game.iseenit = 0;
503             game.isatb=1;
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 */
509             game.iseenit = 1;
510             if (*ipage == 0)  pause_game(1);
511             *ipage=1;
512             proutn(_("Lt. Uhura-  \"Captain, the starbase in "));
513             proutn(cramlc(quadrant, game.state.kscmdr));
514             skip(1);
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?\""));
520             if (ja()==0) return;
521             game.resting = 0;
522             game.optime = 0.0; /* actually finished */
523             return;
524         }
525     }
526     /* Check for intelligence report */
527     if (
528 #ifdef DEBUG
529         game.idebug==0 &&
530 #endif
531         (Rand() > 0.2 ||
532          (game.damage[DRADIO] > 0.0 && game.condit != IHDOCKED) ||
533          !game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted))
534         return;
535     if (*ipage==0) pause_game(1);
536     *ipage = 1;
537     prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"));
538     proutn(_("   the Super-commander is in "));
539     proutn(cramlc(quadrant, game.state.kscmdr));
540     prout(".\"");
541     return;
542 }
543
544 void movetho(void)
545 {
546     int idx, idy, im, i;
547     coord dummy;
548     /* Move the Tholian */
549     if (game.ithere==0 || game.justin == 1) return;
550
551     if (game.tholian.x == 1 && game.tholian.y == 1) {
552         idx = 1; idy = QUADSIZE;
553     }
554     else if (game.tholian.x == 1 && game.tholian.y == QUADSIZE) {
555         idx = QUADSIZE; idy = QUADSIZE;
556     }
557     else if (game.tholian.x == QUADSIZE && game.tholian.y == QUADSIZE) {
558         idx = QUADSIZE; idy = 1;
559     }
560     else if (game.tholian.x == QUADSIZE && game.tholian.y == 1) {
561         idx = 1; idy = 1;
562     }
563     else {
564         /* something is wrong! */
565         game.ithere = 0;
566         return;
567     }
568
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;
572
573     if (game.tholian.x != idx) {
574         /* move in x axis */
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;
579         }
580     }
581     else if (game.tholian.y != idy) {
582         /* move in y axis */
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;
587         }
588     }
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;
592
593     /* check to see if all holes plugged */
594     for_sectors(i) {
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;
599     }
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;
606     game.nenhere--;
607     return;
608 }