]> 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-2017, 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 "nsc.h"
38 #include "path.h"
39 #include "player.h"
40 #include "prototypes.h"
41 #include "retreat.h"
42 #include "unit.h"
43
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);
48
49 static int
50 retreat_steps(char *rpath)
51 {
52     int i;
53
54     for (i = 0; i < MAX_RETREAT && rpath[i]; i++) {
55         if (rpath[i] == 'h')
56             return i + 1;
57     }
58     return i;
59 }
60
61 static void
62 consume_step(char *rpath, int *rflags)
63 {
64     memmove(rpath, rpath + 1, RET_LEN - 1);
65     if (!rpath[0])
66         *rflags = 0;
67 }
68
69 void
70 retreat_ship(struct shpstr *sp, natid own, char code)
71 {
72     int n, i;
73     struct emp_qelem list;
74     struct nstr_item ni;
75     struct shpstr ship;
76
77     if (CANT_HAPPEN(!own || (sp->shp_own && sp->shp_own != own)))
78         return;
79     if (own == player->cnum || !sp->shp_rpath[0])
80         return;
81
82     n = retreat_steps(sp->shp_rpath);
83     if (!n)
84         return;
85
86     /*
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.
92      */
93     putship(sp->shp_uid, sp);
94
95     emp_initque(&list);
96     if (sp->shp_own)
97         retreat_ship_sel(sp, &list, n);
98
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)
106                 continue;
107             if (strncmp(ship.shp_rpath, sp->shp_rpath, MAX_RETREAT + 1))
108                 continue;
109             retreat_ship_sel(&ship, &list, n);
110         }
111     }
112
113     /* Loop similar to the one in unit_move().  Keep it that way! */
114     for (i = 0; i < n && !QEMPTY(&list); i++) {
115         /*
116          * Invariant: shp_may_nav() true for all ships
117          * Implies all are in the same sector
118          */
119         if (!retreat_ships_step(&list, sp->shp_rpath[i], own))
120             n = i;
121         shp_nav_stay_behind(&list, own);
122         unit_rad_map_set(&list);
123     }
124
125     if (!QEMPTY(&list))
126         shp_nav_put(&list, own);
127     getship(sp->shp_uid, sp);
128 }
129
130 static void
131 retreat_ship_sel(struct shpstr *sp, struct emp_qelem *list, int n)
132 {
133     struct shpstr *flg = QEMPTY(list) ? NULL
134         : &((struct ulist *)(list->q_back))->unit.ship;
135
136     if (!shp_may_nav(sp, flg, ", and can't retreat!"))
137         return;
138     if (sp->shp_mobil <= 0) {
139         mpr(sp->shp_own, "%s has no mobility, and can't retreat!\n",
140             prship(sp));
141         return;
142     }
143
144     if (flg)
145         mpr(sp->shp_own, "%s retreats with her\n", prship(sp));
146     else
147         mpr(sp->shp_own, "%s retreats along path %.*s\n",
148             prship(sp), n, sp->shp_rpath);
149     shp_insque(sp, list);
150 }
151
152 static int
153 retreat_ships_step(struct emp_qelem *list, char step, natid actor)
154 {
155     int dir = chkdir(step, DIR_STOP, DIR_LAST);
156     struct emp_qelem *qp;
157     struct ulist *mlp;
158     struct shpstr *sp;
159
160     if (dir != DIR_STOP && shp_nav_dir(list, dir, actor))
161         return 0;               /* can't go there */
162
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);
167         if (dir != DIR_STOP)
168             sp->shp_mission = 0;
169         putship(sp->shp_uid, sp);
170     }
171
172     return dir != DIR_STOP && !shp_nav_gauntlet(list, 0, actor);
173 }
174
175 void
176 retreat_land(struct lndstr *lp, natid own, char code)
177 {
178     int n, i;
179     struct emp_qelem list;
180     struct nstr_item ni;
181     struct lndstr land;
182
183     if (CANT_HAPPEN(!own || (lp->lnd_own && lp->lnd_own != own)))
184         return;
185     if (own == player->cnum || !lp->lnd_rpath[0])
186         return;
187
188     n = retreat_steps(lp->lnd_rpath);
189     if (!n)
190         return;
191
192     /* See explanation in retreat_ship() */
193     putland(lp->lnd_uid, lp);
194
195     emp_initque(&list);
196     if (lp->lnd_own)
197         retreat_land_sel(lp, &list, n);
198
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)
206                 continue;
207             if (strncmp(land.lnd_rpath, lp->lnd_rpath, MAX_RETREAT + 1))
208                 continue;
209             retreat_land_sel(&land, &list, n);
210         }
211     }
212
213     /* Loop similar to the one in unit_move().  Keep it that way! */
214     for (i = 0; i < n && !QEMPTY(&list); i++) {
215         /*
216          * Invariant: lnd_may_nav() true for all land units
217          * Implies all are in the same sector
218          */
219         if (!retreat_lands_step(&list, lp->lnd_rpath[i], own))
220             n = i;
221         lnd_mar_stay_behind(&list, own);
222         unit_rad_map_set(&list);
223     }
224
225     if (!QEMPTY(&list))
226         lnd_mar_put(&list, own);
227     getland(lp->lnd_uid, lp);
228 }
229
230 static void
231 retreat_land_sel(struct lndstr *lp, struct emp_qelem *list, int n)
232 {
233     struct lndstr *ldr = QEMPTY(list)
234         ? NULL : &((struct ulist *)(list->q_back))->unit.land;
235
236     if (!lnd_may_mar(lp, ldr, ", and can't retreat!"))
237         return;
238     if (lp->lnd_mobil <= 0) {
239         mpr(lp->lnd_own, "%s has no mobility, and can't retreat!\n",
240             prland(lp));
241         return;
242     }
243
244     if (ldr)
245         mpr(lp->lnd_own, "%s retreats with them\n", prland(lp));
246     else
247         mpr(lp->lnd_own, "%s retreats along path %.*s\n",
248             prland(lp), n, lp->lnd_rpath);
249     lnd_insque(lp, list);
250 }
251
252 static int
253 retreat_lands_step(struct emp_qelem *list, char step, natid actor)
254 {
255     int dir = chkdir(step, DIR_STOP, DIR_LAST);
256     struct emp_qelem *qp;
257     struct ulist *llp;
258     struct lndstr *lp;
259
260     if (dir != DIR_STOP && lnd_mar_dir(list, dir, actor))
261         return 0;               /* can't go there */
262
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) {
268             lp->lnd_mission = 0;
269             lp->lnd_harden = 0;
270         }
271         putland(lp->lnd_uid, lp);
272     }
273
274     return dir != DIR_STOP && !lnd_mar_gauntlet(list, 0, actor);
275 }