]> git.pond.sub.org Git - empserver/blob - src/lib/subs/retreat.c
Update copyright notice
[empserver] / src / lib / subs / retreat.c
1 /*
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
5  *
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.
10  *
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.
15  *
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/>.
18  *
19  *  ---
20  *
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.
24  *
25  *  ---
26  *
27  *  retreat.c: Retreat subroutines
28  *
29  *  Known contributors to this file:
30  *     Steve McClure, 2000
31  *     Ron Koenderink, 2005-2006
32  *     Markus Armbruster, 2006-2015
33  */
34
35 #include <config.h>
36
37 #include "file.h"
38 #include "nsc.h"
39 #include "path.h"
40 #include "player.h"
41 #include "prototypes.h"
42 #include "retreat.h"
43 #include "unit.h"
44
45 static void retreat_ship_sel(struct shpstr *, struct emp_qelem *, int);
46 static int retreat_ships_step(struct emp_qelem *, char, natid);
47 static void retreat_land_sel(struct lndstr *, struct emp_qelem *, int);
48 static int retreat_lands_step(struct emp_qelem *, char, natid);
49
50 static int
51 retreat_steps(char *rpath)
52 {
53     int i;
54
55     for (i = 0; i < MAX_RETREAT && rpath[i]; i++) {
56         if (rpath[i] == 'h')
57             return i + 1;
58     }
59     return i;
60 }
61
62 static void
63 consume_step(char *rpath, int *rflags)
64 {
65     memmove(rpath, rpath + 1, RET_LEN - 1);
66     if (!rpath[0])
67         *rflags = 0;
68 }
69
70 void
71 retreat_ship(struct shpstr *sp, natid own, char code)
72 {
73     int n, i;
74     struct emp_qelem list;
75     struct nstr_item ni;
76     struct shpstr ship;
77
78     if (CANT_HAPPEN(!own || (sp->shp_own && sp->shp_own != own)))
79         return;
80     if (own == player->cnum || !sp->shp_rpath[0])
81         return;
82
83     n = retreat_steps(sp->shp_rpath);
84     if (!n)
85         return;
86
87     /*
88      * We're going to put a copy of *sp into list.  The movement loop
89      * will use that copy, which may render *sp stale.  To avoid
90      * leaving the caller with a stale *sp, we'll re-get it at the
91      * end.  To make that work, we need to put it now.  Resets
92      * sp->shp_own when the ship sinks.
93      */
94     putship(sp->shp_uid, sp);
95
96     emp_initque(&list);
97     if (sp->shp_own)
98         retreat_ship_sel(sp, &list, n);
99
100     if (sp->shp_rflags & RET_GROUP) {
101         snxtitem_xy(&ni, EF_SHIP, sp->shp_x, sp->shp_y);
102         while (nxtitem(&ni, &ship)) {
103             if (ship.shp_own != own
104                 || !(ship.shp_rflags & RET_GROUP)
105                 || ship.shp_fleet != sp->shp_fleet
106                 || ship.shp_uid == sp->shp_uid)
107                 continue;
108             if (strncmp(ship.shp_rpath, sp->shp_rpath, MAX_RETREAT + 1))
109                 continue;
110             retreat_ship_sel(&ship, &list, n);
111         }
112     }
113
114     /* Loop similar to the one in unit_move().  Keep it that way! */
115     for (i = 0; i < n && !QEMPTY(&list); i++) {
116         /*
117          * Invariant: shp_may_nav() true for all ships
118          * Implies all are in the same sector
119          */
120         if (!retreat_ships_step(&list, sp->shp_rpath[i], own))
121             n = i;
122         shp_nav_stay_behind(&list, own);
123         unit_rad_map_set(&list);
124     }
125
126     if (!QEMPTY(&list))
127         shp_nav_put(&list, own);
128     getship(sp->shp_uid, sp);
129 }
130
131 static void
132 retreat_ship_sel(struct shpstr *sp, struct emp_qelem *list, int n)
133 {
134     struct shpstr *flg = QEMPTY(list) ? NULL
135         : &((struct ulist *)(list->q_back))->unit.ship;
136
137     if (!shp_may_nav(sp, flg, ", and can't retreat!"))
138         return;
139     if (sp->shp_mobil <= 0) {
140         mpr(sp->shp_own, "%s has no mobility, and can't retreat!\n",
141             prship(sp));
142         return;
143     }
144
145     if (flg)
146         mpr(sp->shp_own, "%s retreats with her\n", prship(sp));
147     else
148         mpr(sp->shp_own, "%s retreats along path %.*s\n",
149             prship(sp), n, sp->shp_rpath);
150     shp_insque(sp, list);
151 }
152
153 static int
154 retreat_ships_step(struct emp_qelem *list, char step, natid actor)
155 {
156     int dir = chkdir(step, DIR_STOP, DIR_LAST);
157     struct emp_qelem *qp;
158     struct ulist *mlp;
159     struct shpstr *sp;
160
161     if (dir != DIR_STOP && shp_nav_dir(list, dir, actor))
162         return 0;               /* can't go there */
163
164     for (qp = list->q_back; qp != list; qp = qp->q_back) {
165         mlp = (struct ulist *)qp;
166         sp = &mlp->unit.ship;
167         consume_step(sp->shp_rpath, &sp->shp_rflags);
168         if (dir != DIR_STOP)
169             sp->shp_mission = 0;
170         putship(sp->shp_uid, sp);
171     }
172
173     return dir != DIR_STOP && !shp_nav_gauntlet(list, 0, actor);
174 }
175
176 void
177 retreat_land(struct lndstr *lp, natid own, char code)
178 {
179     int n, i;
180     struct emp_qelem list;
181     struct nstr_item ni;
182     struct lndstr land;
183
184     if (CANT_HAPPEN(!own || (lp->lnd_own && lp->lnd_own != own)))
185         return;
186     if (own == player->cnum || !lp->lnd_rpath[0])
187         return;
188
189     n = retreat_steps(lp->lnd_rpath);
190     if (!n)
191         return;
192
193     /* See explanation in retreat_ship() */
194     putland(lp->lnd_uid, lp);
195
196     emp_initque(&list);
197     if (lp->lnd_own)
198         retreat_land_sel(lp, &list, n);
199
200     if (lp->lnd_rflags & RET_GROUP) {
201         snxtitem_xy(&ni, EF_LAND, lp->lnd_x, lp->lnd_y);
202         while (nxtitem(&ni, &land)) {
203             if (land.lnd_own != own
204                 || !(land.lnd_rflags & RET_GROUP)
205                 || land.lnd_army != lp->lnd_army
206                 || land.lnd_uid == lp->lnd_uid)
207                 continue;
208             if (strncmp(land.lnd_rpath, lp->lnd_rpath, MAX_RETREAT + 1))
209                 continue;
210             retreat_land_sel(&land, &list, n);
211         }
212     }
213
214     /* Loop similar to the one in unit_move().  Keep it that way! */
215     for (i = 0; i < n && !QEMPTY(&list); i++) {
216         /*
217          * Invariant: lnd_may_nav() true for all land units
218          * Implies all are in the same sector
219          */
220         if (!retreat_lands_step(&list, lp->lnd_rpath[i], own))
221             n = i;
222         lnd_mar_stay_behind(&list, own);
223         unit_rad_map_set(&list);
224     }
225
226     if (!QEMPTY(&list))
227         lnd_mar_put(&list, own);
228     getland(lp->lnd_uid, lp);
229 }
230
231 static void
232 retreat_land_sel(struct lndstr *lp, struct emp_qelem *list, int n)
233 {
234     struct lndstr *ldr = QEMPTY(list)
235         ? NULL : &((struct ulist *)(list->q_back))->unit.land;
236
237     if (!lnd_may_mar(lp, ldr, ", and can't retreat!"))
238         return;
239     if (lp->lnd_mobil <= 0) {
240         mpr(lp->lnd_own, "%s has no mobility, and can't retreat!\n",
241             prland(lp));
242         return;
243     }
244
245     if (ldr)
246         mpr(lp->lnd_own, "%s retreats with them\n", prland(lp));
247     else
248         mpr(lp->lnd_own, "%s retreats along path %.*s\n",
249             prland(lp), n, lp->lnd_rpath);
250     lnd_insque(lp, list);
251 }
252
253 static int
254 retreat_lands_step(struct emp_qelem *list, char step, natid actor)
255 {
256     int dir = chkdir(step, DIR_STOP, DIR_LAST);
257     struct emp_qelem *qp;
258     struct ulist *llp;
259     struct lndstr *lp;
260
261     if (dir != DIR_STOP && lnd_mar_dir(list, dir, actor))
262         return 0;               /* can't go there */
263
264     for (qp = list->q_back; qp != list; qp = qp->q_back) {
265         llp = (struct ulist *)qp;
266         lp = &llp->unit.land;
267         consume_step(lp->lnd_rpath, &lp->lnd_rflags);
268         if (dir != DIR_STOP) {
269             lp->lnd_mission = 0;
270             lp->lnd_harden = 0;
271         }
272         putland(lp->lnd_uid, lp);
273     }
274
275     return dir != DIR_STOP && !lnd_mar_gauntlet(list, 0, actor);
276 }