empserver/src/lib/subs/move.c
Markus Armbruster 28d4847416 Clean up move_ground()'s parsing of DIR_MAP
Split with parse() and pass first two arguments instead of the raw
tail to the map() callback.  Advantages:

* Consistent with do_unit_move().

* Does the right thing when the tail is just spaces.  Before, the
  spaces got passed to the map() callback, which complained about
  syntax.  Now, they are ignored.  This is what the commit I just
  reverted tried to fix.

* Works better when the tail splits into more than two arguments.
  Except for explore_map(), which ignores the argument(s), the map()
  callbacks use display_region_map(), which split the tail at the
  first space, and complained about any spaces in the second part.
  Now, display_region_map() takes two argument strings instead of a
  single, unsplit argument string, and extra arguments get silently
  ignored, as usual.
2011-04-14 20:21:23 +02:00

358 lines
9 KiB
C

/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2011, 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.
*
* ---
*
* move.c: Move something somewhere.
*
* Known contributors to this file:
* Markus Armbruster, 2004-2011
*/
#include <config.h>
#include <ctype.h>
#include "damage.h"
#include "file.h"
#include "map.h"
#include "nsc.h"
#include "path.h"
#include "player.h"
#include "prototypes.h"
#include "sect.h"
static int move_map(coord curx, coord cury, char *arg);
int
move_ground(struct sctstr *start, struct sctstr *end,
double weight, char *path,
int (*map)(coord, coord, char *, char *),
int exploring, int *dam)
{
struct sctstr sect;
struct sctstr next;
coord curx, cury, oldx, oldy;
coord tmpx, tmpy;
coord dx, dy;
char *movstr;
double sect_mcost;
double total_mcost;
double mv_cost;
size_t len;
double mobility = start->sct_mobil;
int dir;
char scanspace[1024];
char *argp[128];
int ac;
int intcost;
int takedam = *dam;
int out = 0;
char prompt[128];
char buf[1024];
*end = *start;
if (mobility <= 0.0)
return -1;
*dam = 0;
if (path && sarg_xy(path, &dx, &dy)) {
if (dx == start->sct_x && dy == start->sct_y) {
pr("Start sector is ending sector!\n");
return -1;
}
pr("Looking for best path to %s\n", path);
total_mcost = path_find(start->sct_x, start->sct_y, dx, dy,
player->cnum, MOB_MOVE);
path = NULL;
if (total_mcost < 0)
pr("No owned path exists!\n");
else {
len = path_find_route(buf, sizeof(buf),
start->sct_x, start->sct_y, dx, dy);
if (!exploring) {
if (len < sizeof(buf))
strcpy(buf + len, "h");
len++;
}
if (len >= sizeof(buf))
pr("Can't handle path to %s, it's too long, sorry.\n",
xyas(dx, dy, player->cnum));
else {
path = buf;
pr("Using best path '%s', movement cost %1.3f\n",
path, total_mcost);
if (total_mcost * weight > mobility) {
pr("Not enough mobility to go all the way."
" Nothing moved.\n");
return -1;
}
}
}
}
movstr = path;
curx = start->sct_x;
cury = start->sct_y;
total_mcost = 0.0;
if (getsect(curx, cury, &sect) < 0) {
logerror("move_path: getsect %d,%d", curx, cury);
return -1;
}
for (;;) {
oldx = curx;
oldy = cury;
if (!movstr || *movstr == 0) {
if (exploring) {
map(curx, cury, NULL, NULL);
} else {
move_map(curx, cury, NULL);
}
sprintf(prompt, "<%.1f: %c %s> ", mobility,
dchr[sect.sct_type].d_mnem,
xyas(sect.sct_x, sect.sct_y, player->cnum));
movstr = getstring(prompt, buf);
}
if (movstr && sarg_xy(movstr, &dx, &dy)) {
mv_cost = path_find(sect.sct_x, sect.sct_y, dx, dy,
player->cnum, MOB_MOVE);
if (mv_cost < 0) {
pr("Can't get to %s from here!\n",
xyas(dx, dy, player->cnum));
movstr = NULL;
} else {
len = path_find_route(buf, sizeof(buf),
sect.sct_x, sect.sct_y, dx, dy);
if (len < sizeof(buf))
strcpy(buf + len, "h");
len++;
if (len >= sizeof(buf)) {
pr("Can't handle path to %s, it's too long, sorry.\n",
xyas(dx, dy, player->cnum));
movstr = NULL;
} else {
if ((mv_cost * weight) > mobility) {
pr("Not enough mobility to go all the way. Nothing moved.\n");
movstr = NULL;
} else {
movstr = buf;
pr("Using best path '%s', movement cost %1.3f\n",
movstr, mv_cost);
}
}
}
}
if (!movstr || *movstr == 0) {
buf[0] = dirch[DIR_STOP];
buf[1] = 0;
movstr = buf;
}
if ((dir = chkdir(*movstr, DIR_STOP, DIR_MAP)) < 0) {
pr("\"%c\" is not legal...", *movstr);
direrr("'%c' to stop ", "'%c' to view ", "& '%c' to map\n");
*movstr = 0;
continue;
}
if (dir == DIR_MAP) {
ac = parse(movstr, scanspace, argp, NULL, NULL, NULL);
if (ac == 1) {
pr("Use of '%c' without a space before its argument is deprecated.\n"
"Support for it will go away in a future release\n",
*movstr);
argp[1] = argp[0] + 1;
}
if (!exploring)
map(curx, cury, argp[1], argp[2]);
*movstr = 0;
continue;
}
movstr++;
if (dir == DIR_STOP)
break;
if (dir == DIR_VIEW) {
pr("%d%% %s with %d civilians.\n", sect.sct_effic,
dchr[sect.sct_type].d_name, sect.sct_item[I_CIVIL]);
continue;
}
/*
* now see if we can move into the
* next sector. Mobility, terrain,
* or ownership may prevent us.
*/
tmpx = curx + diroff[dir][0];
tmpy = cury + diroff[dir][1];
if (getsect(tmpx, tmpy, &next) < 0) {
pr("You can't go there...\n");
*movstr = 0;
continue;
}
if (!player->god) {
if ((next.sct_type == SCT_SANCT) &&
(next.sct_own != player->cnum)) {
pr("Converts, huh?\n");
*movstr = 0;
continue;
}
sect_mcost = sector_mcost(&next, MOB_MOVE);
if ((!player->owner && (!exploring
|| next.sct_item[I_MILIT]
|| next.sct_item[I_CIVIL]))
|| sect_mcost == -1.0) {
/* already-owned, or prohibited terrain */
pr("You can't go there...\n");
*movstr = 0;
continue;
}
sect_mcost *= weight;
if (sect_mcost > mobility) {
pr("Not enough mobility. ");
pr("You can't go there...\n");
*movstr = 0;
continue;
}
mobility -= sect_mcost;
total_mcost += sect_mcost;
}
curx = next.sct_x;
cury = next.sct_y;
if (cury != start->sct_y)
out = 1;
if (curx != start->sct_x)
out = 1;
sect = next;
if (takedam)
*dam += check_lmines(sect.sct_x, sect.sct_y, weight);
if (*dam >= 100)
break;
/*
* Check and see if anyone will interdict us
*/
if (takedam && chance(weight / 100.0) &&
((curx != oldx) || (cury != oldy)))
*dam += ground_interdict(curx, cury, player->cnum,
"commodities");
if (*dam >= 100)
break;
}
*end = sect;
intcost = (int)total_mcost;
if (intcost < 0)
return -1;
if ((start->sct_x == end->sct_x) && (start->sct_y == end->sct_y)
&& !out)
return -1;
if (chance(total_mcost - intcost))
intcost++;
return intcost;
}
/*ARGSUSED*/
static int
move_map(coord curx, coord cury, char *arg)
{
struct nstr_sect ns;
struct sctstr sect;
char view[7];
int i;
int changed = 0;
snxtsct_dist(&ns, curx, cury, 1);
i = 0;
while (i < 7 && nxtsct(&ns, &sect)) {
/* Nasty: this relies on the iteration order */
view[i] = dchr[sect.sct_type].d_mnem;
switch (sect.sct_type) {
case SCT_WATER:
case SCT_RURAL:
case SCT_MOUNT:
case SCT_WASTE:
case SCT_PLAINS:
break;
default:
if (sect.sct_own != player->cnum && !player->god)
view[i] = '?';
break;
}
changed += map_set(player->cnum, ns.x, ns.y, view[i], 0);
i++;
}
if (changed)
writemap(player->cnum);
if (!getsect(curx, cury, &sect))
return RET_FAIL;
pr(" %c %c eff mob civ mil uw food work avail\n",
view[0], view[1]);
pr(" %c %c %c %3d %3d %4d %4d %4d %4d %3d %3d\n",
view[2], view[3], view[4],
sect.sct_effic, sect.sct_mobil,
sect.sct_item[I_CIVIL], sect.sct_item[I_MILIT], sect.sct_item[I_UW],
sect.sct_item[I_FOOD], sect.sct_work, sect.sct_avail);
pr(" %c %c\n", view[5], view[6]);
return RET_OK;
}
int
fly_map(coord curx, coord cury)
{
struct nstr_sect ns;
struct sctstr sect;
char view[7];
int i;
snxtsct_dist(&ns, curx, cury, 1);
i = 0;
while (i < 7 && nxtsct(&ns, &sect)) {
/* Nasty: this relies on the iteration order */
if (!(view[i] = player->bmap[sect.sct_uid]))
view[i] = ' ';
i++;
}
pr(" %c %c\n", view[0], view[1]);
pr(" %c %c %c\n", view[2], view[3], view[4]);
pr(" %c %c\n", view[5], view[6]);
return RET_OK;
}
int
check_lmines(coord x, coord y, double weight)
{
struct sctstr sect;
int dam = 0;
getsect(x, y, &sect);
if (SCT_LANDMINES(&sect) > 0 &&
sect.sct_oldown != player->cnum &&
chance(DMINE_LHITCHANCE(sect.sct_mines)) && chance(weight / 100.0)) {
pr_beep();
pr("Blammo! Landmines detected! in %s ",
xyas(sect.sct_x, sect.sct_y, player->cnum));
dam = roll(20);
--sect.sct_mines;
putsect(&sect);
pr("%d damage sustained.\n", dam);
}
return dam;
}