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