]> git.pond.sub.org Git - empserver/blob - src/lib/commands/laun.c
Limit nukes to strategic missions
[empserver] / src / lib / commands / laun.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2009, 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  *  laun.c: Launch missiles from land or sea
29  *
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Ken Stevens, 1995
33  *     Steve McClure, 1998-2000
34  *     Markus Armbruster, 2005-2008
35  */
36
37 #include <config.h>
38
39 #include "commands.h"
40 #include "damage.h"
41 #include "mission.h"
42 #include "news.h"
43 #include "nuke.h"
44 #include "optlist.h"
45 #include "path.h"
46 #include "plane.h"
47 #include "ship.h"
48
49 static int launch_as(struct plnstr *pp);
50 static int launch_missile(struct plnstr *pp, int sublaunch);
51 static int launch_sat(struct plnstr *pp, int sublaunch);
52
53 /*
54  * laun <PLANES>
55  */
56 int
57 laun(void)
58 {
59     struct nstr_item nstr;
60     struct plnstr plane;
61     struct shpstr ship;
62     int sublaunch;
63     struct plchrstr *pcp;
64     int retval, gone;
65
66     if (!snxtitem(&nstr, EF_PLANE, player->argp[1], NULL))
67         return RET_SYN;
68     while (nxtitem(&nstr, &plane)) {
69         if (plane.pln_own != player->cnum)
70             continue;
71         pcp = &plchr[(int)plane.pln_type];
72         if ((pcp->pl_flags & (P_M | P_O)) == 0) {
73             pr("%s isn't a missile!\n", prplane(&plane));
74             continue;
75         }
76         if (pcp->pl_flags & P_F) {
77             pr("%s is a surface-to-air missile!\n", prplane(&plane));
78             continue;
79         }
80         if (pcp->pl_flags & P_N) {
81             pr("%s is an anti-ballistic-missile missile!\n",
82                prplane(&plane));
83             continue;
84         }
85         if (pln_is_in_orbit(&plane)) {
86             pr("%s already in orbit!\n", prplane(&plane));
87             continue;
88         }
89         if (opt_MARKET) {
90             if (ontradingblock(EF_PLANE, &plane)) {
91                 pr("plane #%d inelligible - it's for sale.\n",
92                    plane.pln_uid);
93                 continue;
94             }
95         }
96
97         if (plane.pln_effic < 40) {
98             pr("%s is damaged (%d%%)\n", prplane(&plane), plane.pln_effic);
99             continue;
100         }
101         if (!pln_airbase_ok(&plane, 1, 1))
102             continue;
103         sublaunch = 0;
104         if (plane.pln_ship >= 0) {
105             getship(plane.pln_ship, &ship);
106             if (mchr[(int)ship.shp_type].m_flags & M_SUB)
107                 sublaunch = 1;
108         }
109         pr("%s at %s; range %d, eff %d%%\n", prplane(&plane),
110            xyas(plane.pln_x, plane.pln_y, player->cnum),
111            plane.pln_range, plane.pln_effic);
112         if (!(pcp->pl_flags & P_O)) {
113             retval = launch_missile(&plane, sublaunch);
114             gone = 1;
115         } else if ((pcp->pl_flags & (P_M | P_O)) == (P_M | P_O)) {
116             retval = launch_as(&plane);
117             gone = 1;
118         } else {                /* satellites */
119             retval = launch_sat(&plane, sublaunch);
120             gone = !(plane.pln_flags & PLN_LAUNCHED);
121         }
122         if (retval != RET_OK)
123             return retval;
124         if (gone) {
125             plane.pln_effic = 0;
126             putplane(plane.pln_uid, &plane);
127         }
128     }
129     return RET_OK;
130 }
131
132 /*
133  * Launch anti-sat weapon PP.
134  * Return RET_OK if launched (even when missile explodes),
135  * else RET_SYN or RET_FAIL.
136  */
137 static int
138 launch_as(struct plnstr *pp)
139 {
140     char *cp, buf[1024];
141     struct plnstr plane;
142
143     cp = getstarg(player->argp[2], "Target satellite? ", buf);
144     if (!check_plane_ok(pp))
145         return RET_FAIL;
146     if (!cp || !*cp)
147         return RET_SYN;
148     if (!getplane(atoi(cp), &plane) || !plane.pln_own
149         || !pln_is_in_orbit(&plane)) {
150         pr("No such satellite exists!\n");
151         return RET_FAIL;
152         /* Can be abused to find satellite ids.  Tolerable.  */
153     }
154
155     if (mapdist(pp->pln_x, pp->pln_y, plane.pln_x, plane.pln_y)
156         > pp->pln_range) {
157         pr("Range too great!\n");
158         return RET_FAIL;
159     }
160     if (msl_equip(pp, 'i') < 0)
161         return RET_FAIL;
162     if (msl_hit(pp, pln_def(&plane), EF_PLANE, N_SAT_KILL, N_SAT_KILL,
163                 prplane(&plane), plane.pln_x, plane.pln_y, plane.pln_own)) {
164         pr("Satellite shot down\n");
165         mpr(plane.pln_own, "%s anti-sat destroyed %s over %s\n",
166             cname(player->cnum), prplane(&plane),
167             xyas(plane.pln_x, plane.pln_y, plane.pln_own));
168         plane.pln_effic = 0;
169         putplane(plane.pln_uid, &plane);
170     }
171     return RET_OK;
172 }
173
174 /*
175  * Launch missile PP.
176  * If SUBLAUNCH, it's sub-launched.
177  * Return RET_OK if launched (even when missile explodes),
178  * else RET_SYN or RET_FAIL.
179  */
180 static int
181 launch_missile(struct plnstr *pp, int sublaunch)
182 {
183     struct plchrstr *pcp = plchr + pp->pln_type;
184     coord sx, sy;
185     int n, dam;
186     char *cp;
187     struct mchrstr *mcp;
188     struct shpstr target_ship;
189     struct sctstr sect;
190     int rel;
191     struct natstr *natp;
192     struct nukstr nuke;
193     char buf[1024];
194
195     if (pcp->pl_flags & P_MAR)
196         cp = getstarg(player->argp[2], "Target ship? ", buf);
197     else
198         cp = getstarg(player->argp[2], "Target sector? ", buf);
199     if (!cp || !*cp)
200         return RET_SYN;
201     if (!check_plane_ok(pp))
202         return RET_FAIL;
203     if (sarg_type(cp) == NS_LIST) {
204         if (!(pcp->pl_flags & P_MAR)) {
205             pr("Missile not designed to attack ships!\n");
206             return RET_FAIL;
207         }
208         n = atoi(cp);
209         if ((n < 0) || !getship(n, &target_ship) ||
210             !target_ship.shp_own) {
211             pr("Bad ship number!\n");
212             return RET_FAIL;
213         }
214         sx = target_ship.shp_x;
215         sy = target_ship.shp_y;
216         mcp = &mchr[(int)target_ship.shp_type];
217         if (mcp->m_flags & M_SUB) {
218             pr("Bad ship number!\n");
219             return RET_FAIL;
220         }
221     } else if (!sarg_xy(cp, &sx, &sy)) {
222         pr("Not a sector!\n");
223         return RET_FAIL;
224     } else {
225         if (pcp->pl_flags & P_MAR) {
226             pr("Missile designed to attack ships!\n");
227             return RET_FAIL;
228         }
229     }
230
231     if (mapdist(pp->pln_x, pp->pln_y, sx, sy) > pp->pln_range) {
232         pr("Range too great; try again!\n");
233         return RET_FAIL;
234     }
235     if (!(pcp->pl_flags & P_MAR)) {
236         if (msl_equip(pp, 's') < 0)
237             return RET_FAIL;
238         getsect(sx, sy, &sect);
239         if (opt_SLOW_WAR) {
240             natp = getnatp(player->cnum);
241             rel = getrel(natp, sect.sct_own);
242             if ((rel != AT_WAR) && (sect.sct_own != player->cnum) &&
243                 (sect.sct_own) && (sect.sct_oldown != player->cnum)) {
244                 pr("You are not at war with the player->owner of the target sector!\n");
245                 pr_beep();
246                 pr("Kaboom!!!\n");
247                 pr("Missile monitoring officer destroys RV before detonation.\n");
248                 return RET_OK;
249             }
250         }
251         if (!msl_hit(pp, SECT_HARDTARGET, EF_SECTOR, N_SCT_MISS,
252                      N_SCT_SMISS, "sector", sx, sy, sect.sct_own)) {
253 #if 0
254             /*
255              * FIXME want collateral damage on miss, but we get here
256              * too when launch fails or missile is intercepted
257              */
258             dam = pln_damage(pp, 's', 0);
259             collateral_damage(sect.sct_x, sect.sct_y, dam, 0);
260 #endif
261             return RET_OK;
262         }
263         if (getnuke(nuk_on_plane(pp), &nuke)) {
264             mpr(pp->pln_own,
265                 "Releasing RV's for %s detonation...\n",
266                 pp->pln_flags & PLN_AIRBURST ? "airburst" : "groundburst");
267             detonate(&nuke, sx, sy, pp->pln_flags & PLN_AIRBURST);
268         } else {
269             dam = pln_damage(pp, 's', 1);
270             pr("did %d damage in %s\n", PERCENT_DAMAGE(dam),
271                xyas(sx, sy, player->cnum));
272             if (sect.sct_own != 0) {
273                 if (sublaunch)
274                     wu(0, sect.sct_own,
275                        "Sub missile attack did %d damage in %s\n",
276                        dam, xyas(sx, sy, sect.sct_own));
277                 else
278                     wu(0, sect.sct_own,
279                        "%s missile attack did %d damage in %s\n",
280                        cname(player->cnum), dam,
281                        xyas(sx, sy, sect.sct_own));
282             }
283             sectdamage(&sect, dam);
284             putsect(&sect);
285         }
286     } else {
287         if (msl_equip(pp, 'p') < 0)
288             return RET_FAIL;
289         if (!msl_hit(pp, shp_hardtarget(&target_ship), EF_SHIP,
290                      N_SHP_MISS, N_SHP_SMISS, prship(&target_ship),
291                      target_ship.shp_x, target_ship.shp_y,
292                      target_ship.shp_own)) {
293             pr("splash\n");
294 #if 0 /* FIXME see above */
295             dam = pln_damage(pp, 'p', 0);
296             collateral_damage(target_ship.shp_x, target_ship.shp_y, dam, 0);
297 #endif
298             return RET_OK;
299         }
300         dam = pln_damage(pp, 'p', 1);
301         check_retreat_and_do_shipdamage(&target_ship, dam);
302         putship(target_ship.shp_uid, &target_ship);
303         getship(target_ship.shp_uid, &target_ship);
304         if (!target_ship.shp_own)
305             pr("%s sunk!\n", prship(&target_ship));
306     }
307     return RET_OK;
308 }
309
310 /*
311  * Launch a satellite.
312  * Return RET_OK if launched (even when satellite fails),
313  * else RET_SYN or RET_FAIL.
314  */
315 static int
316 launch_sat(struct plnstr *pp, int sublaunch)
317 {
318     struct plchrstr *pcp = plchr + pp->pln_type;
319     coord sx, sy;
320     int i;
321     int dist;
322     int dir;
323     char *cp;
324     char *p;
325     char buf[1024];
326
327     pr("\n");
328     cp = getstarg(player->argp[2], "Target sector? ", buf);
329     if (!check_plane_ok(pp))
330         return RET_FAIL;
331     if (!cp || !*cp)
332         return RET_SYN;
333     if (!sarg_xy(cp, &sx, &sy)) {
334         pr("Bad sector designation!\n");
335         return RET_SYN;
336     }
337     if ((dist = mapdist(pp->pln_x, pp->pln_y, sx, sy)) > pp->pln_range) {
338         pr("Range too great; try again!\n");
339         return RET_FAIL;
340     }
341     p = getstring("Geostationary orbit? ", buf);
342     if (!p)
343         return RET_SYN;
344     if (!check_plane_ok(pp))
345         return RET_FAIL;
346     if (msl_equip(pp, 'r') < 0)
347         return RET_FAIL;
348     pp->pln_theta = 0;
349     pp->pln_flags |= PLN_SYNCHRONOUS;
350     if (*p == 0 || *p == 'n')
351         pp->pln_flags &= ~(PLN_SYNCHRONOUS);
352     pr("3... 2... 1... Blastoff!!!\n");
353     if (chance(0.07 + (100 - pp->pln_effic) / 100.0)) {
354         pr("KABOOOOM!  Range safety officer detonates booster!\n");
355         pp->pln_effic = 0;
356         return RET_OK;
357     }
358     i = pp->pln_tech + pp->pln_effic;
359     if (chance(1.0 - (i / (i + 50.0)))) {
360         dir = (random() % 6) + 1;
361         sx = xnorm(sx + diroff[dir][0]);
362         sy = ynorm(sy + diroff[dir][1]);
363         pr("Your trajectory was a little off.\n");
364     }
365     nreport(player->cnum, N_LAUNCH, 0, 1);
366     if (msl_intercept(sx, sy, pp->pln_own, pcp->pl_def, sublaunch, P_O, 0)) {
367         return RET_OK;
368     }
369     pp->pln_x = sx;
370     pp->pln_y = sy;
371     CANT_HAPPEN(pp->pln_flags & PLN_LAUNCHED);
372     pp->pln_flags |= PLN_LAUNCHED;
373     pp->pln_mobil = pp->pln_mobil > dist ? pp->pln_mobil - dist : 0;
374     putplane(pp->pln_uid, pp);
375     pr("%s positioned over %s, will be ready for use in %d time units\n",
376        prplane(pp), xyas(sx, sy, player->cnum),
377        plane_mob_max - pp->pln_mobil);
378     return RET_OK;
379 }