]> git.pond.sub.org Git - empserver/blobdiff - src/lib/subs/attsub.c
Update copyright notice
[empserver] / src / lib / subs / attsub.c
index 5fdf81dc085200fee2bca591538515c75c48e69c..b9242f962fc4ac69f2ac37edc594e7210972d87f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2014, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *  Copyright (C) 1986-2021, Dave Pare, Jeff Bailey, Thomas Ruschak,
  *                Ken Stevens, Steve McClure, Markus Armbruster
  *
  *  Empire is free software: you can redistribute it and/or modify
@@ -29,7 +29,7 @@
  *  Known contributors to this file:
  *     Ken Stevens, 1995
  *     Steve McClure, 1996-2000
- *     Markus Armbruster, 2006-2013
+ *     Markus Armbruster, 2006-2016
  */
 
 #include <config.h>
@@ -39,7 +39,6 @@
 #include "chance.h"
 #include "combat.h"
 #include "empobj.h"
-#include "file.h"
 #include "map.h"
 #include "misc.h"
 #include "mission.h"
@@ -189,7 +188,8 @@ att_get_combat(struct combat *com, int isdef)
        y = com->y;
        break;
     case EF_LAND:
-       if (!getland(com->lnd_uid, &land) || !land.lnd_own) {
+       if (!getland(com->lnd_uid, &land) || !land.lnd_own
+           || land.lnd_ship >= 0 || land.lnd_land >= 0) {
            if (isdef)
                pr("Land unit #%d is not in the same sector!\n",
                   com->lnd_uid);
@@ -415,6 +415,11 @@ put_combat(struct combat *com)
        putship(com->shp_uid, &ship);
     }
     com->mobcost = 0;
+    /*
+     * FIXME if we just sank the ship, att_get_combat() will report
+     * "not in the same sector", and proceed to clobber *com.  See
+     * also the workaround in boar().
+     */
     att_get_combat(com, com->own != player->cnum);
 }
 
@@ -740,8 +745,8 @@ att_ask_offense(int combat_mode, struct combat *off, struct combat *def,
 }
 
 /*
- * Return path cost for ATTACKER to enter sector given by DEF.
- * MOBTYPE is a mobility type accepted by sector_mcost().
+ * Return path cost for @attacker to enter sector given by @def.
+ * @mobtype is a mobility type accepted by sector_mcost().
  */
 static double
 att_mobcost(natid attacker, struct combat *def, int mobtype)
@@ -851,7 +856,7 @@ calc_mobcost(int combat_mode, struct combat *off, struct combat *def,
        case EF_SHIP:
            /* the 2 in the formula below is a fudge factor */
            getship(def->shp_uid, &ship);
-           off->mobcost += (def->eff / 100) * (shp_speed(&ship) / 2);
+           off->mobcost += shp_speed(&ship) / 2 * def->eff / 100;
        }
     }
 }
@@ -954,7 +959,7 @@ ask_olist(int combat_mode, struct combat *off, struct combat *def,
     while (nxtitem(&ni, &land)) {
        if (land.lnd_own != player->cnum)
            continue;
-       if (land.lnd_effic < LAND_MINEFF)
+       if (!land.lnd_own)
            continue;
        if (land_answer[(int)land.lnd_army] == 'N')
            continue;
@@ -1037,7 +1042,14 @@ ask_olist(int combat_mode, struct combat *off, struct combat *def,
            return;
        }
        att_val = attack_val(combat_mode, &land);
-       if (att_val < 1.0) {
+       /*
+        * We need to let spies assault even though they have no
+        * offensive strength, because assault is how they sneak
+        * ashore.  If this assault turns out to be a fight, they'll
+        * be removed by get_ototal().
+        */
+       if (att_val < 1.0
+           && !(combat_mode == A_ASSAULT && (lcp->l_flags & L_SPY))) {
            pr("%s has no offensive strength\n", prland(&land));
            continue;
        }
@@ -1068,22 +1080,15 @@ ask_olist(int combat_mode, struct combat *off, struct combat *def,
                land_answer[(int)land.lnd_army] != 'Y')
                continue;
        }
-       if (!(llp = malloc(sizeof(struct ulist)))) {
-           logerror("Malloc failed in attack!\n");
-           abort_attack();
-           return;
-       }
-       memset(llp, 0, sizeof(struct ulist));
-       emp_insque(&llp->queue, olist);
+       llp = lnd_insque(&land, olist);
+       llp->supplied = 1;
        llp->mobil = mobcost;
-       llp->unit.land = land;
        llp->x = llp->unit.land.lnd_x;
        llp->y = llp->unit.land.lnd_y;
-       llp->chrp = (struct empobj_chr *)&lchr[(int)llp->unit.land.lnd_type];
        llp->eff = llp->unit.land.lnd_effic;
        if (lnd_spyval(&land) > *a_spyp)
            *a_spyp = lnd_spyval(&land);
-       if (((struct lchrstr *)llp->chrp)->l_flags & L_ENGINEER)
+       if (lchr[land.lnd_type].l_flags & L_ENGINEER)
            ++*a_engineerp;
        if (def->type == EF_SHIP && ++count >= maxland)
            break;
@@ -1192,24 +1197,19 @@ get_dlist(struct combat *def, struct emp_qelem *list, int a_spy,
            continue;
        if (def->type == EF_SECTOR && land.lnd_land >= 0)
            continue;
+       if (def->type == EF_SECTOR && (lchr[land.lnd_type].l_flags & L_SPY))
+           continue;
        if (def->type == EF_SHIP && land.lnd_ship != def->shp_uid)
            continue;
        if (def->type == EF_LAND && land.lnd_land != def->lnd_uid)
            continue;
        intelligence_report(player->cnum, &land, a_spy,
                            "Scouts report defending unit:");
-       if (!(llp = malloc(sizeof(struct ulist)))) {
-           logerror("Malloc failed in attack!\n");
-           abort_attack();
-           return;
-       }
-       memset(llp, 0, sizeof(struct ulist));
-       emp_insque(&llp->queue, list);
+       llp = lnd_insque(&land, list);
        llp->supplied = lnd_supply_all(&land);
-       llp->unit.land = land;
+       llp->mobil = 0.0;
        llp->x = llp->unit.land.lnd_x;
        llp->y = llp->unit.land.lnd_y;
-       llp->chrp = (struct empobj_chr *)&lchr[(int)llp->unit.land.lnd_type];
        llp->eff = llp->unit.land.lnd_effic;
        if (lnd_spyval(&land) > *d_spyp)
            *d_spyp = lnd_spyval(&land);
@@ -1226,6 +1226,7 @@ get_ototal(int combat_mode, struct combat *off, struct emp_qelem *olist,
     struct emp_qelem *qp, *next;
     struct ulist *llp;
     int n, w;
+    double att_val;
 
     /*
      * first, total the attacking mil
@@ -1248,6 +1249,18 @@ get_ototal(int combat_mode, struct combat *off, struct emp_qelem *olist,
        llp = (struct ulist *)qp;
        if (check && !get_oland(combat_mode, llp))
            continue;
+       att_val = attack_val(combat_mode, &llp->unit.land);
+       if (check && att_val < 1.0) {
+           /*
+            * No offensive strength, and fighting hasn't even begun.
+            * Since ask_olist() doesn't offer such land units, except
+            * for spies sometimes, it's either a spy, or the strength
+            * must have been destroyed since then.  Leave it behind.
+            */
+           lnd_print(player->cnum, llp, "has no offensive strength");
+           lnd_put_one(llp);
+           continue;
+       }
        if (combat_mode == A_ATTACK) {
            w = -1;
            for (n = 0; n <= off->last; ++n) {
@@ -1260,14 +1273,12 @@ get_ototal(int combat_mode, struct combat *off, struct emp_qelem *olist,
            if (w < 0) {
                lnd_print(player->cnum, llp,
                          "can't attack from this sector now");
-               lnd_delete(llp);
+               lnd_put_one(llp);
                continue;
            }
-           ototal += attack_val(combat_mode, &llp->unit.land) *
-               att_combat_eff(off + w);
-       } else {
-           ototal += attack_val(combat_mode, &llp->unit.land);
+           att_val *= att_combat_eff(off + w);
        }
+       ototal += att_val;
     }
     ototal *= osupport;
 
@@ -1326,7 +1337,7 @@ get_oland(int combat_mode, struct ulist *llp)
        sprintf(buf, "was destroyed and is no longer a part of the %s",
                att_mode[combat_mode]);
        lnd_print(player->cnum, llp, buf);
-       lnd_delete(llp);
+       lnd_put_one(llp);
        return 0;
     }
     if (lp->lnd_x != llp->x || lp->lnd_y != llp->y) {
@@ -1334,7 +1345,7 @@ get_oland(int combat_mode, struct ulist *llp)
                "left to fight another battle and is no longer a part of the %s",
                att_mode[combat_mode]);
        lnd_print(player->cnum, llp, buf);
-       lnd_delete(llp);
+       lnd_put_one(llp);
        return 0;
     }
     if (lp->lnd_effic < llp->eff) {
@@ -1351,20 +1362,19 @@ static int
 get_dland(struct combat *def, struct ulist *llp)
 {
     struct lndstr *lp = &llp->unit.land;
-    char buf[512];
 
     getland(llp->unit.land.lnd_uid, lp);
 
-    if (lp->lnd_effic < LAND_MINEFF) {
-       sprintf(buf, "was destroyed and is no longer a part of the defense");
-       lnd_print(llp->unit.land.lnd_own, llp, buf);
-       lnd_delete(llp);
+    if (lp->lnd_own != def->own) {
+       lnd_print(llp->unit.land.lnd_own, llp,
+                 "was destroyed and is no longer a part of the defense");
+       lnd_put_one(llp);
        return 0;
     }
     if (lp->lnd_x != def->x || lp->lnd_y != def->y) {
        lnd_print(llp->unit.land.lnd_own, llp,
                  "left to go fight another battle and is no longer a part of the defense");
-       lnd_delete(llp);
+       lnd_put_one(llp);
        return 0;
     }
 
@@ -1385,7 +1395,7 @@ kill_land(struct emp_qelem *list)
            llp->unit.land.lnd_effic = 0;
            lnd_print(player->cnum, llp,
                      "cannot return to the ship, and dies!");
-           lnd_delete(llp);
+           lnd_put_one(llp);
        }
     }
 }
@@ -1428,7 +1438,7 @@ put_oland(struct emp_qelem *list)
        llp->mobil = 0.0;
        putland(llp->unit.land.lnd_uid, &llp->unit.land);
        if (llp->unit.land.lnd_own != player->cnum) {
-           emp_remque((struct emp_qelem *)llp);
+           emp_remque(&llp->queue);
            free(llp);
        } else
            get_oland(A_ATTACK, llp);
@@ -1507,16 +1517,12 @@ att_reacting_units(struct combat *def, struct emp_qelem *list, int a_spy,
        wu(0, land.lnd_own, "%s reacts to %s.\n",
           prland(&land), xyas(land.lnd_x, land.lnd_y, land.lnd_own));
 
-       llp = malloc(sizeof(struct ulist));
-
-       memset(llp, 0, sizeof(struct ulist));
+       llp = lnd_insque(&land, list);
        llp->supplied = 1;
+       llp->mobil = 0.0;
        llp->x = origx;
        llp->y = origy;
-       llp->chrp = (struct empobj_chr *)&lchr[(int)land.lnd_type];
-       llp->unit.land = land;
        llp->eff = land.lnd_effic;
-       emp_insque(&llp->queue, list);
        if (lnd_spyval(&land) > *d_spyp)
            *d_spyp = lnd_spyval(&land);
 
@@ -1560,8 +1566,7 @@ get_osupport(char *outs, struct combat *def, int fort_sup, int ship_sup,
        ap = dam / 100.0;
        osupport += ap;
     }
-    sprintf(outs, "attacker\t%1.2f\t%1.2f\t%1.2f\t%1.2f\n", af, as, au,
-           ap);
+    sprintf(outs, "attacker\t%1.2f\t%1.2f\t%1.2f\t%1.2f", af, as, au, ap);
     return osupport;
 }
 
@@ -1608,8 +1613,8 @@ get_dsupport(char *outs, struct emp_qelem *list, struct combat *def,
     if (good)
        *outs = '\0';
     else
-       sprintf(outs, "defender\t%1.2f\t%1.2f\t%1.2f\t%1.2f\n\n", df, ds,
-               du, dp);
+       sprintf(outs, "defender\t%1.2f\t%1.2f\t%1.2f\t%1.2f",
+               df, ds, du, dp);
     if (def->own) {
        if (good < 0)
            wu(0, def->own,
@@ -1693,16 +1698,18 @@ att_get_support(int combat_mode, int ofort, int oship, int oland,
        pr("\n\t\tsupport values\n");
        pr("\t\tforts\tships\tunits\tplanes\n");
        if (*osupportp != 1.0)
-           pr("%s", osupports);
+           pr("%s\n", osupports);
        if (*dsupportp != 1.0)
-           pr("%s", dsupports);
+           pr("%s\n", dsupports);
+       pr("\n");
        if (def->own) {
            wu(0, def->own, "\n\t\tsupport values\n");
            wu(0, def->own, "\t\tforts\tships\tunits\tplanes\n");
            if (*osupportp != 1.0)
-               wu(0, def->own, "%s", osupports);
+               wu(0, def->own, "%s\n", osupports);
            if (*dsupportp != 1.0)
-               wu(0, def->own, "%s", dsupports);
+               wu(0, def->own, "%s\n", dsupports);
+           wu(0, def->own, "\n");
        }
     }
 
@@ -1895,7 +1902,7 @@ att_fight(int combat_mode, struct combat *off, struct emp_qelem *olist,
 
     pr("- Casualties -\n     Yours: %d\n", a_cas);
     pr("    Theirs: %d\n", d_cas);
-    pr("Papershuffling ... %.1f B.T.U\n", (d_cas + a_cas) * 0.15);
+    pr("Paper-shuffling ... %.1f BTU\n", (d_cas + a_cas) * 0.15);
     player->btused += (int)((d_cas + a_cas) * 0.015 + 0.5);
 
     if (success) {
@@ -1977,7 +1984,7 @@ att_fight(int combat_mode, struct combat *off, struct emp_qelem *olist,
     send_reacting_units_home(dlist);
 
     /* putland the defending land */
-    unit_put(dlist, 0);
+    lnd_put(dlist);
 
     /* putland the attacking land */
     put_oland(olist);
@@ -2037,8 +2044,8 @@ take_casualty(int combat_mode, struct combat *off, struct emp_qelem *olist)
     int to_take = CASUALTY_LUMP;
     int biggest_troops = 0, index = -1;
     int n, tot_troops = 0, biggest_mil, cas;
-    struct emp_qelem *qp, *biggest;
-    struct ulist *llp;
+    struct emp_qelem *qp;
+    struct ulist *llp, *biggest;
 
     for (n = 0; n <= off->last; ++n) {
        if (off[n].type != EF_BAD) {
@@ -2099,14 +2106,13 @@ take_casualty(int combat_mode, struct combat *off, struct emp_qelem *olist)
 
        if (llp->unit.land.lnd_item[I_MILIT] > biggest_mil) {
            biggest_mil = llp->unit.land.lnd_item[I_MILIT];
-           biggest = qp;
+           biggest = llp;
        }
     }
     if (biggest == NULL)
        return CASUALTY_LUMP - to_take;
 
-    llp = (struct ulist *)biggest;
-    cas = lnd_take_casualty(combat_mode, llp, to_take);
+    cas = lnd_take_casualty(combat_mode, biggest, to_take);
     return CASUALTY_LUMP - (to_take - cas);
 }
 
@@ -2129,7 +2135,7 @@ send_reacting_units_home(struct emp_qelem *list)
            llp->unit.land.lnd_x = llp->x;
            llp->unit.land.lnd_y = llp->y;
            lnd_print(llp->unit.land.lnd_own, llp, buf);
-           lnd_delete(llp);
+           lnd_put_one(llp);
        }
     }
 }
@@ -2209,7 +2215,7 @@ take_def(int combat_mode, struct emp_qelem *list, struct combat *off,
                sprintf(buf, "moves in to occupy %s",
                        xyas(def->x, def->y, player->cnum));
                lnd_print(player->cnum, llp, buf);
-               lnd_delete(llp);
+               lnd_put_one(llp);
            }
        }
     }
@@ -2231,7 +2237,7 @@ take_def(int combat_mode, struct emp_qelem *list, struct combat *off,
        putland(land.lnd_uid, &land);
     }
     if (delete_me)
-       lnd_delete(delete_me);
+       lnd_put_one(delete_me);
     att_get_combat(def, 0);
     return 1;
 }
@@ -2291,7 +2297,7 @@ ask_move_in(struct combat *off, struct emp_qelem *olist,
                xyas(llp->unit.land.lnd_x, llp->unit.land.lnd_y,
                     player->cnum));
        lnd_print(player->cnum, llp, buf);
-       lnd_delete(llp);
+       lnd_put_one(llp);
     }
     if (QEMPTY(olist))
        return;
@@ -2305,7 +2311,7 @@ ask_move_in(struct combat *off, struct emp_qelem *olist,
                    xyas(llp->unit.land.lnd_x, llp->unit.land.lnd_y,
                         player->cnum));
            lnd_print(player->cnum, llp, buf);
-           lnd_delete(llp);
+           lnd_put_one(llp);
        }
        return;
     }
@@ -2314,18 +2320,13 @@ ask_move_in(struct combat *off, struct emp_qelem *olist,
     move_in_land(A_ATTACK, off, olist, def);
 }
 
-/* Move offensive land units to the conquered sector or ship */
-
-static void
-move_in_land(int combat_mode, struct combat *off, struct emp_qelem *olist,
-            struct combat *def)
+void
+att_move_land(int combat_mode, struct combat *off, struct emp_qelem *olist,
+             struct combat *def)
 {
     struct emp_qelem *qp, *next;
     struct ulist *llp;
-    char buf[512];
 
-    if (QEMPTY(olist))
-       return;
     for (qp = olist->q_forw; qp != olist; qp = next) {
        next = qp->q_forw;
        llp = (struct ulist *)qp;
@@ -2339,8 +2340,20 @@ move_in_land(int combat_mode, struct combat *off, struct emp_qelem *olist,
        else
            llp->unit.land.lnd_ship = -1;
     }
-    if (QEMPTY(olist))
-       return;
+}
+
+/* Move offensive land units to the conquered sector or ship */
+
+static void
+move_in_land(int combat_mode, struct combat *off, struct emp_qelem *olist,
+            struct combat *def)
+{
+    struct emp_qelem *qp, *next;
+    struct ulist *llp;
+    char buf[512];
+
+    att_move_land(combat_mode, off, olist, def);
+
     if (def->type == EF_SECTOR) {
        if (opt_INTERDICT_ATT) {
            lnd_sweep(olist, 0, 0, player->cnum);
@@ -2350,16 +2363,14 @@ move_in_land(int combat_mode, struct combat *off, struct emp_qelem *olist,
     } else {
        sprintf(buf, "boards %s", prcom(0, def));
     }
-    if (QEMPTY(olist))
-       return;
+
     for (qp = olist->q_forw; qp != olist; qp = next) {
        next = qp->q_forw;
        llp = (struct ulist *)qp;
        lnd_print(player->cnum, llp, buf);
     }
-    if (QEMPTY(olist))
-       return;
-    unit_put(olist, 0);
+
+    lnd_put(olist);
 }
 
 /*
@@ -2507,7 +2518,7 @@ take_move_in_mob(int combat_mode, struct ulist *llp, struct combat *off,
         * Set mobcost to basic assault cost, moblim to maximum
         * mobility to keep when assaulting from non-landing ship
         */
-       if (((struct lchrstr *)llp->chrp)->l_flags & L_MARINE) {
+       if (lchr[llp->unit.land.lnd_type].l_flags & L_MARINE) {
            mobcost = gain / 2.0;
            moblim = 0;
        } else {
@@ -2519,7 +2530,7 @@ take_move_in_mob(int combat_mode, struct ulist *llp, struct combat *off,
            mobcost = MAX(mobcost, mob - moblim);
        break;
     case A_BOARD:
-       if (((struct lchrstr *)llp->chrp)->l_flags & L_MARINE)
+       if (lchr[llp->unit.land.lnd_type].l_flags & L_MARINE)
            mobcost = 10;
        else
            mobcost = 40;