1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2021, 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  *  miss.c: set missions for ships/planes/units
28  *
29  *  Known contributors to this file:
30  *     Thomas Ruschak, 1992
31  *     Steve McClure, 2000
32  *     Markus Armbruster, 2005-2021
33  */
34 
35 #include <config.h>
36 
37 #include "commands.h"
38 #include "mission.h"
39 #include "unit.h"
40 
41 static int clear_mission(struct nstr_item *);
42 static int show_mission(struct nstr_item *);
43 
44 /*
45  *  mission <type> <planes/ships/units> <mission type> <op sector> [<radius>]
46  */
47 int
c_mission(void)48 c_mission(void)
49 {
50     static int ef_with_missions[] = { EF_SHIP, EF_LAND, EF_PLANE, EF_BAD };
51     char *p;
52     int type;
53     int mission;
54     coord x, y;
55     int radius, range;
56     union empobj_storage item;
57     struct empobj *gp;
58     int num = 0;
59     struct nstr_item ni;
60     char buf[1024];
61 
62     p = getstarg(player->argp[1], "Ship, plane or land unit (p,sh,la)? ",
63 		 buf);
64     if (!p)
65 	return RET_SYN;
66     type = ef_byname_from(p, ef_with_missions);
67     if (type < 0) {
68 	pr("Ships, land units or planes only! (s, l, p)\n");
69 	return RET_SYN;
70     }
71     if (!snxtitem(&ni, type, player->argp[2], NULL))
72 	return RET_SYN;
73 
74     p = getstarg(player->argp[3],
75 		 "Mission (int, sup, osup, dsup, esc, res, air, query, clear)? ",
76 		 buf);
77     if (!p)
78 	return RET_SYN;
79 
80 /*
81  * 'i'     interdiction
82  * 's'     support
83  * 'o'     support attacks
84  * 'd'     support defenders
85  * 'e'     escort
86  * 'r'     defensive reserve
87  * 'a'     air defense (intercepts)
88  */
89     switch (*p) {
90     case 'I':
91     case 'i':
92 	mission = MI_INTERDICT;
93 	break;
94     case 'O':
95     case 'o':
96 	mission = MI_OSUPPORT;
97 	break;
98     case 'D':
99     case 'd':
100 	mission = MI_DSUPPORT;
101 	break;
102     case 'S':
103     case 's':
104 	mission = MI_SUPPORT;
105 	break;
106     case 'C':
107     case 'c':
108 	return clear_mission(&ni);
109     case 'E':
110     case 'e':
111 	mission = MI_ESCORT;
112 	break;
113     case 'R':
114     case 'r':
115 	mission = MI_RESERVE;
116 	break;
117     case 'A':
118     case 'a':
119 	mission = MI_AIR_DEFENSE;
120 	break;
121     case 'q':
122 	return show_mission(&ni);
123     default:
124 	pr("bad condition\n");
125 	pr("i\tinterdiction (any)\n");
126 	pr("s\tsupport (tactical planes only)\n");
127 	pr("o\toffensive support (tactical planes only)\n");
128 	pr("d\tdefensive support (tactical planes only)\n");
129 	pr("r\treserve (land units only)\n");
130 	pr("e\tescort (tactical or escort planes only)\n");
131 	pr("a\tair defense (intercept planes only)\n");
132 	pr("c\tclear mission\n");
133 	pr("q\tquery\n");
134 	return RET_SYN;
135     }
136 
137     if (!cando(mission, type)) {
138 	pr("A %s cannot do that mission!\n", ef_nameof(type));
139 	pr("i\tinterdiction (any)\n");
140 	pr("s\tsupport (planes only)\n");
141 	pr("o\toffensive support (planes only)\n");
142 	pr("d\tdefensive support (planes only)\n");
143 	pr("r\treserve (land units only)\n");
144 	pr("e\tescort (planes only)\n");
145 	pr("a\tair defense (planes only)\n");
146 	return RET_FAIL;
147     }
148 
149     p = getstarg(player->argp[4], "operations point? ", buf);
150     if (!p || !*p)
151 	return RET_SYN;
152 
153     if (*p != '.') {
154 	if (!sarg_xy(p, &x, &y))
155 	    return RET_SYN;
156     }
157 
158     if (player->argp[5] != NULL) {
159 	radius = atoi(player->argp[5]);
160 	if (radius < 0) {
161 	    pr("Radius can't be negative!\n");
162 	    return RET_FAIL;
163 	}
164     } else
165 	radius = 9999;
166 
167     while (nxtitem(&ni, &item)) {
168 	gp = &item.gen;
169 
170 	if (!player->owner || gp->own == 0)
171 	    continue;
172 
173 	if (mission == MI_RESERVE) {
174 	    if (!lnd_can_attack((struct lndstr *)gp)) {
175 		pr("%s is not designed to fight ground troops\n",
176 		   unit_nameof(gp));
177 		continue;
178 	    }
179 	    if (!lchr[gp->type].l_rad) {
180 		pr("%s cannot react anywhere!\n", unit_nameof(gp));
181 		continue;
182 	    }
183 	}
184 
185 	if ((mission == MI_INTERDICT) && (type == EF_SHIP))
186 	    if (mchr[(int)gp->type].m_glim == 0) {
187 		pr("%s: cannot fire at range!\n", unit_nameof(gp));
188 		continue;
189 	    }
190 
191 	if ((mission == MI_INTERDICT) && (type == EF_LAND))
192 	    if (lchr[(int)gp->type].l_dam == 0) {
193 		pr("%s: cannot fire at range!\n", unit_nameof(gp));
194 		continue;
195 	    }
196 
197 	if ((mission == MI_INTERDICT) && (type == EF_PLANE)) {
198 	    struct plchrstr *pcp;
199 
200 	    pcp = &plchr[(int)gp->type];
201 	    if (!(pcp->pl_flags & P_T)) {
202 		pr("Only planes with the tactical ability can interdict.\n"
203 		   "%s #%d is ineligible\n",
204 		   pcp->pl_name, gp->uid);
205 		continue;
206 	    }
207 	}
208 
209 	if ((mission == MI_AIR_DEFENSE) && (type == EF_PLANE)) {
210 	    struct plchrstr *pcp;
211 
212 	    pcp = &plchr[(int)gp->type];
213 	    if (!(pcp->pl_flags & P_F)) {
214 		pr("Only planes with the intercept ability can perform air defense.\n"
215 		   "%s #%d is ineligible\n",
216 		   pcp->pl_name, gp->uid);
217 		continue;
218 	    }
219 	}
220 
221 	if ((mission == MI_ESCORT) && (type == EF_PLANE)) {
222 	    struct plchrstr *pcp;
223 
224 	    pcp = &plchr[(int)gp->type];
225 	    if (pcp->pl_flags & P_M) {
226 		pr("Missiles can't escort.\n"
227 		   "%s #%d is ineligible\n",
228 		   pcp->pl_name, gp->uid);
229 		continue;
230 	    }
231 	    if (!(pcp->pl_flags & P_ESC) && !(pcp->pl_flags & P_F)) {
232 		pr("Only planes with the escort or intercept abilities can escort.\n"
233 		   "%s #%d is ineligible\n",
234 		   pcp->pl_name, gp->uid);
235 		continue;
236 	    }
237 	}
238 
239 	if ((mission == MI_SUPPORT || mission == MI_OSUPPORT ||
240 	     mission == MI_DSUPPORT) && (type == EF_PLANE)) {
241 	    struct plchrstr *pcp;
242 
243 	    pcp = &plchr[(int)gp->type];
244 	    if ((pcp->pl_flags & (P_T | P_MAR)) != P_T) {
245 		pr("Only planes with the tactical ability can support.\n"
246 		   "%s #%d is ineligible\n",
247 		   pcp->pl_name, gp->uid);
248 		continue;
249 	    }
250 	}
251 
252 	if (type == EF_PLANE && nuk_on_plane((struct plnstr *)gp) >= 0) {
253 	    pr("%s can't perform a mission"
254 	       " while it is carrying a nuclear weapon\n",
255 	       unit_nameof(gp));
256 	    continue;
257 	}
258 
259 	if (*p == '.') {
260 	    x = gp->x;
261 	    y = gp->y;
262 	}
263 
264 	gp->mission = mission;
265 	range = oprange(gp);
266 	if (range < mapdist(gp->x, gp->y, x, y)) {
267 	    pr("%s: out of range! (range %d)\n",
268 	       unit_nameof(gp), range);
269 	    continue;
270 	}
271 	gp->opx = x;
272 	gp->opy = y;
273 	gp->radius = MIN(range, radius);
274 	put_empobj(type, gp->uid, gp);
275 	num++;
276 
277 	pr("%s on %s mission, centered on %s, radius %d\n",
278 	   unit_nameof(gp), mission_name(mission),
279 	   xyas(x, y, player->cnum), gp->radius);
280     }
281     if (num == 0) {
282 	pr("No %s%s\n", ef_nameof(type), splur(num));
283 	return RET_FAIL;
284     }
285     pr("%d %s%s\n", num, ef_nameof(type), splur(num));
286     return RET_OK;
287 }
288 
289 static int
clear_mission(struct nstr_item * np)290 clear_mission(struct nstr_item *np)
291 {
292     union empobj_storage item;
293 
294     while (nxtitem(np, &item)) {
295 	item.gen.mission = 0;
296 	put_empobj(item.gen.ef_type, item.gen.uid, &item);
297     }
298 
299     return RET_OK;
300 }
301 
302 static int
show_mission(struct nstr_item * np)303 show_mission(struct nstr_item *np)
304 {
305     int first = 1;
306     union empobj_storage item;
307     struct empobj *gp;
308 
309     while (nxtitem(np, &item)) {
310 	gp = &item.gen;
311 	if (!player->owner || gp->own == 0)
312 	    continue;
313 
314 	if (first) {
315 	    pr("Thing                        x,y     op-sect  rad mission\n");
316 	    first = 0;
317 	}
318 	pr("%-25s", unit_nameof(gp));
319 	prxy(" %4d,%-4d", gp->x, gp->y);
320 	switch (gp->mission) {
321 	case MI_INTERDICT:
322 	case MI_SUPPORT:
323 	case MI_RESERVE:
324 	case MI_ESCORT:
325 	case MI_AIR_DEFENSE:
326 	case MI_DSUPPORT:
327 	case MI_OSUPPORT:
328 	    prxy(" %4d,%-4d", gp->opx, gp->opy);
329 	    pr(" %3d", gp->radius);
330 	    break;
331 	default:
332 	    CANT_REACH();
333 	    /* fall through */
334 	case MI_NONE:
335 	    pr("              ");
336 	}
337 	if (gp->mission)
338 	    pr(" is on %s mission\n", mission_name(gp->mission));
339 	else
340 	    pr(" has no mission.\n");
341     }
342 
343     return RET_OK;
344 }
345