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