2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2016, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure, Markus Armbruster
6 * Empire is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * See files README, COPYING and CREDITS in the root of the source
22 * tree for related information and legal notices. It is expected
23 * that future projects/authors will amend these files as needed.
27 * retreat.c: Retreat subroutines
29 * Known contributors to this file:
31 * Ron Koenderink, 2005-2006
32 * Markus Armbruster, 2006-2015
40 #include "prototypes.h"
44 static void retreat_ship_sel(struct shpstr *, struct emp_qelem *, int);
45 static int retreat_ships_step(struct emp_qelem *, char, natid);
46 static void retreat_land_sel(struct lndstr *, struct emp_qelem *, int);
47 static int retreat_lands_step(struct emp_qelem *, char, natid);
50 retreat_steps(char *rpath)
54 for (i = 0; i < MAX_RETREAT && rpath[i]; i++) {
62 consume_step(char *rpath, int *rflags)
64 memmove(rpath, rpath + 1, RET_LEN - 1);
70 retreat_ship(struct shpstr *sp, natid own, char code)
73 struct emp_qelem list;
77 if (CANT_HAPPEN(!own || (sp->shp_own && sp->shp_own != own)))
79 if (own == player->cnum || !sp->shp_rpath[0])
82 n = retreat_steps(sp->shp_rpath);
87 * We're going to put a copy of *sp into list. The movement loop
88 * will use that copy, which may render *sp stale. To avoid
89 * leaving the caller with a stale *sp, we'll re-get it at the
90 * end. To make that work, we need to put it now. Resets
91 * sp->shp_own when the ship sinks.
93 putship(sp->shp_uid, sp);
97 retreat_ship_sel(sp, &list, n);
99 if (sp->shp_rflags & RET_GROUP) {
100 snxtitem_xy(&ni, EF_SHIP, sp->shp_x, sp->shp_y);
101 while (nxtitem(&ni, &ship)) {
102 if (ship.shp_own != own
103 || !(ship.shp_rflags & RET_GROUP)
104 || ship.shp_fleet != sp->shp_fleet
105 || ship.shp_uid == sp->shp_uid)
107 if (strncmp(ship.shp_rpath, sp->shp_rpath, MAX_RETREAT + 1))
109 retreat_ship_sel(&ship, &list, n);
113 /* Loop similar to the one in unit_move(). Keep it that way! */
114 for (i = 0; i < n && !QEMPTY(&list); i++) {
116 * Invariant: shp_may_nav() true for all ships
117 * Implies all are in the same sector
119 if (!retreat_ships_step(&list, sp->shp_rpath[i], own))
121 shp_nav_stay_behind(&list, own);
122 unit_rad_map_set(&list);
126 shp_nav_put(&list, own);
127 getship(sp->shp_uid, sp);
131 retreat_ship_sel(struct shpstr *sp, struct emp_qelem *list, int n)
133 struct shpstr *flg = QEMPTY(list) ? NULL
134 : &((struct ulist *)(list->q_back))->unit.ship;
136 if (!shp_may_nav(sp, flg, ", and can't retreat!"))
138 if (sp->shp_mobil <= 0) {
139 mpr(sp->shp_own, "%s has no mobility, and can't retreat!\n",
145 mpr(sp->shp_own, "%s retreats with her\n", prship(sp));
147 mpr(sp->shp_own, "%s retreats along path %.*s\n",
148 prship(sp), n, sp->shp_rpath);
149 shp_insque(sp, list);
153 retreat_ships_step(struct emp_qelem *list, char step, natid actor)
155 int dir = chkdir(step, DIR_STOP, DIR_LAST);
156 struct emp_qelem *qp;
160 if (dir != DIR_STOP && shp_nav_dir(list, dir, actor))
161 return 0; /* can't go there */
163 for (qp = list->q_back; qp != list; qp = qp->q_back) {
164 mlp = (struct ulist *)qp;
165 sp = &mlp->unit.ship;
166 consume_step(sp->shp_rpath, &sp->shp_rflags);
169 putship(sp->shp_uid, sp);
172 return dir != DIR_STOP && !shp_nav_gauntlet(list, 0, actor);
176 retreat_land(struct lndstr *lp, natid own, char code)
179 struct emp_qelem list;
183 if (CANT_HAPPEN(!own || (lp->lnd_own && lp->lnd_own != own)))
185 if (own == player->cnum || !lp->lnd_rpath[0])
188 n = retreat_steps(lp->lnd_rpath);
192 /* See explanation in retreat_ship() */
193 putland(lp->lnd_uid, lp);
197 retreat_land_sel(lp, &list, n);
199 if (lp->lnd_rflags & RET_GROUP) {
200 snxtitem_xy(&ni, EF_LAND, lp->lnd_x, lp->lnd_y);
201 while (nxtitem(&ni, &land)) {
202 if (land.lnd_own != own
203 || !(land.lnd_rflags & RET_GROUP)
204 || land.lnd_army != lp->lnd_army
205 || land.lnd_uid == lp->lnd_uid)
207 if (strncmp(land.lnd_rpath, lp->lnd_rpath, MAX_RETREAT + 1))
209 retreat_land_sel(&land, &list, n);
213 /* Loop similar to the one in unit_move(). Keep it that way! */
214 for (i = 0; i < n && !QEMPTY(&list); i++) {
216 * Invariant: lnd_may_nav() true for all land units
217 * Implies all are in the same sector
219 if (!retreat_lands_step(&list, lp->lnd_rpath[i], own))
221 lnd_mar_stay_behind(&list, own);
222 unit_rad_map_set(&list);
226 lnd_mar_put(&list, own);
227 getland(lp->lnd_uid, lp);
231 retreat_land_sel(struct lndstr *lp, struct emp_qelem *list, int n)
233 struct lndstr *ldr = QEMPTY(list)
234 ? NULL : &((struct ulist *)(list->q_back))->unit.land;
236 if (!lnd_may_mar(lp, ldr, ", and can't retreat!"))
238 if (lp->lnd_mobil <= 0) {
239 mpr(lp->lnd_own, "%s has no mobility, and can't retreat!\n",
245 mpr(lp->lnd_own, "%s retreats with them\n", prland(lp));
247 mpr(lp->lnd_own, "%s retreats along path %.*s\n",
248 prland(lp), n, lp->lnd_rpath);
249 lnd_insque(lp, list);
253 retreat_lands_step(struct emp_qelem *list, char step, natid actor)
255 int dir = chkdir(step, DIR_STOP, DIR_LAST);
256 struct emp_qelem *qp;
260 if (dir != DIR_STOP && lnd_mar_dir(list, dir, actor))
261 return 0; /* can't go there */
263 for (qp = list->q_back; qp != list; qp = qp->q_back) {
264 llp = (struct ulist *)qp;
265 lp = &llp->unit.land;
266 consume_step(lp->lnd_rpath, &lp->lnd_rflags);
267 if (dir != DIR_STOP) {
271 putland(lp->lnd_uid, lp);
274 return dir != DIR_STOP && !lnd_mar_gauntlet(list, 0, actor);