empserver/src/lib/commands/mine.c
Markus Armbruster 3722bafaf7 Fix confusion of landmines with seamines
Seamines and landmines share storage.  Sea and bridge span sectors can
hold only sea mines, other sector types only landmines.  Sector type
checks were missing or incorrect in several places:

* Seamines under bridge spans were mistaken for landmines in several
  places:

  - ground combat mine defense bonus, in get_mine_dsupport() and
    stre(),

  - land units retreating from bombs, in retreat_land1(),

  - non-land unit ground movement (commands explore, move, transport,
    and INTERDICT_ATT of military), in check_lmines(),

  Fix them to check the sector type with new SCT_MINES_ARE_SEAMINES(),
  SCT_LANDMINES().

* plane_sweep() mistook landmines for seamines in harbors.  Bug could
  not bite, because it's only called for sea sectors.  Drop the bogus
  check for harbor.

* Collapsing a bridge tower magically converted landmines into
  seamines.  Make knockdown() clear landmines.

Also use SCT_MINES_ARE_SEAMINES() and SCT_LANDMINES() in mine(),
landmine(), lnd_sweep() and lnd_check_mines().  No functional change
there.

Keep checking only for sea in pln_mine(), plane_sweep(),
retreat_ship1(), shp_sweep() and shp_check_one_mines().  This means
seamines continue not to work under bridges.  Making them work there
is tempting, but as long as finding seamines clobbers the sector
designation in the bmap, it's better to have them in sea sectors only.

Historical notes:

Mines started out simple enough: you could mine sea and bridge spans,
and ships hit and swept mines in foreign sectors.

Chainsaw 2 introduced aerial mining and sweeping.  Unlike ships,
planes could not mine bridge spans.  plane_sweep() could sweep
harbors, which was wrong, but it was never called there, so the bug
could not bite.

Chainsaw 3 introduced landmines.  The idea was to permit only seamines
in some sector types, and only landmines in the others, so they can
share storage.  To figure out whether a sector has a particular kind
of mines, you need to check the sector type.  Such checks already
existed in mine, drop and sweep, and they were kept unchanged.  The
new lmine command also got the check.  Everything else did not.
Ground movement and combat could hit and sweep seamines in bridge
spans.  Ships could hit and sweep landmines in harbors.

Empire 2 fixed land unit movement (march, INTERDICT_ATT) not to
mistake seamines for landmines on bridge spans.  It fixed ships not to
mistake landmines for seamines.  The fix also neutered seamines under
bridge spans: ships could neither hit nor sweep them anymore.  Both
fixes missed retreat.

Commit 5663713b (v4.3.1) made ship retreat consistent with other ship
movement.
2009-03-31 22:52:03 +02:00

169 lines
4.9 KiB
C

/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2009, 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.
*
* ---
*
* mine.c: Lay mines from ships or units
*
* Known contributors to this file:
* Markus Armbruster, 2004-2009
*/
#include <config.h>
#include "commands.h"
#include "land.h"
#include "map.h"
#include "ship.h"
/*
* format: mine <SHIPS> <NUMBER MINES>
*/
int
mine(void)
{
struct shpstr ship;
struct sctstr sect;
struct mchrstr *mp;
struct nstr_item ni;
int mines;
int shells;
int mines_avail;
if (!snxtitem(&ni, EF_SHIP, player->argp[1], NULL))
return RET_SYN;
mines = onearg(player->argp[2],
"Drop how many mines from each ship? ");
if (mines <= 0)
return RET_SYN;
while (nxtitem(&ni, &ship)) {
if (!player->owner)
continue;
mp = &mchr[(int)ship.shp_type];
if ((mp->m_flags & M_MINE) == 0)
continue;
if ((shells = ship.shp_item[I_SHELL]) == 0)
continue;
mines_avail = MIN(shells, mines);
if (getsect(ship.shp_x, ship.shp_y, &sect) == 0 ||
!SCT_MINES_ARE_SEAMINES(&sect)) {
pr("You can't lay mines there!!\n");
continue;
}
sect.sct_mines = MIN(sect.sct_mines + mines_avail, MINES_MAX);
ship.shp_item[I_SHELL] = shells - mines_avail;
putsect(&sect);
ship.shp_mission = 0;
putship(ship.shp_uid, &ship);
pr("Laying %d mines from %s\n", mines_avail, prship(&ship));
if (mines_avail &&
map_set(player->cnum, sect.sct_x, sect.sct_y, 'X', 0))
writemap(player->cnum);
}
return RET_OK;
}
/*
* format: landmine <UNITS> <NUMBER MINES>
*/
int
landmine(void)
{
struct lndstr land;
struct sctstr sect;
struct nstr_item ni;
int todo;
int mines_wanted;
int mines_laid;
int total_mines_laid;
char prompt[128];
if (!snxtitem(&ni, EF_LAND, player->argp[1], NULL))
return RET_SYN;
while (nxtitem(&ni, &land)) {
if (!player->owner)
continue;
if (!(lchr[land.lnd_type].l_flags & L_ENGINEER))
continue;
if (land.lnd_ship >= 0 || land.lnd_land >= 0) {
pr("%s is on a %s\n", prland(&land),
land. lnd_ship >= 0 ? "ship" : "land unit");
continue;
}
if (land.lnd_mobil < 1) {
pr("%s is out of mobility\n", prland(&land));
continue;
}
if (!getsect(land.lnd_x, land.lnd_y, &sect)
|| SCT_MINES_ARE_SEAMINES(&sect)
|| sect.sct_own != land.lnd_own) {
pr("You can't lay mines there!!\n");
continue;
}
if (sect.sct_own == sect.sct_oldown)
pr("There are currently %d mines in %s\n",
sect.sct_mines, xyas(sect.sct_x, sect.sct_y, player->cnum));
sprintf(prompt, "Drop how many mines from %s? ", prland(&land));
mines_wanted = onearg(player->argp[2], prompt);
if (mines_wanted < 0)
return RET_FAIL;
if (mines_wanted == 0)
continue;
if (!check_land_ok(&land) || !check_sect_ok(&sect))
continue;
land.lnd_mission = 0;
todo = MIN(mines_wanted, land.lnd_mobil);
total_mines_laid = 0;
do {
lnd_supply(&land, I_SHELL, todo);
mines_laid = MIN(todo, land.lnd_item[I_SHELL]);
land.lnd_item[I_SHELL] -= mines_laid;
land.lnd_mobil -= mines_laid;
putland(land.lnd_uid, &land);
total_mines_laid += mines_laid;
todo -= mines_laid;
} while (todo && mines_laid);
lnd_supply_all(&land);
getsect(sect.sct_x, sect.sct_y, &sect);
sect.sct_mines = MIN(sect.sct_mines + total_mines_laid, MINES_MAX);
putsect(&sect);
if (total_mines_laid == mines_wanted) {
pr("%s laid a total of %d mines in %s",
prland(&land), total_mines_laid,
xyas(sect.sct_x, sect.sct_y, land.lnd_own));
if (!land.lnd_item[I_SHELL])
pr(" but is now out of shells\n");
else
pr("\n");
} else
pr("%s ran out of %s before it could finish the job\n"
"Only %d mines were laid in %s\n",
prland(&land),
land.lnd_mobil > 0 ? "shells" : "mobility",
total_mines_laid,
xyas(sect.sct_x, sect.sct_y, land.lnd_own));
}
return RET_OK;
}