Destination arguments can be a path or sector coordinates. do_unit_move() passes the argument buffer to unit_path() to convert coordinates to a path. If unit_path() fails, do_unit_move() still interprets the argument as path. This is correct when unit_path() fails because the argument is not coordinates. But it can also fail when it is coordinates, namely when the destination isn't reachable, when the path to it is too long, or when the ships or land units aren't together. Then do_unit_move() interprets coordinates as path, and rejects them with "Legal directions are:". Except when a land unit's destination read from a march prompt isn't reachable, because then unit_path() empties the argument buffer that do_unit_move() uses. Change unit_path() to succeed when the argument is not coordinates. Make do_unit_move() discard the argument when unit_path() fails, i.e. when it is bad coordinates. unit_path() emptying the argument no longer has an effect, drop it.
365 lines
9.2 KiB
C
365 lines
9.2 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.
|
|
*
|
|
* ---
|
|
*
|
|
* navi.c: Navigate ships and such
|
|
*
|
|
* Known contributors to this file:
|
|
* Ken Stevens, 1995 (rewritten)
|
|
* Ron Koenderink, 2006-2007
|
|
* Markus Armbruster, 2006-2011
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "commands.h"
|
|
#include "map.h"
|
|
#include "optlist.h"
|
|
#include "path.h"
|
|
#include "empobj.h"
|
|
#include "unit.h"
|
|
|
|
static void pr_leader_change(struct empobj *leader);
|
|
static struct empobj *get_leader(struct emp_qelem *list);
|
|
static void switch_leader(struct emp_qelem *list, int uid);
|
|
|
|
int
|
|
navi(void)
|
|
{
|
|
struct nstr_item ni_ship;
|
|
struct emp_qelem ship_list;
|
|
double minmob, maxmob;
|
|
int together;
|
|
|
|
if (!snxtitem(&ni_ship, EF_SHIP, player->argp[1], NULL))
|
|
return RET_SYN;
|
|
shp_sel(&ni_ship, &ship_list);
|
|
shp_nav(&ship_list, &minmob, &maxmob, &together, player->cnum);
|
|
if (QEMPTY(&ship_list)) {
|
|
pr("No ships\n");
|
|
return RET_FAIL;
|
|
}
|
|
|
|
return do_unit_move(&ship_list, &together, &minmob, &maxmob);
|
|
}
|
|
|
|
int
|
|
do_unit_move(struct emp_qelem *ulist, int *together,
|
|
double *minmob, double *maxmob)
|
|
{
|
|
char *cp = NULL;
|
|
int leader_uid;
|
|
struct empobj *leader;
|
|
int dir;
|
|
int stopping = 0;
|
|
int skip = 0;
|
|
char buf[1024];
|
|
char prompt[128];
|
|
char scanspace[1024];
|
|
char pathtaken[1024]; /* Doubtful we'll have a path longer than this */
|
|
char *pt = pathtaken;
|
|
char bmap_flag;
|
|
int ac;
|
|
int type;
|
|
|
|
leader = get_leader(ulist);
|
|
leader_uid = leader->uid;
|
|
type = leader->ef_type;
|
|
pr("%s is %s\n",
|
|
type == EF_SHIP ? "Flagship" : "Leader",
|
|
obj_nameof(leader));
|
|
|
|
if (player->argp[2]) {
|
|
strcpy(buf, player->argp[2]);
|
|
cp = unit_path(*together, leader, buf);
|
|
}
|
|
|
|
*pt = '\0';
|
|
while (!QEMPTY(ulist)) {
|
|
char dp[80];
|
|
|
|
if (cp == NULL || *cp == '\0' || stopping) {
|
|
stopping = 0;
|
|
if (type == EF_SHIP)
|
|
shp_nav(ulist, minmob, maxmob, together, player->cnum);
|
|
else
|
|
lnd_mar(ulist, minmob, maxmob, together, player->cnum);
|
|
if (QEMPTY(ulist)) {
|
|
pr("No %s left\n", type == EF_SHIP ? "ships" : "lands");
|
|
if (type == EF_SHIP && strlen(pathtaken) > 1) {
|
|
pathtaken[strlen(pathtaken) - 1] = '\0';
|
|
pr("Path taken: %s\n", pathtaken);
|
|
}
|
|
return RET_OK;
|
|
}
|
|
leader = get_leader(ulist);
|
|
if (leader->uid != leader_uid) {
|
|
leader_uid = leader->uid;
|
|
pr_leader_change(leader);
|
|
stopping = 1;
|
|
continue;
|
|
}
|
|
if (!skip)
|
|
nav_map(leader->x, leader->y,
|
|
type == EF_SHIP
|
|
? !(mchr[(int)leader->type].m_flags & M_SUB) : 1);
|
|
else
|
|
skip = 0;
|
|
sprintf(prompt, "<%.1f:%.1f: %s> ", *maxmob,
|
|
*minmob, xyas(leader->x, leader->y, player->cnum));
|
|
cp = getstring(prompt, buf);
|
|
/* Just in case any of our units were shelled while we were
|
|
* at the prompt, we call shp_nav() or lnd_mar() again.
|
|
*/
|
|
if (type == EF_SHIP)
|
|
shp_nav(ulist, minmob, maxmob, together, player->cnum);
|
|
else
|
|
lnd_mar(ulist, minmob, maxmob, together, player->cnum);
|
|
if (QEMPTY(ulist)) {
|
|
pr("No %s left\n", type == EF_SHIP ? "ships" : "lands");
|
|
if (type == EF_SHIP && strlen(pathtaken) > 1) {
|
|
pathtaken[strlen(pathtaken) - 1] = '\0';
|
|
pr("Path taken: %s\n", pathtaken);
|
|
}
|
|
return RET_OK;
|
|
}
|
|
leader = get_leader(ulist);
|
|
if (leader->uid != leader_uid) {
|
|
leader_uid = leader->uid;
|
|
pr_leader_change(leader);
|
|
stopping = 1;
|
|
continue;
|
|
}
|
|
if (cp)
|
|
cp = unit_path(*together, leader, cp);
|
|
}
|
|
if (type == EF_SHIP) {
|
|
rad_map_set(player->cnum, leader->x, leader->y, leader->effic,
|
|
leader->tech, mchr[leader->type].m_vrnge);
|
|
}
|
|
if (cp == NULL || *cp == '\0')
|
|
cp = &dirch[DIR_STOP];
|
|
dir = chkdir(*cp, DIR_STOP, DIR_LAST);
|
|
if (dir >= 0) {
|
|
if (type == EF_SHIP) {
|
|
stopping |= shp_nav_one_sector(ulist, dir,
|
|
player->cnum, *together);
|
|
if (stopping != 2) {
|
|
*pt++ = dirch[dir];
|
|
*pt = '\0';
|
|
}
|
|
} else
|
|
stopping |=
|
|
lnd_mar_one_sector(ulist, dir, player->cnum,
|
|
*together);
|
|
cp++;
|
|
continue;
|
|
}
|
|
ac = parse(cp, scanspace, player->argp, NULL, NULL, NULL);
|
|
if (ac <= 0) {
|
|
player->argp[0] = "";
|
|
cp = NULL;
|
|
} else if (ac == 1) {
|
|
sprintf(dp, "%d", leader->uid);
|
|
player->argp[1] = dp;
|
|
cp++;
|
|
} else
|
|
cp = NULL;
|
|
bmap_flag = 0;
|
|
switch (*player->argp[0]) {
|
|
case 'B':
|
|
bmap_flag = 'b';
|
|
/*
|
|
* fall through
|
|
*/
|
|
case 'M':
|
|
do_map(bmap_flag, leader->ef_type, player->argp[1],
|
|
player->argp[2]);
|
|
skip = 1;
|
|
continue;
|
|
case 'f':
|
|
if (ac <= 1)
|
|
switch_leader(ulist, -1);
|
|
else
|
|
switch_leader(ulist, atoi(player->argp[1]));
|
|
leader = get_leader(ulist);
|
|
if (leader->uid != leader_uid) {
|
|
leader_uid = leader->uid;
|
|
pr_leader_change(leader);
|
|
}
|
|
continue;
|
|
case 'i':
|
|
unit_list(ulist);
|
|
continue;
|
|
case 'm':
|
|
if (type == EF_SHIP)
|
|
stopping |= shp_sweep(ulist, 1, 1, player->cnum);
|
|
else {
|
|
lnd_sweep(ulist, 1, 1, player->cnum);
|
|
stopping |= lnd_check_mines(ulist);
|
|
}
|
|
continue;
|
|
case 'r':
|
|
radar(leader->ef_type);
|
|
skip = 1;
|
|
player->btused++;
|
|
continue;
|
|
case 'l':
|
|
do_look(type);
|
|
player->btused++;
|
|
continue;
|
|
case 's':
|
|
if (leader->ef_type != EF_SHIP)
|
|
break;
|
|
sona();
|
|
player->btused++;
|
|
skip = 1;
|
|
continue;
|
|
case 'd':
|
|
if (ac < 3) {
|
|
player->argp[2] = ac < 2 ? "1" : player->argp[1];
|
|
sprintf(dp, "%d", leader->uid);
|
|
player->argp[1] = dp;
|
|
}
|
|
if (type == EF_SHIP)
|
|
mine();
|
|
else
|
|
landmine();
|
|
stopping = 1;
|
|
skip = 1;
|
|
player->btused++;
|
|
continue;
|
|
case 'v':
|
|
unit_view(ulist);
|
|
continue;
|
|
}
|
|
direrr("`%c' to stop", ", `%c' to view", NULL);
|
|
pr(", `i' to list %s, `f' to change %s,\n",
|
|
type == EF_SHIP ? "ships" : "units",
|
|
type == EF_SHIP ? "flagship" : "leader");
|
|
pr("`r' to radar, %s`l' to look, `M' to map, `B' to bmap,\n",
|
|
type == EF_SHIP ? "`s' to sonar, " : "");
|
|
pr("`d' to drop mines, and `m' to minesweep\n");
|
|
stopping = 1;
|
|
}
|
|
if (type == EF_SHIP && strlen(pathtaken) > 1) {
|
|
pathtaken[strlen(pathtaken) - 1] = '\0';
|
|
pr("Path taken: %s\n", pathtaken);
|
|
}
|
|
return RET_OK;
|
|
}
|
|
|
|
int
|
|
nav_map(int x, int y, int show_designations)
|
|
{
|
|
char *ptr;
|
|
struct nstr_sect ns;
|
|
struct sctstr sect;
|
|
int i;
|
|
/* Note this is not re-entrant anyway, so we keep the buffers
|
|
around */
|
|
static char *wmapbuf = NULL;
|
|
static char **wmap = NULL;
|
|
int changed = 0;
|
|
|
|
if (!wmapbuf)
|
|
wmapbuf = malloc(WORLD_Y * MAPWIDTH(1));
|
|
if (!wmap) {
|
|
wmap = malloc(WORLD_Y * sizeof(*wmap));
|
|
if (wmap && wmapbuf) {
|
|
for (i = 0; i < WORLD_Y; i++)
|
|
wmap[i] = &wmapbuf[MAPWIDTH(1) * i];
|
|
} else if (wmap) {
|
|
free(wmap);
|
|
wmap = NULL;
|
|
}
|
|
}
|
|
if (!wmapbuf || !wmap) {
|
|
pr("Memory error, tell the deity.\n");
|
|
logerror("malloc failed in navi\n");
|
|
return RET_FAIL;
|
|
}
|
|
snxtsct_dist(&ns, x, y, 1);
|
|
blankfill(wmapbuf, &ns.range, 1);
|
|
while (nxtsct(&ns, §)) {
|
|
ptr = &wmap[ns.dy][ns.dx];
|
|
*ptr = dchr[sect.sct_type].d_mnem;
|
|
if (!show_designations &&
|
|
sect.sct_own != player->cnum &&
|
|
sect.sct_type != SCT_WATER &&
|
|
sect.sct_type != SCT_BSPAN && sect.sct_type != SCT_HARBR)
|
|
*ptr = '?';
|
|
changed += map_set(player->cnum, sect.sct_x, sect.sct_y, *ptr, 0);
|
|
/*
|
|
* We do it this way so that 'x' and 'X'
|
|
* bdesignations will show up. This can
|
|
* be used to mark mined sectors. So, the
|
|
* player will see the current des, UNLESS
|
|
* they've marked the sector 'x' or 'X',
|
|
* in which case they'll see that.
|
|
* --ts
|
|
*/
|
|
*ptr = player->bmap[sect.sct_uid];
|
|
}
|
|
if (changed)
|
|
writemap(player->cnum);
|
|
for (i = 0; i < ns.range.height; i++)
|
|
pr("%s\n", wmap[i]);
|
|
return RET_OK;
|
|
}
|
|
|
|
static void
|
|
pr_leader_change(struct empobj *leader)
|
|
{
|
|
pr("Changing %s to %s\n",
|
|
leader->ef_type == EF_SHIP ? "flagship" : "leader",
|
|
obj_nameof(leader));
|
|
}
|
|
|
|
static struct empobj *
|
|
get_leader(struct emp_qelem *list)
|
|
{
|
|
return &((struct ulist *)(list->q_back))->unit.gen;
|
|
}
|
|
|
|
static void
|
|
switch_leader(struct emp_qelem *list, int uid)
|
|
{
|
|
struct emp_qelem *qp, *save;
|
|
struct ulist *ulp;
|
|
|
|
if (QEMPTY(list))
|
|
return;
|
|
|
|
save = qp = list->q_back;
|
|
do {
|
|
emp_remque(qp);
|
|
emp_insque(qp, list);
|
|
qp = list->q_back;
|
|
ulp = (struct ulist *)qp;
|
|
if (ulp->unit.gen.uid == uid || uid == -1)
|
|
break;
|
|
} while (list->q_back != save);
|
|
}
|