empserver/src/lib/commands/tend.c
Markus Armbruster 3e370da58c Get rid of ship and land unit load counters
Load counters are redundant; they can be computed from the carrier
uids.  Keeping them up-to-date as the carriers change is a pain, and
we never got that quite complete.

Computing load counters straight from the carrier uids every time we
need them would be rather inefficient, but computing them from cargo
lists is not.  So do that.

Remove the load counters: struct shpstr members shp_nplane,
shp_nchoppers, shp_nxlight, shp_nland, and struct lndstr members
lnd_nxlight and lnd_nland.

Don't compute/update load counters in build_ship(), build_land(),
land(), ldump(), load_plane_ship(), load_land_ship(),
load_plane_land(), load_land_land(), lstat(), sdump(), shi(), sstat(),
tend_land(), check_trade(), put_combat(), pln_oneway_to_carrier_ok),
pln_newlanding(), fit_plane_on_ship(), fit_plane_on_land(),
unit_list().

Nothing left in fit_plane_off_ship(), fit_plane_off_land(), so remove
them.

load_land_ship(), load_land_land(), check_trade(), pln_newlanding(),
put_plane_on_ship(), take_plane_off_ship(), put_plane_on_land(),
take_plane_off_land() no longer change the carrier, so don't put it.

Remove functions to recompute the load counters from carrier uids:
count_units(), lnd_count_units(), count_planes(), count_land_planes(),
pln_fixup() and lnd_fixup(), along with the latter two's private
copies of fit_plane_on_ship() and fit_plane_on_land().

New cargo list functions to compute load counts: unit_cargo_count()
and unit_nplane(), with convenience wrappers shp_nplane(),
shp_nland(), lnd_nxlight(), lnd_nland().

Use them to make ship selectors nplane, nchoppers, nxlight, nland
virtual.  They now reflect what is loaded, not how the load uses the
available slots.  This makes a difference when x-light planes or
choppers use plane slots.

Use them to make land unit selectors nxlight and nland virtual.

Use them to get load counts in land(), ldump(), load_plane_ship(),
load_land_ship(), load_plane_land(), load_land_land(), sdump(), shi(),
tend_land(), fit_plane_on_land(), trade_desc(), unit_list().

Rewrite fit_plane_on_ship() and could_be_on_ship() to use
shp_nplane().  could_be_on_ship() now takes load count arguments, as
computed by shp_nplane(), so it can be used for checking against an
existing load as well.
2008-09-08 21:32:56 -04:00

299 lines
8.4 KiB
C

/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See files README, COPYING and CREDITS in the root of the source
* tree for related information and legal notices. It is expected
* that future projects/authors will amend these files as needed.
*
* ---
*
* tend.c: Transfer goodies from one ship to another.
*
* Known contributors to this file:
* Dave Pare, 1986
* Thomas Ruschak, 1992
* Steve McClure, 2000
*/
#include <config.h>
#include "commands.h"
#include "item.h"
#include "land.h"
#include "plague.h"
#include "plane.h"
#include "ship.h"
static void expose_ship(struct shpstr *s1, struct shpstr *s2);
static int tend_land(struct shpstr *tenderp, char *units);
int
tend(void)
{
struct nstr_item targets;
struct nstr_item tenders;
struct shpstr tender;
struct shpstr target;
struct ichrstr *ip;
struct mchrstr *vbase;
int amt;
int retval;
int ontender;
int ontarget;
int maxtender;
int maxtarget;
int transfer;
int total;
int type;
char *p;
char prompt[512];
char buf[1024];
if (!(p = getstarg(player->argp[1],
"Tend what commodity (or 'land')? ", buf)) || !*p)
return RET_SYN;
if (!strncmp(p, "land", 4))
type = EF_LAND;
else if (NULL != (ip = item_by_name(p)))
type = EF_SECTOR;
else {
pr("Can't tend '%s'\n", p);
return RET_SYN;
}
if (!snxtitem(&tenders, EF_SHIP, player->argp[2], "Tender(s)? "))
return RET_SYN;
while (nxtitem(&tenders, &tender)) {
if (!player->owner)
continue;
if (type == EF_LAND) {
sprintf(prompt, "Land unit(s) to tend from %s? ",
prship(&tender));
p = getstarg(player->argp[3], prompt, buf);
if (!p)
return RET_FAIL;
if (!*p)
continue;
if (!check_ship_ok(&tender))
return RET_SYN;
if (0 != (retval = tend_land(&tender, p)))
return retval;
continue;
}
sprintf(prompt, "Number of %s to tend from %s? ",
ip->i_name, prship(&tender));
p = getstarg(player->argp[3], prompt, buf);
if (!p)
return RET_FAIL;
if (!*p)
continue;
if (!check_ship_ok(&tender))
return RET_SYN;
if (!(amt = atoi(p))) {
pr("Amount must be non-zero!\n");
return RET_SYN;
}
ontender = tender.shp_item[ip->i_uid];
if (ontender == 0 && amt > 0) {
pr("No %s on %s\n", ip->i_name, prship(&tender));
return RET_FAIL;
}
vbase = &mchr[(int)tender.shp_type];
maxtender = vbase->m_item[ip->i_uid];
if (maxtender == 0) {
pr("A %s cannot hold any %s\n",
mchr[(int)tender.shp_type].m_name, ip->i_name);
break;
}
if (!snxtitem(&targets, EF_SHIP,
player->argp[4], "Ships to be tended? "))
return RET_FAIL;
if (!check_ship_ok(&tender))
return RET_SYN;
total = 0;
while (nxtitem(&targets, &target)) {
if (!player->owner &&
(getrel(getnatp(target.shp_own), player->cnum) < FRIENDLY))
continue;
if (target.shp_uid == tender.shp_uid)
continue;
if (tender.shp_x != target.shp_x ||
tender.shp_y != target.shp_y)
continue;
ontarget = target.shp_item[ip->i_uid];
if (ontarget == 0 && amt < 0) {
pr("No %s on %s\n", ip->i_name, prship(&target));
continue;
}
vbase = &mchr[(int)target.shp_type];
maxtarget = vbase->m_item[ip->i_uid];
if (amt < 0) {
if (!player->owner)
amt = 0;
/* take from target and give to tender */
transfer = MIN(ontarget, -amt);
transfer = MIN(maxtender - ontender, transfer);
if (transfer == 0)
continue;
target.shp_item[ip->i_uid] = ontarget - transfer;
ontender += transfer;
total += transfer;
} else {
/* give to target from tender */
transfer = MIN(ontender, amt);
transfer = MIN(transfer, maxtarget - ontarget);
if (transfer == 0)
continue;
target.shp_item[ip->i_uid] = ontarget + transfer;
ontender -= transfer;
total += transfer;
if (transfer && target.shp_own != player->cnum) {
wu(0, target.shp_own, "%s tended %d %s to %s\n",
cname(player->cnum), total, ip->i_name,
prship(&target));
}
}
expose_ship(&tender, &target);
putship(target.shp_uid, &target);
if (amt > 0 && ontender == 0) {
pr("%s out of %s\n", prship(&tender), ip->i_name);
break;
}
}
pr("%d total %s transferred %s %s\n",
total, ip->i_name, (amt > 0) ? "off of" : "to",
prship(&tender));
tender.shp_item[ip->i_uid] = ontender;
tender.shp_mission = 0;
putship(tender.shp_uid, &tender);
}
return RET_OK;
}
static void
expose_ship(struct shpstr *s1, struct shpstr *s2)
{
if (s1->shp_pstage == PLG_INFECT && s2->shp_pstage == PLG_HEALTHY)
s2->shp_pstage = PLG_EXPOSED;
if (s2->shp_pstage == PLG_INFECT && s1->shp_pstage == PLG_HEALTHY)
s1->shp_pstage = PLG_EXPOSED;
}
static int
tend_land(struct shpstr *tenderp, char *units)
{
struct nstr_item lni;
struct nstr_item targets;
struct shpstr target;
struct lndstr land;
struct plnstr plane;
struct nstr_item pni;
char buf[1024];
if (!snxtitem(&lni, EF_LAND, units, NULL))
return RET_SYN;
while (nxtitem(&lni, &land)) {
if (!player->owner)
continue;
if (land.lnd_ship != tenderp->shp_uid) {
pr("%s is not on %s!\n", prland(&land), prship(tenderp));
continue;
}
if (!(lchr[(int)land.lnd_type].l_flags & L_ASSAULT)) {
pr("%s does not have \"assault\" capability and can't be tended\n",
prland(&land));
continue;
}
if (!snxtitem(&targets, EF_SHIP,
player->argp[4], "Ship to be tended? "))
return RET_FAIL;
if (!check_land_ok(&land))
return RET_SYN;
while (nxtitem(&targets, &target)) {
if (!player->owner &&
(getrel(getnatp(target.shp_own), player->cnum) < FRIENDLY))
continue;
if (target.shp_uid == tenderp->shp_uid)
continue;
if (tenderp->shp_x != target.shp_x ||
tenderp->shp_y != target.shp_y)
continue;
/* Fit unit on ship */
getship(target.shp_uid, &target);
if ((!(lchr[(int)land.lnd_type].l_flags & L_LIGHT)) &&
(!((mchr[(int)target.shp_type].m_flags & M_SUPPLY) &&
(!(mchr[(int)target.shp_type].m_flags & M_SUB))))) {
pr("You can only load light units onto ships,\n"
"unless the ship is a non-sub supply ship\n"
"%s not tended\n", prland(&land));
continue;
}
if ((mchr[(int)target.shp_type].m_flags & M_SUB) &&
(lchr[(int)land.lnd_type].l_flags & L_SPY) &&
!mchr[(int)target.shp_type].m_nland) {
if (shp_nland(&target) > 1) {
pr("%s doesn't have room for more than two spy units!\n",
prship(&target));
continue;
}
} else if (shp_nland(&target) >= mchr[target.shp_type].m_nland) {
if (mchr[(int)target.shp_type].m_nland)
pr("%s doesn't have room for any more land units!\n",
prship(&target));
else
pr("%s doesn't carry land units!\n", prship(&target));
continue;
}
pr("%s transferred from %s to %s\n",
prland(&land), prship(tenderp), prship(&target));
sprintf(buf, "loaded on your %s at %s",
prship(&target), xyas(target.shp_x, target.shp_y,
target.shp_own));
gift(target.shp_own, player->cnum, &land, buf);
land.lnd_ship = target.shp_uid;
land.lnd_harden = 0;
land.lnd_mission = 0;
putland(land.lnd_uid, &land);
expose_ship(tenderp, &target);
putship(target.shp_uid, &target);
putship(tenderp->shp_uid, tenderp);
snxtitem_xy(&pni, EF_PLANE, land.lnd_x, land.lnd_y);
while (nxtitem(&pni, &plane)) {
if (plane.pln_flags & PLN_LAUNCHED)
continue;
if (plane.pln_land != land.lnd_uid)
continue;
sprintf(buf, "loaded on %s", prship(&target));
gift(target.shp_own, player->cnum, &plane, buf);
plane.pln_mission = 0;
putplane(plane.pln_uid, &plane);
}
}
}
return 0;
}