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