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