]> git.pond.sub.org Git - empserver/blob - src/lib/commands/buil.c
Simplify buil(): replace a switch by a function pointer
[empserver] / src / lib / commands / buil.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2011, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  buil.c: Build ships, nukes, bridges, planes, land units, bridge towers
28  *
29  *  Known contributors to this file:
30  *     Steve McClure, 1998-2000
31  *     Markus Armbruster, 2004-2011
32  */
33
34 #include <config.h>
35
36 #include <limits.h>
37 #include "commands.h"
38 #include "game.h"
39 #include "land.h"
40 #include "lost.h"
41 #include "map.h"
42 #include "nuke.h"
43 #include "optlist.h"
44 #include "path.h"
45 #include "plague.h"
46 #include "plane.h"
47 #include "ship.h"
48 #include "treaty.h"
49 #include "unit.h"
50
51 static int build_ship(struct sctstr *sp, int type, short *vec, int tlev);
52 static int build_land(struct sctstr *sp, int type, short *vec, int tlev);
53 static int build_nuke(struct sctstr *sp, int type, short *vec, int tlev);
54 static int build_plane(struct sctstr *sp, int type, short *vec, int tlev);
55 static int build_bridge(char);
56 static int build_bspan(struct sctstr *sp, short *vec);
57 static int build_btower(struct sctstr *sp, short *vec);
58 static int build_can_afford(double, char *);
59
60 /*
61  * build <WHAT> <SECTS> <TYPE|DIR|MEG> [NUMBER]
62  */
63 int
64 buil(void)
65 {
66     struct sctstr sect;
67     struct nstr_sect nstr;
68     struct natstr *natp;
69     int rqtech;
70     int tlev;
71     int type;
72     char what;
73     char *p;
74     int gotsect = 0;
75     int (*build_it)(struct sctstr *, int, short[], int);
76     int number;
77     char buf[1024];
78
79     natp = getnatp(player->cnum);
80     tlev = (int)natp->nat_level[NAT_TLEV];
81
82     p = getstarg(player->argp[1],
83                  "Build (ship, nuke, bridge, plane, land unit, tower)? ",
84                  buf);
85     if (!p)
86         return RET_SYN;
87     what = *p;
88     switch (what) {
89     case 'b':
90     case 't':
91         return build_bridge(what);
92     case 's':
93         build_it = build_ship;
94         break;
95     case 'p':
96         build_it = build_plane;
97         break;
98     case 'l':
99         build_it = build_land;
100         break;
101     case 'n':
102         if (!ef_nelem(EF_NUKE_CHR)) {
103             pr("There are no nukes in this game.\n");
104             return RET_FAIL;
105         }
106         if (drnuke_const > MIN_DRNUKE_CONST)
107             tlev = MIN(tlev,
108                        (int)(natp->nat_level[NAT_RLEV] / drnuke_const));
109         build_it = build_nuke;
110         break;
111     default:
112         pr("You can't build that!\n");
113         return RET_SYN;
114     }
115
116     if (!snxtsct(&nstr, player->argp[2]))
117         return RET_SYN;
118
119     switch (what) {
120     case 'p':
121         p = getstarg(player->argp[3], "Plane type? ", buf);
122         if (!p || !*p)
123             return RET_SYN;
124         type = ef_elt_byname(EF_PLANE_CHR, p);
125         if (type >= 0) {
126             rqtech = plchr[type].pl_tech;
127             if (rqtech > tlev)
128                 type = -1;
129         }
130         if (type < 0) {
131             pr("You can't build that!\n");
132             pr("Use `show plane build %d' to show types you can build.\n",
133                tlev);
134             return RET_FAIL;
135         }
136         break;
137     case 's':
138         p = getstarg(player->argp[3], "Ship type? ", buf);
139         if (!p || !*p)
140             return RET_SYN;
141         type = ef_elt_byname(EF_SHIP_CHR, p);
142         if (type >= 0) {
143             rqtech = mchr[type].m_tech;
144             if (rqtech > tlev)
145                 type = -1;
146             if ((mchr[type].m_flags & M_TRADE) && !opt_TRADESHIPS)
147                 type = -1;
148         }
149         if (type < 0) {
150             pr("You can't build that!\n");
151             pr("Use `show ship build %d' to show types you can build.\n",
152                tlev);
153             return RET_FAIL;
154         }
155         break;
156     case 'l':
157         p = getstarg(player->argp[3], "Land unit type? ", buf);
158         if (!p || !*p)
159             return RET_SYN;
160         type = ef_elt_byname(EF_LAND_CHR, p);
161         if (type >= 0) {
162             rqtech = lchr[type].l_tech;
163             if (rqtech > tlev)
164                 type = -1;
165             if ((lchr[type].l_flags & L_SPY) && !opt_LANDSPIES)
166                 type = -1;
167         }
168         if (type < 0) {
169             pr("You can't build that!\n");
170             pr("Use `show land build %d' to show types you can build.\n",
171                tlev);
172             return RET_FAIL;
173         }
174         break;
175     case 'n':
176         p = getstarg(player->argp[3], "Nuke type? ", buf);
177         if (!p || !*p)
178             return RET_SYN;
179         type = ef_elt_byname(EF_NUKE_CHR, p);
180         if (type >= 0) {
181             rqtech = nchr[type].n_tech;
182             if (rqtech > tlev)
183                 type = -1;
184         }
185         if (type < 0) {
186             pr("You can't build that!\n");
187             pr("Use `show nuke build %d' to show types you can build.\n",
188                tlev);
189             return RET_FAIL;
190         }
191         break;
192     default:
193         CANT_REACH();
194         return RET_FAIL;
195     }
196
197     number = 1;
198     if (player->argp[4]) {
199         number = atoi(player->argp[4]);
200         if (number > 20) {
201             char bstr[80];
202             sprintf(bstr,
203                     "Are you sure that you want to build %s of them? ",
204                     player->argp[4]);
205             p = getstarg(player->argp[6], bstr, buf);
206             if (!p || *p != 'y')
207                 return RET_SYN;
208         }
209     }
210
211     if (what != 'n') {
212         if (player->argp[5]) {
213             tlev = atoi(player->argp[5]);
214             if (tlev > natp->nat_level[NAT_TLEV] && !player->god) {
215                 pr("Your tech level is only %d.\n",
216                    (int)natp->nat_level[NAT_TLEV]);
217                 return RET_FAIL;
218             }
219             if (rqtech > tlev) {
220                 pr("Required tech is %d.\n", rqtech);
221                 return RET_FAIL;
222             }
223             pr("building with tech level %d.\n", tlev);
224         }
225     }
226
227     while (number-- > 0) {
228         while (nxtsct(&nstr, &sect)) {
229             gotsect++;
230             if (!player->owner)
231                 continue;
232             if (build_it(&sect, type, sect.sct_item, tlev))
233                 putsect(&sect);
234         }
235         snxtsct_rewind(&nstr);
236     }
237     if (!gotsect) {
238         pr("Bad sector specification.\n");
239     }
240     return RET_OK;
241 }
242
243 static int
244 build_ship(struct sctstr *sp, int type, short *vec, int tlev)
245 {
246     struct mchrstr *mp = &mchr[type];
247     struct shpstr ship;
248     struct nstr_item nstr;
249     int avail;
250     double cost;
251     double eff = SHIP_MINEFF / 100.0;
252     int lcm, hcm;
253     int freeship = 0;
254
255     hcm = roundavg(mp->m_hcm * eff);
256     lcm = roundavg(mp->m_lcm * eff);
257
258     if (sp->sct_type != SCT_HARBR) {
259         pr("Ships must be built in harbours.\n");
260         return 0;
261     }
262     if (sp->sct_effic < 60 && !player->god) {
263         pr("Sector %s is not 60%% efficient.\n",
264            xyas(sp->sct_x, sp->sct_y, player->cnum));
265         return 0;
266     }
267     if (vec[I_LCM] < lcm || vec[I_HCM] < hcm) {
268         pr("Not enough materials in %s\n",
269            xyas(sp->sct_x, sp->sct_y, player->cnum));
270         return 0;
271     }
272     avail = (SHP_BLD_WORK(mp->m_lcm, mp->m_hcm) * SHIP_MINEFF + 99) / 100;
273     if (sp->sct_avail < avail) {
274         pr("Not enough available work in %s to build a %s\n",
275            xyas(sp->sct_x, sp->sct_y, player->cnum), mp->m_name);
276         pr(" (%d available work required)\n", avail);
277         return 0;
278     }
279     cost = mp->m_cost * SHIP_MINEFF / 100.0;
280     if (!build_can_afford(cost, mp->m_name))
281         return 0;
282     if (!trechk(player->cnum, 0, NEWSHP))
283         return 0;
284     if (!check_sect_ok(sp))
285         return 0;
286     sp->sct_avail -= avail;
287     player->dolcost += cost;
288     snxtitem_all(&nstr, EF_SHIP);
289     while (nxtitem(&nstr, &ship)) {
290         if (ship.shp_own == 0) {
291             freeship++;
292             break;
293         }
294     }
295     if (freeship == 0) {
296         ef_extend(EF_SHIP, 50);
297     }
298     ef_blank(EF_SHIP, nstr.cur, &ship);
299     ship.shp_x = sp->sct_x;
300     ship.shp_y = sp->sct_y;
301     ship.shp_own = player->cnum;
302     ship.shp_type = mp - mchr;
303     ship.shp_effic = SHIP_MINEFF;
304     if (opt_MOB_ACCESS) {
305         game_tick_to_now(&ship.shp_access);
306         ship.shp_mobil = -(etu_per_update / sect_mob_neg_factor);
307     } else {
308         ship.shp_mobil = 0;
309     }
310     memset(ship.shp_item, 0, sizeof(ship.shp_item));
311     ship.shp_pstage = PLG_HEALTHY;
312     ship.shp_ptime = 0;
313     ship.shp_name[0] = 0;
314     ship.shp_orig_own = player->cnum;
315     ship.shp_orig_x = sp->sct_x;
316     ship.shp_orig_y = sp->sct_y;
317     shp_set_tech(&ship, tlev);
318     unit_wipe_orders((struct empobj *)&ship);
319
320     vec[I_LCM] -= lcm;
321     vec[I_HCM] -= hcm;
322
323     if (sp->sct_pstage == PLG_INFECT)
324         ship.shp_pstage = PLG_EXPOSED;
325     putship(ship.shp_uid, &ship);
326     pr("%s", prship(&ship));
327     pr(" built in sector %s\n", xyas(sp->sct_x, sp->sct_y, player->cnum));
328     return 1;
329 }
330
331 static int
332 build_land(struct sctstr *sp, int type, short *vec, int tlev)
333 {
334     struct lchrstr *lp = &lchr[type];
335     struct lndstr land;
336     struct nstr_item nstr;
337     int avail;
338     double cost;
339     double eff = LAND_MINEFF / 100.0;
340     int mil, lcm, hcm, gun, shell;
341     int freeland = 0;
342
343 #if 0
344     mil = roundavg(lp->l_mil * eff);
345     shell = roundavg(lp->l_shell * eff);
346     gun = roundavg(lp->l_gun * eff);
347 #else
348     mil = shell = gun = 0;
349 #endif
350     hcm = roundavg(lp->l_hcm * eff);
351     lcm = roundavg(lp->l_lcm * eff);
352
353     if (sp->sct_type != SCT_HEADQ) {
354         pr("Land units must be built in headquarters.\n");
355         return 0;
356     }
357     if (sp->sct_effic < 60 && !player->god) {
358         pr("Sector %s is not 60%% efficient.\n",
359            xyas(sp->sct_x, sp->sct_y, player->cnum));
360         return 0;
361     }
362     if (vec[I_LCM] < lcm || vec[I_HCM] < hcm) {
363         pr("Not enough materials in %s\n",
364            xyas(sp->sct_x, sp->sct_y, player->cnum));
365         return 0;
366     }
367 #if 0
368     if (vec[I_GUN] < gun || vec[I_GUN] == 0) {
369         pr("Not enough guns in %s\n",
370            xyas(sp->sct_x, sp->sct_y, player->cnum));
371         return 0;
372     }
373     if (vec[I_SHELL] < shell) {
374         pr("Not enough shells in %s\n",
375            xyas(sp->sct_x, sp->sct_y, player->cnum));
376         return 0;
377     }
378     if (vec[I_MILIT] < mil) {
379         pr("Not enough military in %s\n",
380            xyas(sp->sct_x, sp->sct_y, player->cnum));
381         return 0;
382     }
383 #endif
384     if (!trechk(player->cnum, 0, NEWLND))
385         return 0;
386     if (!check_sect_ok(sp))
387         return 0;
388     avail = (LND_BLD_WORK(lp->l_lcm, lp->l_hcm) * LAND_MINEFF + 99) / 100;
389     if (sp->sct_avail < avail) {
390         pr("Not enough available work in %s to build a %s\n",
391            xyas(sp->sct_x, sp->sct_y, player->cnum), lp->l_name);
392         pr(" (%d available work required)\n", avail);
393         return 0;
394     }
395     cost = lp->l_cost * LAND_MINEFF / 100.0;
396     if (!build_can_afford(cost, lp->l_name))
397         return 0;
398     sp->sct_avail -= avail;
399     player->dolcost += cost;
400     snxtitem_all(&nstr, EF_LAND);
401     while (nxtitem(&nstr, &land)) {
402         if (land.lnd_own == 0) {
403             freeland++;
404             break;
405         }
406     }
407     if (freeland == 0) {
408         ef_extend(EF_LAND, 50);
409     }
410     ef_blank(EF_LAND, nstr.cur, &land);
411     land.lnd_x = sp->sct_x;
412     land.lnd_y = sp->sct_y;
413     land.lnd_own = player->cnum;
414     land.lnd_type = lp - lchr;
415     land.lnd_effic = LAND_MINEFF;
416     if (opt_MOB_ACCESS) {
417         game_tick_to_now(&land.lnd_access);
418         land.lnd_mobil = -(etu_per_update / sect_mob_neg_factor);
419     } else {
420         land.lnd_mobil = 0;
421     }
422     land.lnd_ship = -1;
423     land.lnd_land = -1;
424     land.lnd_harden = 0;
425     memset(land.lnd_item, 0, sizeof(land.lnd_item));
426     land.lnd_pstage = PLG_HEALTHY;
427     land.lnd_ptime = 0;
428     lnd_set_tech(&land, tlev);
429     unit_wipe_orders((struct empobj *)&land);
430
431     vec[I_LCM] -= lcm;
432     vec[I_HCM] -= hcm;
433     vec[I_MILIT] -= mil;
434     vec[I_GUN] -= gun;
435     vec[I_SHELL] -= shell;
436
437     if (sp->sct_pstage == PLG_INFECT)
438         land.lnd_pstage = PLG_EXPOSED;
439     putland(nstr.cur, &land);
440     pr("%s", prland(&land));
441     pr(" built in sector %s\n", xyas(sp->sct_x, sp->sct_y, player->cnum));
442     return 1;
443 }
444
445 static int
446 build_nuke(struct sctstr *sp, int type, short *vec, int tlev)
447 {
448     struct nchrstr *np = &nchr[type];
449     struct nukstr nuke;
450     struct nstr_item nstr;
451     int avail;
452     int freenuke;
453
454     if (sp->sct_type != SCT_NUKE && !player->god) {
455         pr("Nuclear weapons must be built in nuclear plants.\n");
456         return 0;
457     }
458     if (sp->sct_effic < 60 && !player->god) {
459         pr("Sector %s is not 60%% efficient.\n",
460            xyas(sp->sct_x, sp->sct_y, player->cnum));
461         return 0;
462     }
463     if (vec[I_HCM] < np->n_hcm || vec[I_LCM] < np->n_lcm ||
464         vec[I_OIL] < np->n_oil || vec[I_RAD] < np->n_rad) {
465         pr("Not enough materials for a %s bomb in %s\n",
466            np->n_name, xyas(sp->sct_x, sp->sct_y, player->cnum));
467         pr("(%d hcm, %d lcm, %d oil, & %d rads).\n",
468            np->n_hcm, np->n_lcm, np->n_oil, np->n_rad);
469         return 0;
470     }
471     if (!build_can_afford(np->n_cost, np->n_name))
472         return 0;
473     avail = NUK_BLD_WORK(np->n_lcm, np->n_hcm, np->n_oil, np->n_rad);
474     /*
475      * XXX when nukes turn into units (or whatever), then
476      * make them start at 20%.  Since they don't have efficiency
477      * now, we charge all the work right away.
478      */
479     if (sp->sct_avail < avail) {
480         pr("Not enough available work in %s to build a %s;\n",
481            xyas(sp->sct_x, sp->sct_y, player->cnum), np->n_name);
482         pr(" (%d available work required)\n", avail);
483         return 0;
484     }
485     if (!trechk(player->cnum, 0, NEWNUK))
486         return 0;
487     if (!check_sect_ok(sp))
488         return 0;
489     sp->sct_avail -= avail;
490     player->dolcost += np->n_cost;
491     snxtitem_all(&nstr, EF_NUKE);
492     freenuke = 0;
493     while (nxtitem(&nstr, &nuke)) {
494         if (nuke.nuk_own == 0) {
495             freenuke++;
496             break;
497         }
498     }
499     if (freenuke == 0) {
500         ef_extend(EF_NUKE, 50);
501     }
502     ef_blank(EF_NUKE, nstr.cur, &nuke);
503     nuke.nuk_x = sp->sct_x;
504     nuke.nuk_y = sp->sct_y;
505     nuke.nuk_own = sp->sct_own;
506     nuke.nuk_type = np - nchr;
507     nuke.nuk_effic = 100;
508     nuke.nuk_plane = -1;
509     nuke.nuk_tech = tlev;
510     unit_wipe_orders((struct empobj *)&nuke);
511
512     vec[I_HCM] -= np->n_hcm;
513     vec[I_LCM] -= np->n_lcm;
514     vec[I_OIL] -= np->n_oil;
515     vec[I_RAD] -= np->n_rad;
516
517     putnuke(nuke.nuk_uid, &nuke);
518     pr("%s created in %s\n", prnuke(&nuke),
519        xyas(sp->sct_x, sp->sct_y, player->cnum));
520     return 1;
521 }
522
523 static int
524 build_plane(struct sctstr *sp, int type, short *vec, int tlev)
525 {
526     struct plchrstr *pp = &plchr[type];
527     struct plnstr plane;
528     struct nstr_item nstr;
529     int avail;
530     double cost;
531     double eff = PLANE_MINEFF / 100.0;
532     int hcm, lcm, mil;
533     int freeplane;
534
535     mil = roundavg(pp->pl_crew * eff);
536     /* Always use at least 1 mil to build a plane */
537     if (mil == 0 && pp->pl_crew > 0)
538         mil = 1;
539     hcm = roundavg(pp->pl_hcm * eff);
540     lcm = roundavg(pp->pl_lcm * eff);
541     if (sp->sct_type != SCT_AIRPT && !player->god) {
542         pr("Planes must be built in airports.\n");
543         return 0;
544     }
545     if (sp->sct_effic < 60 && !player->god) {
546         pr("Sector %s is not 60%% efficient.\n",
547            xyas(sp->sct_x, sp->sct_y, player->cnum));
548         return 0;
549     }
550     if (vec[I_LCM] < lcm || vec[I_HCM] < hcm) {
551         pr("Not enough materials in %s\n",
552            xyas(sp->sct_x, sp->sct_y, player->cnum));
553         return 0;
554     }
555     avail = (PLN_BLD_WORK(pp->pl_lcm, pp->pl_hcm) * PLANE_MINEFF + 99) / 100;
556     if (sp->sct_avail < avail) {
557         pr("Not enough available work in %s to build a %s\n",
558            xyas(sp->sct_x, sp->sct_y, player->cnum), pp->pl_name);
559         pr(" (%d available work required)\n", avail);
560         return 0;
561     }
562     cost = pp->pl_cost * PLANE_MINEFF / 100.0;
563     if (!build_can_afford(cost, pp->pl_name))
564         return 0;
565     if (vec[I_MILIT] < mil || (vec[I_MILIT] == 0 && pp->pl_crew > 0)) {
566         pr("Not enough military for crew in %s\n",
567            xyas(sp->sct_x, sp->sct_y, player->cnum));
568         return 0;
569     }
570     if (!trechk(player->cnum, 0, NEWPLN))
571         return 0;
572     if (!check_sect_ok(sp))
573         return 0;
574     sp->sct_avail -= avail;
575     player->dolcost += cost;
576     snxtitem_all(&nstr, EF_PLANE);
577     freeplane = 0;
578     while (nxtitem(&nstr, &plane)) {
579         if (plane.pln_own == 0) {
580             freeplane++;
581             break;
582         }
583     }
584     if (freeplane == 0) {
585         ef_extend(EF_PLANE, 50);
586     }
587     ef_blank(EF_PLANE, nstr.cur, &plane);
588     plane.pln_x = sp->sct_x;
589     plane.pln_y = sp->sct_y;
590     plane.pln_own = sp->sct_own;
591     plane.pln_type = pp - plchr;
592     plane.pln_effic = PLANE_MINEFF;
593     if (opt_MOB_ACCESS) {
594         game_tick_to_now(&plane.pln_access);
595         plane.pln_mobil = -(etu_per_update / sect_mob_neg_factor);
596     } else {
597         plane.pln_mobil = 0;
598     }
599     plane.pln_range = UCHAR_MAX; /* will be adjusted by pln_set_tech() */
600     plane.pln_ship = -1;
601     plane.pln_land = -1;
602     plane.pln_harden = 0;
603     plane.pln_flags = 0;
604     pln_set_tech(&plane, tlev);
605     unit_wipe_orders((struct empobj *)&plane);
606
607     vec[I_LCM] -= lcm;
608     vec[I_HCM] -= hcm;
609     vec[I_MILIT] -= mil;
610
611     putplane(plane.pln_uid, &plane);
612     pr("%s built in sector %s\n", prplane(&plane),
613        xyas(sp->sct_x, sp->sct_y, player->cnum));
614     return 1;
615 }
616
617 static int
618 build_bridge(char what)
619 {
620     struct natstr *natp = getnatp(player->cnum);
621     struct nstr_sect nstr;
622     int (*build_it)(struct sctstr *, short[]);
623     int gotsect;
624     struct sctstr sect;
625
626     switch (what) {
627     case 'b':
628         if (natp->nat_level[NAT_TLEV] < buil_bt) {
629             pr("Building a span requires a tech of %.0f\n", buil_bt);
630             return RET_FAIL;
631         }
632         build_it = build_bspan;
633         break;
634     case 't':
635         if (!opt_BRIDGETOWERS) {
636             pr("Bridge tower building is disabled.\n");
637             return RET_FAIL;
638         }
639         if (natp->nat_level[NAT_TLEV] < buil_tower_bt) {
640             pr("Building a tower requires a tech of %.0f\n",
641                buil_tower_bt);
642             return RET_FAIL;
643         }
644         build_it = build_btower;
645         break;
646     default:
647         CANT_REACH();
648         return RET_FAIL;
649     }
650
651     if (!snxtsct(&nstr, player->argp[2]))
652         return RET_SYN;
653     gotsect = 0;
654     while (nxtsct(&nstr, &sect)) {
655         gotsect++;
656         if (!player->owner)
657             continue;
658         if (build_it(&sect, sect.sct_item))
659             putsect(&sect);
660     }
661     if (!gotsect) {
662         pr("Bad sector specification.\n");
663     }
664     return RET_OK;
665 }
666
667 static int
668 build_bspan(struct sctstr *sp, short *vec)
669 {
670     struct sctstr sect;
671     int val;
672     int newx, newy;
673     int avail;
674     int nx, ny, i, good = 0;
675     char *p;
676     char buf[1024];
677
678     if (opt_EASY_BRIDGES == 0) {        /* must have a bridge head or tower */
679         if (sp->sct_type != SCT_BTOWER) {
680             if (sp->sct_type != SCT_BHEAD)
681                 return 0;
682             if (sp->sct_newtype != SCT_BHEAD)
683                 return 0;
684         }
685     }
686
687     if (sp->sct_effic < 60 && !player->god) {
688         pr("Sector %s is not 60%% efficient.\n",
689            xyas(sp->sct_x, sp->sct_y, player->cnum));
690         return 0;
691     }
692
693     if (vec[I_HCM] < buil_bh) {
694         pr("%s only has %d unit%s of hcm,\n",
695            xyas(sp->sct_x, sp->sct_y, player->cnum),
696            vec[I_HCM], vec[I_HCM] > 1 ? "s" : "");
697         pr("(a bridge span requires %d)\n", buil_bh);
698         return 0;
699     }
700
701     if (!build_can_afford(buil_bc, dchr[SCT_BSPAN].d_name))
702         return 0;
703     avail = (SCT_BLD_WORK(0, buil_bh) * SCT_MINEFF + 99) / 100;
704     if (sp->sct_avail < avail) {
705         pr("Not enough available work in %s to build a bridge\n",
706            xyas(sp->sct_x, sp->sct_y, player->cnum));
707         pr(" (%d available work required)\n", avail);
708         return 0;
709     }
710     if (!player->argp[3]) {
711         pr("Bridge head at %s\n",
712            xyas(sp->sct_x, sp->sct_y, player->cnum));
713         nav_map(sp->sct_x, sp->sct_y, 1);
714     }
715     p = getstarg(player->argp[3], "build span in what direction? ", buf);
716     if (!p || !*p) {
717         return 0;
718     }
719     /* Sanity check time */
720     if (!check_sect_ok(sp))
721         return 0;
722
723     if ((val = chkdir(*p, DIR_FIRST, DIR_LAST)) < 0) {
724         pr("'%c' is not a valid direction...\n", *p);
725         direrr(NULL, NULL, NULL);
726         return 0;
727     }
728     newx = sp->sct_x + diroff[val][0];
729     newy = sp->sct_y + diroff[val][1];
730     if (getsect(newx, newy, &sect) == 0 || sect.sct_type != SCT_WATER) {
731         pr("%s is not a water sector\n", xyas(newx, newy, player->cnum));
732         return 0;
733     }
734     if (opt_EASY_BRIDGES) {
735         good = 0;
736
737         for (i = 1; i <= 6; i++) {
738             struct sctstr s2;
739             nx = sect.sct_x + diroff[i][0];
740             ny = sect.sct_y + diroff[i][1];
741             getsect(nx, ny, &s2);
742             if ((s2.sct_type != SCT_WATER) && (s2.sct_type != SCT_BSPAN))
743                 good = 1;
744         }
745         if (!good) {
746             pr("Bridges must be built adjacent to land or bridge towers.\n");
747             pr("That sector is not adjacent to land or a bridge tower.\n");
748             return 0;
749         }
750     }                           /* end EASY_BRIDGES */
751     sp->sct_avail -= avail;
752     player->dolcost += buil_bc;
753     sect.sct_type = SCT_BSPAN;
754     sect.sct_newtype = SCT_BSPAN;
755     sect.sct_effic = SCT_MINEFF;
756     sect.sct_road = 0;
757     sect.sct_rail = 0;
758     sect.sct_defense = 0;
759     if (opt_MOB_ACCESS) {
760         game_tick_to_now(&sect.sct_access);
761         sect.sct_mobil = -(etu_per_update / sect_mob_neg_factor);
762     } else {
763         sect.sct_mobil = 0;
764     }
765     sect.sct_mines = 0;
766     map_set(player->cnum, sect.sct_x, sect.sct_y, dchr[SCT_BSPAN].d_mnem, 2);
767     writemap(player->cnum);
768     putsect(&sect);
769     pr("Bridge span built over %s\n",
770        xyas(sect.sct_x, sect.sct_y, player->cnum));
771     vec[I_HCM] -= buil_bh;
772     return 1;
773 }
774
775 static int
776 build_btower(struct sctstr *sp, short *vec)
777 {
778     struct sctstr sect;
779     int val;
780     int newx, newy;
781     int avail;
782     char *p;
783     char buf[1024];
784     int i;
785     int nx;
786     int ny;
787
788     if (sp->sct_type != SCT_BSPAN) {
789         pr("Bridge towers can only be built from bridge spans.\n");
790         return 0;
791     }
792
793     if (sp->sct_effic < 60 && !player->god) {
794         pr("Sector %s is not 60%% efficient.\n",
795            xyas(sp->sct_x, sp->sct_y, player->cnum));
796         return 0;
797     }
798
799     if (vec[I_HCM] < buil_tower_bh) {
800         pr("%s only has %d unit%s of hcm,\n",
801            xyas(sp->sct_x, sp->sct_y, player->cnum),
802            vec[I_HCM], vec[I_HCM] > 1 ? "s" : "");
803         pr("(a bridge tower requires %d)\n", buil_tower_bh);
804         return 0;
805     }
806
807     if (!build_can_afford(buil_tower_bc, dchr[SCT_BTOWER].d_name))
808         return 0;
809     avail = (SCT_BLD_WORK(0, buil_tower_bh) * SCT_MINEFF + 99) / 100;
810     if (sp->sct_avail < avail) {
811         pr("Not enough available work in %s to build a bridge tower\n",
812            xyas(sp->sct_x, sp->sct_y, player->cnum));
813         pr(" (%d available work required)\n", avail);
814         return 0;
815     }
816     if (!player->argp[3]) {
817         pr("Building from %s\n", xyas(sp->sct_x, sp->sct_y, player->cnum));
818         nav_map(sp->sct_x, sp->sct_y, 1);
819     }
820     p = getstarg(player->argp[3], "build tower in what direction? ", buf);
821     if (!p || !*p) {
822         return 0;
823     }
824     /* Sanity check time */
825     if (!check_sect_ok(sp))
826         return 0;
827
828     if ((val = chkdir(*p, DIR_FIRST, DIR_LAST)) < 0) {
829         pr("'%c' is not a valid direction...\n", *p);
830         direrr(NULL, NULL, NULL);
831         return 0;
832     }
833     newx = sp->sct_x + diroff[val][0];
834     newy = sp->sct_y + diroff[val][1];
835     if (getsect(newx, newy, &sect) == 0 || sect.sct_type != SCT_WATER) {
836         pr("%s is not a water sector\n", xyas(newx, newy, player->cnum));
837         return 0;
838     }
839
840     /* Now, check.  You aren't allowed to build bridge towers
841        next to land. */
842     for (i = 1; i <= 6; i++) {
843         struct sctstr s2;
844         nx = sect.sct_x + diroff[i][0];
845         ny = sect.sct_y + diroff[i][1];
846         getsect(nx, ny, &s2);
847         if ((s2.sct_type != SCT_WATER) &&
848             (s2.sct_type != SCT_BTOWER) && (s2.sct_type != SCT_BSPAN)) {
849             pr("Bridge towers cannot be built adjacent to land.\n");
850             pr("That sector is adjacent to land.\n");
851             return 0;
852         }
853     }
854
855     sp->sct_avail -= avail;
856     player->dolcost += buil_tower_bc;
857     sect.sct_type = SCT_BTOWER;
858     sect.sct_newtype = SCT_BTOWER;
859     sect.sct_effic = SCT_MINEFF;
860     sect.sct_road = 0;
861     sect.sct_rail = 0;
862     sect.sct_defense = 0;
863     if (opt_MOB_ACCESS) {
864         game_tick_to_now(&sect.sct_access);
865         sect.sct_mobil = -(etu_per_update / sect_mob_neg_factor);
866     } else {
867         sect.sct_mobil = 0;
868     }
869     sect.sct_mines = 0;
870     map_set(player->cnum, sect.sct_x, sect.sct_y, dchr[SCT_BTOWER].d_mnem, 2);
871     writemap(player->cnum);
872     putsect(&sect);
873     pr("Bridge tower built in %s\n",
874        xyas(sect.sct_x, sect.sct_y, player->cnum));
875     vec[I_HCM] -= buil_tower_bh;
876     return 1;
877 }
878
879 static int
880 build_can_afford(double cost, char *what)
881 {
882     struct natstr *natp = getnatp(player->cnum);
883     if (natp->nat_money < player->dolcost + cost) {
884         pr("Not enough money left to build a %s\n", what);
885         return 0;
886     }
887     return 1;
888 }