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