]> git.pond.sub.org Git - empserver/blobdiff - src/lib/commands/buil.c
Update copyright notice
[empserver] / src / lib / commands / buil.c
index a77548ef6409f27a37abd5bb805e4eadae9af98f..b30049bb73221033fea775da53f12cab0e780bbd 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2011, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *  Copyright (C) 1986-2015, Dave Pare, Jeff Bailey, Thomas Ruschak,
  *                Ken Stevens, Steve McClure, Markus Armbruster
  *
  *  Empire is free software: you can redistribute it and/or modify
  *
  *  Known contributors to this file:
  *     Steve McClure, 1998-2000
- *     Markus Armbruster, 2004-2011
+ *     Markus Armbruster, 2004-2014
  */
 
 #include <config.h>
 
 #include <limits.h>
+#include <math.h>
+#include "chance.h"
 #include "commands.h"
 #include "game.h"
 #include "land.h"
 #include "plague.h"
 #include "plane.h"
 #include "ship.h"
-#include "treaty.h"
 #include "unit.h"
 
-static int build_ship(struct sctstr *sp, int type, short *vec, int tlev);
-static int build_land(struct sctstr *sp, int type, short *vec, int tlev);
-static int build_nuke(struct sctstr *sp, int type, short *vec, int tlev);
-static int build_plane(struct sctstr *sp, int type, short *vec, int tlev);
+static int build_ship(struct sctstr *sp, int type, int tlev);
+static int build_land(struct sctstr *sp, int type, int tlev);
+static int build_nuke(struct sctstr *sp, int type, int tlev);
+static int build_plane(struct sctstr *sp, int type, int tlev);
+static int pick_unused_unit_uid(int);
 static int build_bridge(char);
-static int build_bspan(struct sctstr *sp, short *vec);
-static int build_btower(struct sctstr *sp, short *vec);
-static int build_can_afford(double, char *);
+static int build_bspan(struct sctstr *sp);
+static int build_btower(struct sctstr *sp);
+static int sector_can_build(struct sctstr *, short[], int, int, char *);
+static void build_charge(struct sctstr *, short[], int, double, int);
+static int build_can_afford(double, int, char *);
 
 /*
  * build <WHAT> <SECTS> <TYPE|DIR|MEG> [NUMBER]
@@ -69,7 +73,7 @@ buil(void)
     struct nstr_sect nstr;
     int rqtech, type, number, val, gotsect;
     char *p, *what, *prompt;
-    int (*build_it)(struct sctstr *, int, short[], int);
+    int (*build_it)(struct sctstr *, int, int);
     char buf[1024];
 
     p = getstarg(player->argp[1],
@@ -129,19 +133,13 @@ buil(void)
        break;
     case 's':
        type = ef_elt_byname(EF_SHIP_CHR, p);
-       if (type >= 0) {
+       if (type >= 0)
            rqtech = mchr[type].m_tech;
-           if ((mchr[type].m_flags & M_TRADE) && !opt_TRADESHIPS)
-               type = -1;
-       }
        break;
     case 'l':
        type = ef_elt_byname(EF_LAND_CHR, p);
-       if (type >= 0) {
+       if (type >= 0)
            rqtech = lchr[type].l_tech;
-           if ((lchr[type].l_flags & L_SPY) && !opt_LANDSPIES)
-               type = -1;
-       }
        break;
     case 'n':
        type = ef_elt_byname(EF_NUKE_CHR, p);
@@ -153,6 +151,8 @@ buil(void)
        return RET_FAIL;
     }
 
+    if (tlev < rqtech && player->god)
+       tlev = rqtech;
     if (type < 0 || tlev < rqtech) {
        pr("You can't build that!\n");
        pr("Use `show %s build %d' to show types you can build.\n",
@@ -166,8 +166,8 @@ buil(void)
        if (number > 20) {
            char bstr[80];
            sprintf(bstr,
-                   "Are you sure that you want to build %s of them? ",
-                   player->argp[4]);
+                   "Are you sure that you want to build %d of them? ",
+                   number);
            p = getstarg(player->argp[6], bstr, buf);
            if (!p || *p != 'y')
                return RET_SYN;
@@ -196,7 +196,7 @@ buil(void)
            if (!player->owner)
                continue;
            gotsect = 1;
-           if (build_it(&sect, type, sect.sct_item, tlev))
+           if (build_it(&sect, type, tlev))
                putsect(&sect);
        }
        snxtsct_rewind(&nstr);
@@ -207,64 +207,32 @@ buil(void)
 }
 
 static int
-build_ship(struct sctstr *sp, int type, short *vec, int tlev)
+build_ship(struct sctstr *sp, int type, int tlev)
 {
     struct mchrstr *mp = &mchr[type];
+    short mat[I_MAX+1];
+    int work;
     struct shpstr ship;
-    struct nstr_item nstr;
-    int avail;
-    double cost;
-    double eff = SHIP_MINEFF / 100.0;
-    int lcm, hcm;
-    int freeship = 0;
 
-    hcm = roundavg(mp->m_hcm * eff);
-    lcm = roundavg(mp->m_lcm * eff);
+    memset(mat, 0, sizeof(mat));
+    mat[I_LCM] = mp->m_lcm;
+    mat[I_HCM] = mp->m_hcm;
+    work = SHP_BLD_WORK(mp->m_lcm, mp->m_hcm);
 
-    if (sp->sct_type != SCT_HARBR) {
+    if (sp->sct_type != SCT_HARBR && !player->god) {
        pr("Ships must be built in harbours.\n");
        return 0;
     }
-    if (sp->sct_effic < 60 && !player->god) {
-       pr("Sector %s is not 60%% efficient.\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       return 0;
-    }
-    if (vec[I_LCM] < lcm || vec[I_HCM] < hcm) {
-       pr("Not enough materials in %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       return 0;
-    }
-    avail = (SHP_BLD_WORK(mp->m_lcm, mp->m_hcm) * SHIP_MINEFF + 99) / 100;
-    if (sp->sct_avail < avail) {
-       pr("Not enough available work in %s to build a %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum), mp->m_name);
-       pr(" (%d available work required)\n", avail);
+    if (!sector_can_build(sp, mat, work, SHIP_MINEFF, mp->m_name))
        return 0;
-    }
-    cost = mp->m_cost * SHIP_MINEFF / 100.0;
-    if (!build_can_afford(cost, mp->m_name))
+    if (!build_can_afford(mp->m_cost, SHIP_MINEFF, mp->m_name))
        return 0;
-    if (!trechk(player->cnum, 0, NEWSHP))
-       return 0;
-    if (!check_sect_ok(sp))
-       return 0;
-    sp->sct_avail -= avail;
-    player->dolcost += cost;
-    snxtitem_all(&nstr, EF_SHIP);
-    while (nxtitem(&nstr, &ship)) {
-       if (ship.shp_own == 0) {
-           freeship++;
-           break;
-       }
-    }
-    if (freeship == 0) {
-       ef_extend(EF_SHIP, 50);
-    }
-    ef_blank(EF_SHIP, nstr.cur, &ship);
+    build_charge(sp, mat, work, mp->m_cost, SHIP_MINEFF);
+
+    ef_blank(EF_SHIP, pick_unused_unit_uid(EF_SHIP), &ship);
     ship.shp_x = sp->sct_x;
     ship.shp_y = sp->sct_y;
-    ship.shp_own = player->cnum;
+    ship.shp_own = sp->sct_own;
     ship.shp_type = mp - mchr;
     ship.shp_effic = SHIP_MINEFF;
     if (opt_MOB_ACCESS) {
@@ -277,15 +245,12 @@ build_ship(struct sctstr *sp, int type, short *vec, int tlev)
     ship.shp_pstage = PLG_HEALTHY;
     ship.shp_ptime = 0;
     ship.shp_name[0] = 0;
-    ship.shp_orig_own = player->cnum;
+    ship.shp_orig_own = sp->sct_own;
     ship.shp_orig_x = sp->sct_x;
     ship.shp_orig_y = sp->sct_y;
     shp_set_tech(&ship, tlev);
     unit_wipe_orders((struct empobj *)&ship);
 
-    vec[I_LCM] -= lcm;
-    vec[I_HCM] -= hcm;
-
     if (sp->sct_pstage == PLG_INFECT)
        ship.shp_pstage = PLG_EXPOSED;
     putship(ship.shp_uid, &ship);
@@ -295,88 +260,32 @@ build_ship(struct sctstr *sp, int type, short *vec, int tlev)
 }
 
 static int
-build_land(struct sctstr *sp, int type, short *vec, int tlev)
+build_land(struct sctstr *sp, int type, int tlev)
 {
     struct lchrstr *lp = &lchr[type];
+    short mat[I_MAX+1];
+    int work;
     struct lndstr land;
-    struct nstr_item nstr;
-    int avail;
-    double cost;
-    double eff = LAND_MINEFF / 100.0;
-    int mil, lcm, hcm, gun, shell;
-    int freeland = 0;
-
-#if 0
-    mil = roundavg(lp->l_mil * eff);
-    shell = roundavg(lp->l_shell * eff);
-    gun = roundavg(lp->l_gun * eff);
-#else
-    mil = shell = gun = 0;
-#endif
-    hcm = roundavg(lp->l_hcm * eff);
-    lcm = roundavg(lp->l_lcm * eff);
-
-    if (sp->sct_type != SCT_HEADQ) {
+
+    memset(mat, 0, sizeof(mat));
+    mat[I_LCM] = lp->l_lcm;
+    mat[I_HCM] = lp->l_hcm;
+    work = LND_BLD_WORK(lp->l_lcm, lp->l_hcm);
+
+    if (sp->sct_type != SCT_HEADQ && !player->god) {
        pr("Land units must be built in headquarters.\n");
        return 0;
     }
-    if (sp->sct_effic < 60 && !player->god) {
-       pr("Sector %s is not 60%% efficient.\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
+    if (!sector_can_build(sp, mat, work, LAND_MINEFF, lp->l_name))
        return 0;
-    }
-    if (vec[I_LCM] < lcm || vec[I_HCM] < hcm) {
-       pr("Not enough materials in %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
+    if (!build_can_afford(lp->l_cost, LAND_MINEFF, lp->l_name))
        return 0;
-    }
-#if 0
-    if (vec[I_GUN] < gun || vec[I_GUN] == 0) {
-       pr("Not enough guns in %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       return 0;
-    }
-    if (vec[I_SHELL] < shell) {
-       pr("Not enough shells in %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       return 0;
-    }
-    if (vec[I_MILIT] < mil) {
-       pr("Not enough military in %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       return 0;
-    }
-#endif
-    if (!trechk(player->cnum, 0, NEWLND))
-       return 0;
-    if (!check_sect_ok(sp))
-       return 0;
-    avail = (LND_BLD_WORK(lp->l_lcm, lp->l_hcm) * LAND_MINEFF + 99) / 100;
-    if (sp->sct_avail < avail) {
-       pr("Not enough available work in %s to build a %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum), lp->l_name);
-       pr(" (%d available work required)\n", avail);
-       return 0;
-    }
-    cost = lp->l_cost * LAND_MINEFF / 100.0;
-    if (!build_can_afford(cost, lp->l_name))
-       return 0;
-    sp->sct_avail -= avail;
-    player->dolcost += cost;
-    snxtitem_all(&nstr, EF_LAND);
-    while (nxtitem(&nstr, &land)) {
-       if (land.lnd_own == 0) {
-           freeland++;
-           break;
-       }
-    }
-    if (freeland == 0) {
-       ef_extend(EF_LAND, 50);
-    }
-    ef_blank(EF_LAND, nstr.cur, &land);
+    build_charge(sp, mat, work, lp->l_cost, LAND_MINEFF);
+
+    ef_blank(EF_LAND, pick_unused_unit_uid(EF_LAND), &land);
     land.lnd_x = sp->sct_x;
     land.lnd_y = sp->sct_y;
-    land.lnd_own = player->cnum;
+    land.lnd_own = sp->sct_own;
     land.lnd_type = lp - lchr;
     land.lnd_effic = LAND_MINEFF;
     if (opt_MOB_ACCESS) {
@@ -394,78 +303,45 @@ build_land(struct sctstr *sp, int type, short *vec, int tlev)
     lnd_set_tech(&land, tlev);
     unit_wipe_orders((struct empobj *)&land);
 
-    vec[I_LCM] -= lcm;
-    vec[I_HCM] -= hcm;
-    vec[I_MILIT] -= mil;
-    vec[I_GUN] -= gun;
-    vec[I_SHELL] -= shell;
-
     if (sp->sct_pstage == PLG_INFECT)
        land.lnd_pstage = PLG_EXPOSED;
-    putland(nstr.cur, &land);
+    putland(land.lnd_uid, &land);
     pr("%s", prland(&land));
     pr(" built in sector %s\n", xyas(sp->sct_x, sp->sct_y, player->cnum));
     return 1;
 }
 
 static int
-build_nuke(struct sctstr *sp, int type, short *vec, int tlev)
+build_nuke(struct sctstr *sp, int type, int tlev)
 {
     struct nchrstr *np = &nchr[type];
+    short mat[I_MAX+1];
+    int work;
     struct nukstr nuke;
-    struct nstr_item nstr;
-    int avail;
-    int freenuke;
 
     if (sp->sct_type != SCT_NUKE && !player->god) {
        pr("Nuclear weapons must be built in nuclear plants.\n");
        return 0;
     }
-    if (sp->sct_effic < 60 && !player->god) {
-       pr("Sector %s is not 60%% efficient.\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       return 0;
-    }
-    if (vec[I_HCM] < np->n_hcm || vec[I_LCM] < np->n_lcm ||
-       vec[I_OIL] < np->n_oil || vec[I_RAD] < np->n_rad) {
-       pr("Not enough materials for a %s bomb in %s\n",
-          np->n_name, xyas(sp->sct_x, sp->sct_y, player->cnum));
-       pr("(%d hcm, %d lcm, %d oil, & %d rads).\n",
-          np->n_hcm, np->n_lcm, np->n_oil, np->n_rad);
-       return 0;
-    }
-    if (!build_can_afford(np->n_cost, np->n_name))
-       return 0;
-    avail = NUK_BLD_WORK(np->n_lcm, np->n_hcm, np->n_oil, np->n_rad);
     /*
      * XXX when nukes turn into units (or whatever), then
      * make them start at 20%.  Since they don't have efficiency
      * now, we charge all the work right away.
      */
-    if (sp->sct_avail < avail) {
-       pr("Not enough available work in %s to build a %s;\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum), np->n_name);
-       pr(" (%d available work required)\n", avail);
-       return 0;
-    }
-    if (!trechk(player->cnum, 0, NEWNUK))
+    memset(mat, 0, sizeof(mat));
+    mat[I_LCM] = np->n_lcm;
+    mat[I_HCM] = np->n_hcm;
+    mat[I_OIL] = np->n_oil;
+    mat[I_RAD] = np->n_rad;
+    work = NUK_BLD_WORK(np->n_lcm, np->n_hcm, np->n_oil, np->n_rad);
+
+    if (!sector_can_build(sp, mat, work, 100, np->n_name))
        return 0;
-    if (!check_sect_ok(sp))
+    if (!build_can_afford(np->n_cost, 100, np->n_name))
        return 0;
-    sp->sct_avail -= avail;
-    player->dolcost += np->n_cost;
-    snxtitem_all(&nstr, EF_NUKE);
-    freenuke = 0;
-    while (nxtitem(&nstr, &nuke)) {
-       if (nuke.nuk_own == 0) {
-           freenuke++;
-           break;
-       }
-    }
-    if (freenuke == 0) {
-       ef_extend(EF_NUKE, 50);
-    }
-    ef_blank(EF_NUKE, nstr.cur, &nuke);
+    build_charge(sp, mat, work, np->n_cost, 100);
+
+    ef_blank(EF_NUKE, pick_unused_unit_uid(EF_NUKE), &nuke);
     nuke.nuk_x = sp->sct_x;
     nuke.nuk_y = sp->sct_y;
     nuke.nuk_own = sp->sct_own;
@@ -475,11 +351,6 @@ build_nuke(struct sctstr *sp, int type, short *vec, int tlev)
     nuke.nuk_tech = tlev;
     unit_wipe_orders((struct empobj *)&nuke);
 
-    vec[I_HCM] -= np->n_hcm;
-    vec[I_LCM] -= np->n_lcm;
-    vec[I_OIL] -= np->n_oil;
-    vec[I_RAD] -= np->n_rad;
-
     putnuke(nuke.nuk_uid, &nuke);
     pr("%s created in %s\n", prnuke(&nuke),
        xyas(sp->sct_x, sp->sct_y, player->cnum));
@@ -487,70 +358,30 @@ build_nuke(struct sctstr *sp, int type, short *vec, int tlev)
 }
 
 static int
-build_plane(struct sctstr *sp, int type, short *vec, int tlev)
+build_plane(struct sctstr *sp, int type, int tlev)
 {
     struct plchrstr *pp = &plchr[type];
+    short mat[I_MAX+1];
+    int work;
     struct plnstr plane;
-    struct nstr_item nstr;
-    int avail;
-    double cost;
-    double eff = PLANE_MINEFF / 100.0;
-    int hcm, lcm, mil;
-    int freeplane;
-
-    mil = roundavg(pp->pl_crew * eff);
-    /* Always use at least 1 mil to build a plane */
-    if (mil == 0 && pp->pl_crew > 0)
-       mil = 1;
-    hcm = roundavg(pp->pl_hcm * eff);
-    lcm = roundavg(pp->pl_lcm * eff);
+
+    memset(mat, 0, sizeof(mat));
+    mat[I_MILIT] = pp->pl_crew;
+    mat[I_LCM] = pp->pl_lcm;
+    mat[I_HCM] = pp->pl_hcm;
+    work = PLN_BLD_WORK(pp->pl_lcm, pp->pl_hcm);
+
     if (sp->sct_type != SCT_AIRPT && !player->god) {
        pr("Planes must be built in airports.\n");
        return 0;
     }
-    if (sp->sct_effic < 60 && !player->god) {
-       pr("Sector %s is not 60%% efficient.\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
+    if (!sector_can_build(sp, mat, work, PLANE_MINEFF, pp->pl_name))
        return 0;
-    }
-    if (vec[I_LCM] < lcm || vec[I_HCM] < hcm) {
-       pr("Not enough materials in %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
+    if (!build_can_afford(pp->pl_cost, PLANE_MINEFF, pp->pl_name))
        return 0;
-    }
-    avail = (PLN_BLD_WORK(pp->pl_lcm, pp->pl_hcm) * PLANE_MINEFF + 99) / 100;
-    if (sp->sct_avail < avail) {
-       pr("Not enough available work in %s to build a %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum), pp->pl_name);
-       pr(" (%d available work required)\n", avail);
-       return 0;
-    }
-    cost = pp->pl_cost * PLANE_MINEFF / 100.0;
-    if (!build_can_afford(cost, pp->pl_name))
-       return 0;
-    if (vec[I_MILIT] < mil || (vec[I_MILIT] == 0 && pp->pl_crew > 0)) {
-       pr("Not enough military for crew in %s\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       return 0;
-    }
-    if (!trechk(player->cnum, 0, NEWPLN))
-       return 0;
-    if (!check_sect_ok(sp))
-       return 0;
-    sp->sct_avail -= avail;
-    player->dolcost += cost;
-    snxtitem_all(&nstr, EF_PLANE);
-    freeplane = 0;
-    while (nxtitem(&nstr, &plane)) {
-       if (plane.pln_own == 0) {
-           freeplane++;
-           break;
-       }
-    }
-    if (freeplane == 0) {
-       ef_extend(EF_PLANE, 50);
-    }
-    ef_blank(EF_PLANE, nstr.cur, &plane);
+    build_charge(sp, mat, work, pp->pl_cost, PLANE_MINEFF);
+
+    ef_blank(EF_PLANE, pick_unused_unit_uid(EF_PLANE), &plane);
     plane.pln_x = sp->sct_x;
     plane.pln_y = sp->sct_y;
     plane.pln_own = sp->sct_own;
@@ -570,28 +401,39 @@ build_plane(struct sctstr *sp, int type, short *vec, int tlev)
     pln_set_tech(&plane, tlev);
     unit_wipe_orders((struct empobj *)&plane);
 
-    vec[I_LCM] -= lcm;
-    vec[I_HCM] -= hcm;
-    vec[I_MILIT] -= mil;
-
     putplane(plane.pln_uid, &plane);
     pr("%s built in sector %s\n", prplane(&plane),
        xyas(sp->sct_x, sp->sct_y, player->cnum));
     return 1;
 }
 
+static int
+pick_unused_unit_uid(int type)
+{
+    struct nstr_item nstr;
+    union empobj_storage unit;
+
+    snxtitem_all(&nstr, type);
+    while (nxtitem(&nstr, &unit)) {
+       if (!unit.gen.own)
+           return nstr.cur;
+    }
+    ef_extend(type, 50);
+    return nstr.cur;
+}
+
 static int
 build_bridge(char what)
 {
     struct natstr *natp = getnatp(player->cnum);
     struct nstr_sect nstr;
-    int (*build_it)(struct sctstr *, short[]);
+    int (*build_it)(struct sctstr *);
     int gotsect;
     struct sctstr sect;
 
     switch (what) {
     case 'b':
-       if (natp->nat_level[NAT_TLEV] < buil_bt) {
+       if (natp->nat_level[NAT_TLEV] < buil_bt && !player->god) {
            pr("Building a span requires a tech of %.0f\n", buil_bt);
            return RET_FAIL;
        }
@@ -602,7 +444,7 @@ build_bridge(char what)
            pr("Bridge tower building is disabled.\n");
            return RET_FAIL;
        }
-       if (natp->nat_level[NAT_TLEV] < buil_tower_bt) {
+       if (natp->nat_level[NAT_TLEV] < buil_tower_bt && !player->god) {
            pr("Building a tower requires a tech of %.0f\n",
               buil_tower_bt);
            return RET_FAIL;
@@ -621,7 +463,7 @@ build_bridge(char what)
        if (!player->owner)
            continue;
        gotsect = 1;
-       if (build_it(&sect, sect.sct_item))
+       if (build_it(&sect))
            putsect(&sect);
     }
     if (!gotsect)
@@ -630,17 +472,18 @@ build_bridge(char what)
 }
 
 static int
-build_bspan(struct sctstr *sp, short *vec)
+build_bspan(struct sctstr *sp)
 {
     struct sctstr sect;
+    short mat[I_MAX+1];
+    int work;
     int val;
     int newx, newy;
-    int avail;
-    int nx, ny, i, good = 0;
     char *p;
     char buf[1024];
 
-    if (opt_EASY_BRIDGES == 0) {       /* must have a bridge head or tower */
+    if (!opt_EASY_BRIDGES && !player->god) {
+       /* must have a bridge head or tower */
        if (sp->sct_type != SCT_BTOWER) {
            if (sp->sct_type != SCT_BHEAD)
                return 0;
@@ -649,29 +492,14 @@ build_bspan(struct sctstr *sp, short *vec)
        }
     }
 
-    if (sp->sct_effic < 60 && !player->god) {
-       pr("Sector %s is not 60%% efficient.\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       return 0;
-    }
+    memset(mat, 0, sizeof(mat));
+    mat[I_HCM] = buil_bh;
+    work = (SCT_BLD_WORK(0, buil_bh) * SCT_MINEFF + 99) / 100;
 
-    if (vec[I_HCM] < buil_bh) {
-       pr("%s only has %d unit%s of hcm,\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum),
-          vec[I_HCM], vec[I_HCM] > 1 ? "s" : "");
-       pr("(a bridge span requires %d)\n", buil_bh);
+    if (!sector_can_build(sp, mat, work, 100, dchr[SCT_BSPAN].d_name))
        return 0;
-    }
-
-    if (!build_can_afford(buil_bc, dchr[SCT_BSPAN].d_name))
+    if (!build_can_afford(buil_bc, 100, dchr[SCT_BSPAN].d_name))
        return 0;
-    avail = (SCT_BLD_WORK(0, buil_bh) * SCT_MINEFF + 99) / 100;
-    if (sp->sct_avail < avail) {
-       pr("Not enough available work in %s to build a bridge\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       pr(" (%d available work required)\n", avail);
-       return 0;
-    }
     if (!player->argp[3]) {
        pr("Bridge head at %s\n",
           xyas(sp->sct_x, sp->sct_y, player->cnum));
@@ -681,7 +509,6 @@ build_bspan(struct sctstr *sp, short *vec)
     if (!p || !*p) {
        return 0;
     }
-    /* Sanity check time */
     if (!check_sect_ok(sp))
        return 0;
 
@@ -696,25 +523,22 @@ build_bspan(struct sctstr *sp, short *vec)
        pr("%s is not a water sector\n", xyas(newx, newy, player->cnum));
        return 0;
     }
-    if (opt_EASY_BRIDGES) {
-       good = 0;
-
-       for (i = 1; i <= 6; i++) {
-           struct sctstr s2;
-           nx = sect.sct_x + diroff[i][0];
-           ny = sect.sct_y + diroff[i][1];
-           getsect(nx, ny, &s2);
-           if ((s2.sct_type != SCT_WATER) && (s2.sct_type != SCT_BSPAN))
-               good = 1;
-       }
-       if (!good) {
-           pr("Bridges must be built adjacent to land or bridge towers.\n");
-           pr("That sector is not adjacent to land or a bridge tower.\n");
-           return 0;
+    if (!bridge_support_at(&sect, DIR_STOP)) {
+       if (opt_EASY_BRIDGES) {
+           pr("%s is not next to land or a bridge tower",
+              xyas(newx, newy, player->cnum));
+       } else {
+           /*
+            * Note: because players need a 60% bridge head or tower,
+            * we can get here only for a deity.
+            */
+           pr("%s is not next to a supporting bridge head or tower\n",
+              xyas(newx, newy, player->cnum));
        }
-    }                          /* end EASY_BRIDGES */
-    sp->sct_avail -= avail;
-    player->dolcost += buil_bc;
+       return 0;
+    }
+    build_charge(sp, mat, work, buil_bc, 100);
+
     sect.sct_type = SCT_BSPAN;
     sect.sct_newtype = SCT_BSPAN;
     sect.sct_effic = SCT_MINEFF;
@@ -733,51 +557,36 @@ build_bspan(struct sctstr *sp, short *vec)
     putsect(&sect);
     pr("Bridge span built over %s\n",
        xyas(sect.sct_x, sect.sct_y, player->cnum));
-    vec[I_HCM] -= buil_bh;
     return 1;
 }
 
 static int
-build_btower(struct sctstr *sp, short *vec)
+build_btower(struct sctstr *sp)
 {
     struct sctstr sect;
+    short mat[I_MAX+1];
+    int work;
     int val;
     int newx, newy;
-    int avail;
     char *p;
     char buf[1024];
     int i;
     int nx;
     int ny;
 
-    if (sp->sct_type != SCT_BSPAN) {
+    if (sp->sct_type != SCT_BSPAN && !player->god) {
        pr("Bridge towers can only be built from bridge spans.\n");
        return 0;
     }
 
-    if (sp->sct_effic < 60 && !player->god) {
-       pr("Sector %s is not 60%% efficient.\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       return 0;
-    }
+    memset(mat, 0, sizeof(mat));
+    mat[I_HCM] = buil_tower_bh;
+    work = (SCT_BLD_WORK(0, buil_tower_bh) * SCT_MINEFF + 99) / 100;
 
-    if (vec[I_HCM] < buil_tower_bh) {
-       pr("%s only has %d unit%s of hcm,\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum),
-          vec[I_HCM], vec[I_HCM] > 1 ? "s" : "");
-       pr("(a bridge tower requires %d)\n", buil_tower_bh);
+    if (!sector_can_build(sp, mat, work, 100, dchr[SCT_BTOWER].d_name))
        return 0;
-    }
-
-    if (!build_can_afford(buil_tower_bc, dchr[SCT_BTOWER].d_name))
+    if (!build_can_afford(buil_tower_bc, 100, dchr[SCT_BTOWER].d_name))
        return 0;
-    avail = (SCT_BLD_WORK(0, buil_tower_bh) * SCT_MINEFF + 99) / 100;
-    if (sp->sct_avail < avail) {
-       pr("Not enough available work in %s to build a bridge tower\n",
-          xyas(sp->sct_x, sp->sct_y, player->cnum));
-       pr(" (%d available work required)\n", avail);
-       return 0;
-    }
     if (!player->argp[3]) {
        pr("Building from %s\n", xyas(sp->sct_x, sp->sct_y, player->cnum));
        nav_map(sp->sct_x, sp->sct_y, 1);
@@ -786,7 +595,6 @@ build_btower(struct sctstr *sp, short *vec)
     if (!p || !*p) {
        return 0;
     }
-    /* Sanity check time */
     if (!check_sect_ok(sp))
        return 0;
 
@@ -811,14 +619,14 @@ build_btower(struct sctstr *sp, short *vec)
        getsect(nx, ny, &s2);
        if ((s2.sct_type != SCT_WATER) &&
            (s2.sct_type != SCT_BTOWER) && (s2.sct_type != SCT_BSPAN)) {
-           pr("Bridge towers cannot be built adjacent to land.\n");
-           pr("That sector is adjacent to land.\n");
+           pr("%s is next to land, can't build bridge tower there",
+              xyas(newx, newy, player->cnum));
            return 0;
        }
     }
 
-    sp->sct_avail -= avail;
-    player->dolcost += buil_tower_bc;
+    build_charge(sp, mat, work, buil_tower_bc, 100);
+
     sect.sct_type = SCT_BTOWER;
     sect.sct_newtype = SCT_BTOWER;
     sect.sct_effic = SCT_MINEFF;
@@ -837,15 +645,69 @@ build_btower(struct sctstr *sp, short *vec)
     putsect(&sect);
     pr("Bridge tower built in %s\n",
        xyas(sect.sct_x, sect.sct_y, player->cnum));
-    vec[I_HCM] -= buil_tower_bh;
     return 1;
 }
 
 static int
-build_can_afford(double cost, char *what)
+sector_can_build(struct sctstr *sp, short mat[], int work,
+                int effic, char *what)
+{
+    int i, avail, ret;
+    double needed;
+
+    if (player->god)
+       return 1;               /* Deity builds ex nihilo */
+
+    if (sp->sct_effic < 60 && !player->god) {
+       pr("Sector %s is not 60%% efficient.\n",
+          xyas(sp->sct_x, sp->sct_y, player->cnum));
+       return 0;
+    }
+
+    avail = (work * effic + 99) / 100;
+    if (sp->sct_avail < avail) {
+       pr("Not enough available work in %s to build a %s\n",
+          xyas(sp->sct_x, sp->sct_y, player->cnum), what);
+       pr(" (%d available work required)\n", avail);
+       return 0;
+    }
+
+    ret = 1;
+    for (i = I_NONE + 1; i <= I_MAX; i++) {
+       needed = mat[i] * (effic / 100.0);
+       if (sp->sct_item[i] < needed) {
+           pr("Not enough %s in %s (need %g more)\n",
+              ichr[i].i_name, xyas(sp->sct_x, sp->sct_y, player->cnum),
+              ceil(needed - sp->sct_item[i]));
+           ret = 0;
+       }
+       mat[i] = roundavg(needed);
+    }
+
+    return ret;
+}
+
+static void
+build_charge(struct sctstr *sp,
+            short mat[], int work, double cost, int effic)
+{
+    int i;
+
+    if (player->god)
+       return;                 /* Deity builds ex nihilo */
+
+    for (i = I_NONE + 1; i <= I_MAX; i++)
+       sp->sct_item[i] -= mat[i];
+    sp->sct_avail -= (work * effic + 99) / 100;
+    player->dolcost += cost * effic / 100.0;
+}
+
+static int
+build_can_afford(double cost, int effic, char *what)
 {
     struct natstr *natp = getnatp(player->cnum);
-    if (natp->nat_money < player->dolcost + cost) {
+
+    if (natp->nat_money < player->dolcost + cost * effic / 100.0) {
        pr("Not enough money left to build a %s\n", what);
        return 0;
     }