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