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