]> git.pond.sub.org Git - empserver/blob - src/lib/commands/miss.c
Don't reduce mission op area when range shrinks
[empserver] / src / lib / commands / miss.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program 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 2 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, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  miss.c: set missions for ships/planes/units
29  *
30  *  Known contributors to this file:
31  *     Thomas Ruschak, 1992
32  *     Steve McClure, 2000
33  */
34
35 #include <config.h>
36
37 #include "commands.h"
38 #include "empobj.h"
39 #include "mission.h"
40 #include "optlist.h"
41 #include "path.h"
42
43 /*
44  *  mission <type> <planes/ships/units> <mission type> <op sector> [<radius>]
45  */
46 int
47 mission(void)
48 {
49     static int ef_with_missions[] = { EF_SHIP, EF_LAND, EF_PLANE, EF_BAD };
50     char *p;
51     int type;
52     int mission;
53     coord x, y;
54     int desired_radius, radius;
55     struct sctstr opsect;
56     union empobj_storage item;
57     struct empobj *gp;
58     int num = 0, mobmax, mobused, dist;
59     struct nstr_item ni;
60     char buf[1024];
61
62     if ((p =
63          getstarg(player->argp[1], "Ship, plane or land unit (p,sh,la)? ",
64                   buf)) == 0)
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     if ((p =
75          getstarg(player->argp[3],
76                   "Mission (int, sup, osup, dsup, esc, res, air, query, clear)? ",
77                   buf)) == 0)
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         mission = 0;
109         break;
110     case 'E':
111     case 'e':
112         mission = MI_ESCORT;
113         break;
114     case 'R':
115     case 'r':
116         mission = MI_RESERVE;
117         break;
118     case 'A':
119     case 'a':
120         mission = MI_AIR_DEFENSE;
121         break;
122     case 'q':
123         show_mission(type, &ni);
124         return RET_OK;
125     default:
126         pr("bad condition\n");
127         pr("i\tinterdiction (any)\n");
128         pr("s\tsupport (tactical planes only)\n");
129         pr("o\toffensive support (tactical planes only)\n");
130         pr("d\tdefensive support (tactical planes only)\n");
131         pr("r\treserve (land units only)\n");
132         pr("e\tescort (tactical or escort planes only)\n");
133         pr("a\tair defense (intercept planes only)\n");
134         pr("c\tclear mission\n");
135         pr("q\tquery\n");
136         return RET_SYN;
137     }
138
139     if (mission && !cando(mission, type)) {
140         pr("A %s cannot do that mission!\n", ef_nameof(type));
141         pr("i\tinterdiction (any)\n");
142         pr("s\tsupport (planes only)\n");
143         pr("o\toffensive support (planes only)\n");
144         pr("d\tdefensive support (planes only)\n");
145         pr("r\treserve (land units only)\n");
146         pr("e\tescort (planes only)\n");
147         pr("a\tair defense (planes only)\n");
148         return RET_FAIL;
149     }
150
151     if (mission && ((mission != MI_RESERVE) && (mission != MI_ESCORT))) {
152         if ((p = getstarg(player->argp[4], "operations point? ", buf)) == 0
153             || *p == 0)
154             return RET_SYN;
155
156         if (*p != '.') {
157             if (!sarg_xy(p, &x, &y))
158                 return RET_SYN;
159
160             if (!getsect(x, y, &opsect))
161                 return RET_FAIL;
162         }
163     } else {
164         x = 0;
165         y = 0;
166     }
167
168     if (player->argp[5] != NULL) {
169         desired_radius = atoi(player->argp[5]);
170         if (desired_radius < 0) {
171             pr("Radius must be greater than zero!\n");
172             return RET_FAIL;
173         }
174     } else {
175         desired_radius = 9999;
176     }
177
178     if ((mobmax = get_empobj_mob_max(type)) == -1)
179         return RET_FAIL;
180
181     mobused = ldround(mission_mob_cost * (double)mobmax, 1);
182
183     while (nxtitem(&ni, &item)) {
184         gp = (struct empobj *)&item;
185
186         if (!player->owner || gp->own == 0)
187             continue;
188
189         if ((mission && (gp->mobil < mobused)) && mission_mob_cost) {
190             pr("%s: not enough mobility! (needs %d)\n",
191                obj_nameof(gp), mobused);
192             continue;
193         }
194         if (mission == MI_RESERVE && !lnd_can_attack((struct lndstr *)gp)) {
195             pr("%s is not designed to fight ground troops\n",
196                obj_nameof(gp));
197             continue;
198         }
199         if (*p == '.') {
200             x = gp->x;
201             y = gp->y;
202             if (!getsect(x, y, &opsect))
203                 return RET_FAIL;
204         }
205
206         dist = mapdist(gp->x, gp->y, x, y);
207         radius = 999;
208         if ((mission == MI_INTERDICT || mission == MI_SUPPORT ||
209              mission == MI_OSUPPORT || mission == MI_DSUPPORT ||
210              mission == MI_AIR_DEFENSE)) {
211             radius = oprange(gp);
212             if (radius < dist) {
213                 pr("%s: out of range! (range %d)\n",
214                    obj_nameof(gp), radius);
215                 continue;
216             }
217         }
218
219         if (radius > desired_radius)
220             radius = desired_radius;
221
222         if ((mission == MI_INTERDICT) && (type == EF_SHIP))
223             if (mchr[(int)gp->type].m_glim == 0) {
224                 pr("%s: cannot fire at range!\n", obj_nameof(gp));
225                 continue;
226             }
227
228         if ((mission == MI_INTERDICT) && (type == EF_LAND))
229             if (lchr[(int)gp->type].l_dam == 0) {
230                 pr("%s: cannot fire at range!\n", obj_nameof(gp));
231                 continue;
232             }
233
234         if ((mission == MI_INTERDICT) && (type == EF_PLANE)) {
235             struct plchrstr *pcp;
236
237             pcp = &plchr[(int)gp->type];
238             if (!(pcp->pl_flags & P_T)) {
239                 pr("Only planes with the tactical ability can interdict.\n"
240                    "%s #%d is ineligible\n",
241                    pcp->pl_name, gp->uid);
242                 continue;
243             }
244         }
245
246         if ((mission == MI_AIR_DEFENSE) && (type == EF_PLANE)) {
247             struct plchrstr *pcp;
248
249             pcp = &plchr[(int)gp->type];
250             if (!(pcp->pl_flags & P_F)) {
251                 pr("Only planes with the intercept abilities can perform air defense.\n"
252                    "%s #%d is ineligible\n",
253                    pcp->pl_name, gp->uid);
254                 continue;
255             }
256         }
257
258         if ((mission == MI_ESCORT) && (type == EF_PLANE)) {
259             struct plchrstr *pcp;
260
261             pcp = &plchr[(int)gp->type];
262             if (!(pcp->pl_flags & P_ESC) && !(pcp->pl_flags & P_F)) {
263                 pr("Only planes with the escort or intercept abilities can escort.\n"
264                    "%s #%d is ineligible\n",
265                    pcp->pl_name, gp->uid);
266                 continue;
267             }
268         }
269
270         if ((mission == MI_SUPPORT || mission == MI_OSUPPORT ||
271              mission == MI_DSUPPORT) && (type == EF_PLANE)) {
272             struct plchrstr *pcp;
273
274             pcp = &plchr[(int)gp->type];
275             if (!(pcp->pl_flags & P_T)) {
276                 pr("Only planes with the tactical ability can support.\n"
277                    "%s #%d is ineligible\n",
278                    pcp->pl_name, gp->uid);
279                 continue;
280             }
281         }
282
283         num++;                  /* good one.. go with it */
284
285         if (mission == MI_INTERDICT || mission == MI_SUPPORT ||
286             mission == MI_OSUPPORT || mission == MI_DSUPPORT ||
287             mission == MI_AIR_DEFENSE)
288             gp->radius = radius;
289         else
290             gp->radius = 0;
291
292         if (mission == MI_SUPPORT || mission == MI_OSUPPORT ||
293             mission == MI_DSUPPORT || mission == MI_INTERDICT ||
294             mission == MI_AIR_DEFENSE) {
295             pr("%s on %s mission, centered on %s, radius %d\n",
296                obj_nameof(gp), mission_name(mission),
297                xyas(x, y, player->cnum), gp->radius);
298         } else if (mission == MI_RESERVE) {
299             radius = ((struct lndstr *)gp)->lnd_rad_max;
300             if (radius) {
301                 getsect(gp->x, gp->y, &opsect);
302                 if ((opsect.sct_type == SCT_HEADQ)
303                     && (opsect.sct_effic >= 60))
304                     radius++;
305             }
306
307             pr("%s on %s mission with maximum reaction radius %d\n",
308                obj_nameof(gp), mission_name(mission), radius);
309         } else if (mission) {
310             pr("%s on %s mission\n", obj_nameof(gp),
311                mission_name(mission));
312         }
313
314         if (mission)
315             gp->mobil -= mobused;
316         gp->mission = mission;
317         gp->opx = x;
318         gp->opy = y;
319         put_empobj(type, gp->uid, gp);
320     }
321     if (num == 0) {
322         pr("No %s%s\n", ef_nameof(type), splur(num));
323         return RET_FAIL;
324     }
325     pr("%d %s%s\n", num, ef_nameof(type), splur(num));
326     return RET_OK;
327 }