autonav: Remove the feature

The autonavigation feature has issues:

* Autonavigation orders are executed at the update.  Crafty players
  can use them to get around the update window.

* Usability is poor:

  - The order command is overly complex, not least because it can do
    five different things: clear, suspend, resume, declare route, set
    cargo levels.

  - Unlike every other command involving movement, order does not let
    you specify routes, only destination sectors.

  - Setting cargo levels can silently swap start and end point of a
    circular route, because "this keeps the load_it() procedure
    happy".  Maybe it does, but it surely keeps players confused.

  - Setting "start" cargo levels actually sets the "end" levels, and
    vice versa.  Has always been broken that way.

  - Predicting what exactly autonavigation will do at the update isn't
    easy.

* The info pages documenting it amount to almost 400 non-blank lines
  formatted.  They claim only merchant ships can be given orders.
  This is wrong.  Unlikely to be the only error.

* Few players use it, and its workings at the update a fairly opaque.
  Makes it a nice hidey-hole for bugs.  Here are two:

  - Unlike the scuttle command, autonavigation happily scuttles trade
    ships while they're on the trading block.

  - Unlike the load command, autonavigation can load in friendly and
    allied sectors.

* It's more than 700 lines of rather crufty code nobody wants to
  touch.  Thanks to a big effort in Empire 2, it shares code with the
  navigation command.  It still duplicates load code.  The sharing
  complicates fixing the bugs demonstrated by navi-march-test.

Reviewing, fixing and testing this mess isn't worth the opportunity
cost.  Remove it instead.  Drop commands order, qorder and sorder.
Drop ship selectors xstart, xend, ystart, yend, cargostart, cargoend,
amtstart, amtend, autonav.

xdump ship sheds almost half its columns.  struct shpstr shrinks, on
my system from 200 to 160 bytes.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
This commit is contained in:
Markus Armbruster 2014-12-24 16:33:51 +01:00
parent a4e519c377
commit 48e656c057
37 changed files with 306 additions and 1764 deletions

View file

@ -1,330 +0,0 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2014, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure, Markus Armbruster
*
* Empire 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 3 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, see <http://www.gnu.org/licenses/>.
*
* ---
*
* 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.
*
* ---
*
* nav_ship.c: Navigate ships and such
*
* Known contributors to this file:
* Chad Zabel, 1994
* Ken Stevens, 1995
* Markus Armbruster, 2004-2011
*/
#include <config.h>
#include "nsc.h"
#include "path.h"
#include "update.h"
#include "empobj.h"
#include "unit.h"
static void swap(struct shpstr *);
static void
scuttle_it(struct shpstr *sp)
{
struct sctstr *sectp;
sp->shp_autonav &= ~AN_SCUTTLE;
if (!(sectp = getsectp(sp->shp_x, sp->shp_y))) {
wu(0, 0, "bad sector (%d,%d) ship %d\n",
sp->shp_x, sp->shp_y, sp->shp_uid);
return;
}
if (CANT_HAPPEN(!(mchr[sp->shp_type].m_flags & M_TRADE)))
return;
if (!scuttle_tradeship(sp, 0)) {
wu(0, sp->shp_own,
"%s doesn't pay here! Not scuttled.\n", prship(sp));
return;
}
wu(0, sp->shp_own, "Scuttling %s in sector %s\n",
prship(sp), xyas(sp->shp_x, sp->shp_y, sp->shp_own));
if (sectp->sct_own == sp->shp_own)
unit_drop_cargo((struct empobj *)sp, sectp->sct_own);
sp->shp_effic = 0;
putship(sp->shp_uid, sp);
}
static void
nav_check_atdest(struct shpstr *sp)
{
if ((sp->shp_x == sp->shp_destx[0]) && (sp->shp_y == sp->shp_desty[0])) {
if ((sp->shp_destx[0] == sp->shp_destx[1]) &&
(sp->shp_desty[0] == sp->shp_desty[1])) {
/* End of road */
sp->shp_autonav &= ~AN_AUTONAV;
wu(0, sp->shp_own, "%s arrived at %s, finished\n",
prship(sp), xyas(sp->shp_x, sp->shp_y, sp->shp_own));
if (sp->shp_autonav & AN_SCUTTLE) {
scuttle_it(sp);
}
} else {
/* unload all cargo */
unload_it(sp);
wu(0, sp->shp_own, "%s arrived at %s\n",
prship(sp), xyas(sp->shp_x, sp->shp_y, sp->shp_own));
/* Swap */
swap(sp);
}
} else
sp->shp_autonav &= ~AN_LOADING;
}
/* flip the 2 fields that deal with autonav movement. */
/* CZ 6/1/94 */
static void
swap(struct shpstr *sp)
{
coord tcord;
i_type tcomm[TMAX];
short lev[TMAX];
int i;
tcord = sp->shp_destx[0];
sp->shp_destx[0] = sp->shp_destx[1];
sp->shp_destx[1] = tcord;
tcord = sp->shp_desty[0];
sp->shp_desty[0] = sp->shp_desty[1];
sp->shp_desty[1] = tcord;
for (i = 0; i < TMAX; ++i) {
lev[i] = sp->shp_lstart[i];
tcomm[i] = sp->shp_tstart[i];
}
for (i = 0; i < TMAX; ++i) {
sp->shp_lstart[i] = sp->shp_lend[i];
sp->shp_tstart[i] = sp->shp_tend[i];
}
for (i = 0; i < TMAX; ++i) {
sp->shp_lend[i] = lev[i];
sp->shp_tend[i] = tcomm[i];
}
/* set load bit */
sp->shp_autonav |= AN_LOADING;
}
/* New Autonav code.
* Chad Zabel
* 6-1-94
*/
static int
nav_loadship(struct shpstr *sp)
{
struct sctstr *sectp;
int i, didsomething[TMAX], rel;
for (i = 0; i < TMAX; i++)
didsomething[i] = 0;
/* Turn off the loading flag.
* if any of the loads fail on the ship
* it will be turned back on.
*/
sp->shp_autonav &= ~AN_LOADING;
if (!(sectp = getsectp(sp->shp_x, sp->shp_y)))
return 0; /* safety */
rel = relations_with(sectp->sct_own, sp->shp_own);
/* loop through each field for that ship */
for (i = 0; i < TMAX; ++i) {
/* check and see if the data fields have been set. */
if (sp->shp_tend[i] == I_NONE || sp->shp_lend[i] == 0) {
/* nothing to do move on. */
didsomething[i] = 1;
continue;
}
if (sectp->sct_own == 0) {
/* either sea or deity harbor */
didsomething[i] = 1;
continue;
}
if (!sect_has_dock(sectp)) {
/* we can only load in harbors */
didsomething[i] = 1;
continue;
}
if (rel >= FRIENDLY)
didsomething[i] = load_it(sp, sectp, i);
}
/* check for any unsucessful loads */
/* if we have any return 0 to stop */
/* the nav_ship loop. */
for (i = 0; i < TMAX; i++) {
if (didsomething[i] == 0)
return 0;
}
/* All loads were succesful */
return 1;
}
static int
nav_load_ship_at_sea(struct shpstr *sp)
{
int i;
int n_items;
int max_amt, item_amt;
struct mchrstr *mcp;
struct sctstr *sectp;
struct check_list_st {
long cap;
i_type item;
} check_list[] = {{M_FOOD, I_FOOD}, {M_OIL, I_OIL}};
n_items = sizeof(check_list) / sizeof(check_list[0]);
mcp = &mchr[(int)sp->shp_type];
sectp = getsectp(sp->shp_x, sp->shp_y);
for (i = 0; i < n_items; i++) {
if (mcp->m_flags & check_list[i].cap) {
item_amt = sp->shp_item[check_list[i].item];
max_amt = mcp->m_item[check_list[i].item];
if (item_amt < max_amt && sectp->sct_type == SCT_WATER)
return 1;
}
}
return 0;
}
/* new autonav code.
*
* 1. Try and move to the next sector/harbor given by the player.
* 2. Once we reach a harbor try and load all cargo holds for that ship.
* 3. If the ship reaches its max levels set by the player try to use
* up all mobility getting to the next harbor.
* Continue to loop until the ship runs out of mobility, a load fails,
* the ship gets sunk (forts,ect..), the ship hits a mine.
*
* Questions, bugs (fixes) , or new ideas should be directed at
* Chad Zabel.
* 6-1-94
* Modified to use shp_nav by Ken Stevens 1995
*/
int
nav_ship(struct shpstr *sp)
{
char *cp;
int stopping;
int quit;
int didsomething = 0;
char buf[1024];
struct emp_qelem ship_list;
struct emp_qelem *qp, *newqp;
struct ulist *mlp;
int dummyint;
double dummydouble;
int dir;
/* just return if no autonaving to do for this ship */
if (!(sp->shp_autonav & AN_AUTONAV) || (sp->shp_autonav & AN_STANDBY))
return 0;
/* Make a list of one ships so we can use the navi.c code */
emp_initque(&ship_list);
mlp = shp_insque(sp, &ship_list);
ef_mark_fresh(EF_SHIP, &mlp->unit.ship);
do {
if ((sp->shp_mobil > 0) && (!(sp->shp_autonav & AN_LOADING)) &&
(!(sp->shp_autonav & AN_STANDBY))) {
shp_nav(&ship_list, &dummydouble, &dummydouble, &dummyint,
sp->shp_own);
if (QEMPTY(&ship_list))
return 0;
if (path_find(sp->shp_x, sp->shp_y,
sp->shp_destx[0], sp->shp_desty[0],
sp->shp_own, MOB_SAIL) < 0) {
wu(0, sp->shp_own,
"%s bad path, ship put on standby\n", prship(sp));
sp->shp_autonav |= AN_STANDBY;
putship(sp->shp_uid, sp);
/* We need to free the ship list */
qp = ship_list.q_forw;
while (qp != &ship_list) {
newqp = qp->q_forw;
emp_remque(qp);
free(qp);
qp = newqp;
}
return -1;
}
path_find_route(buf, sizeof(buf), sp->shp_x, sp->shp_y,
sp->shp_destx[0], sp->shp_desty[0]);
stopping = 0;
cp = buf;
while (*cp && !stopping && sp->shp_own && mlp->mobil > 0.0) {
dir = diridx(*cp++);
stopping |= shp_nav_one_sector(&ship_list, dir,
sp->shp_own, 0);
}
/* Ship not sunk */
if (sp->shp_own)
nav_check_atdest(sp);
}
quit = 0; /* stop loop */
/* Try to load the ship */
if (sp->shp_autonav & AN_LOADING) {
didsomething = nav_loadship(sp);
if (didsomething)
quit = 1;
}
/* special case for fishing boats and oil derricks */
if (nav_load_ship_at_sea(sp))
quit = 0;
/* reset flag and check if we can move. */
} while (quit); /* end loop */
putship(sp->shp_uid, sp);
/* We need to free the ship list (just in case) */
qp = ship_list.q_forw;
while (qp != &ship_list) {
newqp = qp->q_forw;
emp_remque(qp);
free(qp);
qp = newqp;
}
return 0;
}

View file

@ -1,175 +0,0 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2014, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure, Markus Armbruster
*
* Empire 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 3 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, see <http://www.gnu.org/licenses/>.
*
* ---
*
* 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.
*
* ---
*
* nav_util.c: Utilities for autonav and sail
*
* Known contributors to this file:
*
*/
#include <config.h>
#include "item.h"
#include "nsc.h"
#include "plague.h"
#include "ship.h"
#include "update.h"
/* load a specific ship given its
* location and what field to modify.
* new autonav code
* Chad Zabel 6/1/94
*/
int
load_it(struct shpstr *sp, struct sctstr *psect, int i)
{
int shipown, amount, ship_amt, sect_amt;
int abs_max, max_amt, transfer;
i_type comm;
struct mchrstr *vship;
amount = sp->shp_lend[i];
shipown = sp->shp_own;
comm = sp->shp_tend[i];
if (CANT_HAPPEN(comm <= I_NONE || comm > I_MAX))
return 0;
ship_amt = sp->shp_item[comm];
sect_amt = psect->sct_item[comm];
/* check for disloyal civilians */
if (psect->sct_oldown != shipown && comm == I_CIVIL) {
wu(0, shipown,
"Ship #%d - unable to load disloyal civilians at %s.",
sp->shp_uid, xyas(psect->sct_x, psect->sct_y, shipown));
return 0;
}
if (comm == I_CIVIL || comm == I_MILIT)
sect_amt--; /* leave 1 civ or mil to hold the sector. */
vship = &mchr[(int)sp->shp_type];
abs_max = max_amt = vship->m_item[comm];
if (!abs_max)
return 0; /* can't load the ship, skip to the end. */
max_amt = MIN(sect_amt, max_amt - ship_amt);
if (max_amt <= 0 && (ship_amt != abs_max)) {
sp->shp_autonav |= AN_LOADING;
return 0;
}
transfer = amount - ship_amt;
if (transfer > sect_amt) { /* not enough in the */
transfer = sect_amt; /* sector to fill the */
sp->shp_autonav |= AN_LOADING; /* ship, set load flag */
}
if (ship_amt + transfer > abs_max) /* Do not load more */
transfer = abs_max - ship_amt; /* then the max alowed */
/* on the ship. */
if (transfer <= 0)
return 0; /* nothing to move */
sp->shp_item[comm] = ship_amt + transfer;
if (comm == I_CIVIL || comm == I_MILIT)
sect_amt++; /*adjustment */
psect->sct_item[comm] = sect_amt - transfer;
/* deal with the plague */
if (psect->sct_pstage == PLG_INFECT && sp->shp_pstage == PLG_HEALTHY)
sp->shp_pstage = PLG_EXPOSED;
if (sp->shp_pstage == PLG_INFECT && psect->sct_pstage == PLG_HEALTHY)
psect->sct_pstage = PLG_EXPOSED;
return 1; /* we did someloading return 1 to keep */
/* our loop happy in nav_ship() */
}
/* unload_it
* A guess alot of this looks like load_it but because of its location
* in the autonav code I had to split the 2 procedures up.
* unload_it dumps all the goods from the ship to the harbor.
* ONLY goods in the trade fields will be unloaded.
* new autonav code
* Chad Zabel 6/1/94
*/
void
unload_it(struct shpstr *sp)
{
struct sctstr *sectp;
int i;
int landowner;
int shipown;
i_type comm;
int sect_amt;
int ship_amt;
int max_amt;
sectp = getsectp(sp->shp_x, sp->shp_y);
landowner = sectp->sct_own;
shipown = sp->shp_own;
for (i = 0; i < TMAX; ++i) {
if (sp->shp_tend[i] == I_NONE || sp->shp_lend[i] == 0)
continue;
if (landowner == 0)
continue;
if (sectp->sct_type != SCT_HARBR)
continue;
comm = sp->shp_tend[i];
if (CANT_HAPPEN(comm <= I_NONE || comm > I_MAX))
continue;
ship_amt = sp->shp_item[comm];
sect_amt = sectp->sct_item[comm];
/* check for disloyal civilians */
if (sectp->sct_oldown != shipown && comm == I_CIVIL) {
wu(0, sp->shp_own,
"Ship #%d - unable to unload civilians into a disloyal sector at %s.",
sp->shp_uid, xyas(sectp->sct_x, sectp->sct_y, sp->shp_own));
continue;
}
if (comm == I_CIVIL)
ship_amt--; /* This leaves 1 civs on board the ship */
max_amt = MIN(ship_amt, ITEM_MAX - sect_amt);
if (max_amt <= 0)
continue;
sp->shp_item[comm] = ship_amt - max_amt;
sectp->sct_item[comm] = sect_amt + max_amt;
if (sectp->sct_pstage == PLG_INFECT && sp->shp_pstage == PLG_HEALTHY)
sp->shp_pstage = PLG_EXPOSED;
if (sp->shp_pstage == PLG_INFECT && sectp->sct_pstage == PLG_HEALTHY)
sectp->sct_pstage = PLG_EXPOSED;
}
}

View file

@ -75,8 +75,6 @@ prod_ship(int etus, int natnum, struct bp *bp, int build)
np = getnatp(sp->shp_own);
start_money = np->nat_money;
upd_ship(sp, etus, np, bp, build);
if (build && !player->simulation) /* make sure to only autonav once */
nav_ship(sp); /* autonav the ship */
sea_money[sp->shp_own] += np->nat_money - start_money;
if (!build || np->nat_money != start_money)
k++;