]> git.pond.sub.org Git - empserver/blob - src/lib/commands/bomb.c
2a8ecaa6516e83cfe5c6b3a445d21ef25f8dbadc
[empserver] / src / lib / commands / bomb.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, 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  *  bomb.c: Fly bombing missions
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Ken Stevens, 1995
33  *     Steve McClure, 1998-2000
34  */
35
36 #include <ctype.h>
37 #include "misc.h"
38 #include "player.h"
39 #include "var.h"
40 #include "sect.h"
41 #include "ship.h"
42 #include "land.h"
43 #include "item.h"
44 #include "plane.h"
45 #include "nuke.h"
46 #include "retreat.h"
47 #include "xy.h"
48 #include "nsc.h"
49 #include "news.h"
50 #include "file.h"
51 #include "nat.h"
52 #include "path.h"
53 #include "optlist.h"
54 #include "damage.h"
55 #include "commands.h"
56
57 static void pin_bomb(struct emp_qelem *list, struct sctstr *target);
58 static void strat_bomb(struct emp_qelem *list, struct sctstr *target);
59 static void comm_bomb(struct emp_qelem *list, struct sctstr *target);
60 static void eff_bomb(struct emp_qelem *list, struct sctstr *target);
61 static int pinflak_planedamage(struct plnstr *pp, struct plchrstr *pcp,
62                                natid from, int flak);
63 static void plane_bomb(struct emp_qelem *list, struct sctstr *target);
64 static void land_bomb(struct emp_qelem *list, struct sctstr *target);
65 static void ship_bomb(struct emp_qelem *list, struct sctstr *target);
66
67 static int bombcomm[] = {
68     I_CIVIL,
69     I_MILIT,
70     I_SHELL,
71     I_GUN,
72     I_PETROL,
73     I_IRON,
74     I_DUST,
75     I_BAR,
76     I_FOOD,
77     I_OIL,
78     I_LCM,
79     I_HCM,
80     I_UW,
81     I_RAD
82 };
83 static int nbomb = sizeof(bombcomm) / sizeof(int);
84
85 int
86 bomb(void)
87 {
88     s_char *p;
89     int mission_flags;
90     int tech;
91     coord tx, ty;
92     coord ax, ay;
93     int ap_to_target;
94     struct ichrstr *ip;
95     s_char flightpath[MAX_PATH_LEN];
96     struct nstr_item ni_bomb;
97     struct nstr_item ni_esc;
98     coord x, y;
99     struct sctstr target;
100     struct emp_qelem bomb_list;
101     struct emp_qelem esc_list;
102     int wantflags;
103     struct sctstr ap_sect;
104     s_char mission;
105     int rel;
106     struct natstr *natp;
107     s_char buf[1024];
108
109     wantflags = 0;
110     if (!snxtitem(&ni_bomb, EF_PLANE, player->argp[1]))
111         return RET_SYN;
112     if (!snxtitem
113         (&ni_esc, EF_PLANE, getstarg(player->argp[2], "escort(s)? ", buf)))
114         pr("No escorts...\n");
115     if ((p =
116          getstarg(player->argp[3], "pinpoint, or strategic? ", buf)) == 0)
117         return RET_SYN;
118     mission = *p;
119     if (index("ps", mission) == 0)
120         return RET_SYN;
121     if ((p = getstarg(player->argp[4], "assembly point? ", buf)) == 0
122         || *p == 0)
123         return RET_SYN;
124     if (!sarg_xy(p, &x, &y) || !getsect(x, y, &ap_sect))
125         return RET_SYN;
126     if (ap_sect.sct_own && ap_sect.sct_own != player->cnum &&
127         getrel(getnatp(ap_sect.sct_own), player->cnum) != ALLIED) {
128         pr("Assembly point not owned by you or an ally!\n");
129         return RET_SYN;
130     }
131     ax = x;
132     ay = y;
133     if (getpath(flightpath, player->argp[5], ax, ay, 0, 0,
134                 0, P_FLYING) == 0 || *flightpath == 0)
135         return RET_SYN;
136     tx = ax;
137     ty = ay;
138     (void)pathtoxy(flightpath, &tx, &ty, fcost);
139     pr("target sector is %s\n", xyas(tx, ty, player->cnum));
140     getsect(tx, ty, &target);
141     if (target.sct_type == SCT_SANCT) {
142         pr("You can't bomb that sector!\n");
143         return RET_SYN;
144     }
145     ip = 0;
146     ap_to_target = strlen(flightpath);
147     if (*(flightpath + strlen(flightpath) - 1) == 'h')
148         ap_to_target--;
149     pr("range to target is %d\n", ap_to_target);
150     /*
151      * select planes within range
152      */
153     mission_flags = 0;
154     pln_sel(&ni_bomb, &bomb_list, &ap_sect, ap_to_target,
155             2, wantflags, P_M | P_O);
156     pln_sel(&ni_esc, &esc_list, &ap_sect, ap_to_target,
157             2, wantflags | P_F | P_ESC, P_M | P_O);
158     /*
159      * now arm and equip the bombers, transports, whatever.
160      * tech is stored in high 16 bits of mission_flags.
161      * yuck.
162      */
163     tech = 0;
164     mission_flags |= P_X;       /* stealth (shhh) */
165     mission_flags |= P_H;       /* gets turned off if not all choppers */
166     mission_flags =
167         pln_arm(&bomb_list, 2 * ap_to_target, mission, ip, 0,
168                 mission_flags, &tech);
169     if (QEMPTY(&bomb_list)) {
170         pr("No planes could be equipped for the mission.\n");
171         return RET_FAIL;
172     }
173     mission_flags =
174         pln_arm(&esc_list, 2 * ap_to_target, mission, ip, P_F | P_ESC,
175                 mission_flags, &tech);
176     ac_encounter(&bomb_list, &esc_list, ax, ay, flightpath, mission_flags,
177                  0, 0, 0);
178     if (QEMPTY(&bomb_list)) {
179         pr("No planes got through fighter defenses\n");
180     } else {
181         switch (mission) {
182         case 'p':
183             pin_bomb(&bomb_list, &target);
184             break;
185         case 's':
186             if (opt_SLOW_WAR) {
187                 natp = getnatp(player->cnum);
188                 if (target.sct_own) {
189                     rel = getrel(natp, target.sct_own);
190                     if ((rel != AT_WAR) && (player->cnum != target.sct_own)
191                         && (target.sct_own) &&
192                         (target.sct_oldown != player->cnum)) {
193                         pr("You're not at war with them!\n");
194                         pln_put(&bomb_list);
195                         pln_put(&esc_list);
196                         return RET_FAIL;
197                     }
198                 }
199             }
200             nreport(player->cnum, N_SCT_BOMB, target.sct_own, 1);
201             strat_bomb(&bomb_list, &target);
202             break;
203         default:
204             pr("Bad mission %c\n", mission);
205             break;
206         }
207     }
208     pln_put(&bomb_list);
209     pln_put(&esc_list);
210     return RET_OK;
211 }
212
213 static void
214 pin_bomb(struct emp_qelem *list, struct sctstr *target)
215 {
216     extern s_char *effadv();
217     struct dchrstr *dcp;
218     int nplanes;
219     int nships;
220     int type;
221     int bad;
222     s_char *p;
223     int vec[I_MAX + 1];
224     struct plist *plp;
225     struct emp_qelem *qp;
226     int bestacc;
227     int nsubs;
228     int nunits;
229     struct natstr *natp;
230     int rel;
231     s_char buf[1024];
232     int i;
233
234     bad = 0;
235     type = target->sct_type;
236     dcp = &dchr[type];
237     pr("Target sector is a %s constructed %s\n",
238        effadv((int)target->sct_effic), dcp->d_name);
239     nsubs = 0;
240     plp = (struct plist *)list->q_forw;
241     if (plp->pcp->pl_flags & P_A) {
242         bestacc = 0;
243         for (qp = list->q_forw; qp != list; qp = qp->q_forw)
244             plp = (struct plist *)qp;
245         if (plp->plane.pln_acc < bestacc)
246             bestacc = plp->plane.pln_acc;
247         nships = num_shipsatxy(target->sct_x, target->sct_y, 0, 0);
248         nsubs = nships - shipsatxy(target->sct_x, target->sct_y, 0, M_SUB);
249         if (nsubs > 0)
250             pr("Some subs are present in the sector.\n");
251     } else {
252         nships = shipsatxy(target->sct_x, target->sct_y, 0, M_SUB);
253     }
254     nplanes = planesatxy(target->sct_x, target->sct_y, 0, 0, list);
255     nunits = unitsatxy(target->sct_x, target->sct_y, 0, 0);
256     getvec(VT_ITEM, vec, (s_char *)target, EF_SECTOR);
257   retry:
258     p = getstring
259         ("Bomb what? (ship, plane, land unit, efficiency, commodities) ",
260          buf);
261     if (p == 0 || *p == 0) {
262         if (player->aborted)
263             return;
264         bad++;
265         if (bad > 2)
266             return;
267         goto retry;
268     }
269     switch (*p) {
270     case 'l':
271         if (opt_SLOW_WAR) {
272             natp = getnatp(player->cnum);
273             if (target->sct_own) {
274                 rel = getrel(natp, target->sct_own);
275                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
276                     && (target->sct_own) &&
277                     (target->sct_oldown != player->cnum)) {
278                     pr("You're not at war with them!\n");
279                     goto retry;
280                 }
281             }
282         }
283         if (nunits == 0) {
284             pr("no units there\n");
285             goto retry;
286         }
287         land_bomb(list, target);
288         break;
289     case 'p':
290         if (opt_SLOW_WAR) {
291             natp = getnatp(player->cnum);
292             if (target->sct_own) {
293                 rel = getrel(natp, target->sct_own);
294                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
295                     && (target->sct_own) &&
296                     (target->sct_oldown != player->cnum)) {
297                     pr("You're not at war with them!\n");
298                     goto retry;
299                 }
300             }
301         }
302         if (nplanes == 0) {
303             pr("no planes there\n");
304             goto retry;
305         }
306         plane_bomb(list, target);
307         break;
308     case 's':
309         if (nships == 0) {
310             if (((struct plist *)list->q_forw)->pcp->pl_flags & P_A) {
311                 if (nsubs == 0) {
312                     pr("no ships there\n");
313                     goto retry;
314                 }
315             } else {
316                 pr("no ships there\n");
317                 goto retry;
318             }
319         }
320         ship_bomb(list, target);
321         break;
322     case 'c':
323         if (opt_SLOW_WAR) {
324             natp = getnatp(player->cnum);
325             if (target->sct_own) {
326                 rel = getrel(natp, target->sct_own);
327                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
328                     && (target->sct_own) &&
329                     (target->sct_oldown != player->cnum)) {
330                     pr("You're not at war with them!\n");
331                     goto retry;
332                 }
333             }
334         }
335
336         for (i = 0; i < nbomb; i++) {
337             if (!vec[bombcomm[i]])
338                 continue;
339             break;
340         }
341         if (i >= nbomb) {
342             pr("No bombable commodities in %s\n",
343                xyas(target->sct_x, target->sct_y, player->cnum));
344             goto retry;
345         }
346         comm_bomb(list, target);
347         break;
348     case 'e':
349         if (opt_SLOW_WAR) {
350             natp = getnatp(player->cnum);
351             if (target->sct_own) {
352                 rel = getrel(natp, target->sct_own);
353                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
354                     && (target->sct_own) &&
355                     (target->sct_oldown != player->cnum)) {
356                     pr("You're not at war with them!\n");
357                     goto retry;
358                 }
359             }
360         }
361         eff_bomb(list, target);
362         break;
363     case 'q':
364         pr("Aborting mission.\n");
365         return;
366     default:
367         pr("Bad target type.\n");
368         goto retry;
369     }
370 }
371
372 static void
373 eff_bomb(struct emp_qelem *list, struct sctstr *target)
374 {
375     register struct plist *plp;
376     struct emp_qelem *qp;
377     struct sctstr sect;
378     int oldeff, dam = 0;
379     int nukedam;
380
381     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
382         plp = (struct plist *)qp;
383         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
384             continue;
385         if (plp->bombs || plp->plane.pln_nuketype != -1)
386             dam +=
387                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
388                            &nukedam, 1);
389     }
390     if (dam <= 0)               /* dam == 0 if only nukes were delivered */
391         return;
392     getsect(target->sct_x, target->sct_y, &sect);
393     target = &sect;
394     oldeff = target->sct_effic;
395     target->sct_effic = effdamage(target->sct_effic, dam);
396     target->sct_road = effdamage(target->sct_road, dam);
397     target->sct_rail = effdamage(target->sct_rail, dam);
398     target->sct_defense = effdamage(target->sct_defense, dam);
399     if (!opt_DEFENSE_INFRA)
400         target->sct_defense = target->sct_effic;
401     pr("did %d%% damage to efficiency in %s\n",
402        oldeff - target->sct_effic,
403        xyas(target->sct_x, target->sct_y, player->cnum));
404     if (target->sct_own)
405         wu(0, target->sct_own,
406            "%s bombing raid did %d%% damage in %s\n",
407            cname(player->cnum), oldeff - target->sct_effic,
408            xyas(target->sct_x, target->sct_y, target->sct_own));
409     if (target->sct_effic < 20) {
410         if (target->sct_type == SCT_BSPAN)
411             knockdown(target, list);
412         else if (target->sct_type == SCT_BTOWER) {
413             knockdown(target, list);
414             bridgefall(target, list);
415         }
416     }
417     putsect(&sect);
418     collateral_damage(target->sct_x, target->sct_y, dam, list);
419 }
420
421 static void
422 comm_bomb(struct emp_qelem *list, struct sctstr *target)
423 {
424     register struct plist *plp;
425     float b;
426     int i;
427     int amt, before;
428     struct ichrstr *ip;
429     int vec[I_MAX + 1];
430     struct emp_qelem *qp;
431     struct sctstr sect;
432     int dam = 0;
433     int nukedam;
434
435     getvec(VT_ITEM, vec, (s_char *)target, EF_SECTOR);
436     for (i = 0; i < nbomb; i++) {
437         if (vec[bombcomm[i]] == 0)
438             continue;
439         if (opt_SUPER_BARS && bombcomm[i] == I_BAR)
440             continue;
441         ip = &ichr[bombcomm[i]];
442         pr("some %s\n", ip->i_name);
443     }
444     for (;;) {
445         ip = whatitem((s_char *)0, "commodity to bomb? ");
446         if (player->aborted)
447             return;
448         if (!ip)
449             continue;
450
451         for (i = 0; i < nbomb; i++) {
452             if (opt_SUPER_BARS && bombcomm[i] == I_BAR)
453                 continue;
454             if (&ichr[bombcomm[i]] == ip)
455                 break;
456         }
457         if (i == nbomb) {
458             pr("You can't bomb %s!\n", ip->i_name);
459             for (i = 0; i < nbomb; i++) {
460                 if (opt_SUPER_BARS && bombcomm[i] == I_BAR)
461                     continue;
462                 pr(i == 0 ? "Bombable: " : ", ");
463                 pr(ichr[bombcomm[i]].i_name);
464             }
465             pr("\n");
466         } else
467             break;
468     }
469     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
470         plp = (struct plist *)qp;
471         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
472             continue;
473         if (plp->bombs || plp->plane.pln_nuketype != -1)
474             dam +=
475                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
476                            &nukedam, 1);
477     }
478     if (dam <= 0)               /* dam == 0 if only nukes were delivered */
479         return;
480     getsect(target->sct_x, target->sct_y, &sect);
481     target = &sect;
482     before = amt = getvar(ip->i_vtype, (s_char *)target, EF_SECTOR);
483     putvar(ip->i_vtype, commdamage(amt, dam, ip->i_vtype),
484            (s_char *)target, EF_SECTOR);
485     amt = getvar(ip->i_vtype, (s_char *)target, EF_SECTOR);
486     if (before > 0.0)
487         b = 100.0 * (1.0 - ((float)amt / (float)before));
488     else
489         b = 0.0;
490     pr("did %.2f%% damage to %s in %s\n",
491        b, ip->i_name, xyas(target->sct_x, target->sct_y, player->cnum));
492     nreport(player->cnum, N_SCT_BOMB, target->sct_own, 1);
493     if (target->sct_own != 0)
494         wu(0, target->sct_own,
495            "%s precision bombing raid did %.2f%% damage to %s in %s\n",
496            cname(player->cnum), b, ip->i_name,
497            xyas(target->sct_x, target->sct_y, target->sct_own));
498     putsect(&sect);
499     collateral_damage(target->sct_x, target->sct_y, dam, list);
500 }
501
502 static void
503 ship_bomb(struct emp_qelem *list, struct sctstr *target)
504 {
505     struct plist *plp;
506     int onsea;
507     struct mchrstr *mcp;
508     int dam;
509     s_char *q;
510     int n;
511     struct emp_qelem *qp;
512     int shipno;
513     int ignore;
514     struct shpstr ship;
515     int nships = 0;
516     struct shiplook head, *s, *s2;
517     s_char buf[1024];
518     s_char prompt[128];
519     s_char msg[128];
520     int hitchance;
521     int nukedam;
522     int flak;
523     int gun;
524     int shell;
525
526     memset(&head, 0, sizeof(struct shiplook));
527     head.uid = -1;
528     onsea = (target->sct_type == SCT_WATER) ? 1 : 0;
529     for (qp = list->q_forw; qp != list && !player->aborted;
530          qp = qp->q_forw) {
531         if (head.uid != -1) {
532             s = head.next;
533             while (s != (struct shiplook *)0) {
534                 s2 = s;
535                 s = s->next;
536                 free(s2);
537             }
538         }
539         memset(&head, 0, sizeof(struct shiplook));
540         head.uid = -1;
541         plp = (struct plist *)qp;
542         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
543             continue;
544         if (plp->pcp->pl_flags & P_A)
545             nships = asw_shipsatxy(target->sct_x, target->sct_y, 0, 0,
546                                    &plp->plane, &head);
547         else
548             nships = shipsatxy(target->sct_x, target->sct_y, 0, M_SUB);
549         if (nships == 0) {
550             pr("%s could not find any ships!\n", prplane(&plp->plane));
551             continue;
552         }
553         (void)sprintf(prompt, "%s, %d bombs.  Target ('~' to skip)? ",
554                       prplane(&plp->plane), plp->bombs);
555         ignore = 0;
556         shipno = -1;
557         while (shipno < 0 && !player->aborted && !ignore) {
558             if ((q = getstring(prompt, buf)) == 0 || *q == 0)
559                 continue;
560             if (*q == '~') {
561                 ignore = 1;
562                 continue;
563             }
564             if (*q == '?') {
565                 if (plp->pcp->pl_flags & P_A)
566                     print_found(&head);
567                 else
568                     shipsatxy(target->sct_x, target->sct_y, 0, M_SUB);
569                 continue;
570             }
571             if (*q == 'd')
572                 goto next;
573             if (!isdigit(*q))
574                 continue;
575             n = atoi(q);
576             if (n < 0)
577                 continue;
578             if (getship(n, &ship) && ship.shp_x == target->sct_x &&
579                 ship.shp_y == target->sct_y)
580                 shipno = n;
581         }
582         if (shipno < 0)
583             continue;
584
585         shell = gun = 0;
586         gun = min(getvar(V_GUN, (s_char *)&ship, EF_SHIP), ship.shp_glim);
587         if (gun > 0) {
588             shell = getvar(V_SHELL, (s_char *)&ship, EF_SHIP);
589             if (shell <= 0)
590                 shell = supply_commod(ship.shp_own, ship.shp_x,
591                                       ship.shp_y, I_SHELL, 1);
592         }
593         mcp = &mchr[(int)ship.shp_type];
594         if (gun > 0 && shell > 0 && !(mcp->m_flags & M_SUB)) {
595             flak = (int)(techfact(ship.shp_tech, (double)gun) * 2.0);
596             putvar(V_SHELL, shell, (s_char *)&ship, EF_SHIP);
597             putship(ship.shp_uid, &ship);
598             sprintf(msg, "Flak! Firing %d guns from ship %s\n",
599                     flak, prship(&ship));
600             PR(ship.shp_own, msg);
601             if (pinflak_planedamage
602                 (&plp->plane, plp->pcp, ship.shp_own, flak))
603                 continue;
604         }
605
606         dam = 0;
607         if (plp->plane.pln_nuketype != -1)
608             hitchance = 100;
609         else {
610             hitchance =
611                 pln_hitchance(&plp->plane, shp_hardtarget(&ship), EF_SHIP);
612             pr("%d%% hitchance...", hitchance);
613         }
614         if (roll(100) <= hitchance) {
615             /* pinbombing is more accurate than normal bombing */
616             dam =
617                 2 * pln_damage(&plp->plane, target->sct_x, target->sct_y,
618                                'p', &nukedam, 1);
619         } else {
620             pr("splash\n");
621             /* Bombs that miss have to land somewhere! */
622             dam =
623                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
624                            &nukedam, 0);
625             collateral_damage(target->sct_x, target->sct_y, dam, list);
626             dam = 0;
627         }
628         if (dam <= 0)           /* dam == 0 if only nukes were delivered */
629             continue;
630         if (mcp->m_flags & M_SUB)
631             nreport(player->cnum, N_SUB_BOMB, ship.shp_own, 1);
632         else
633             nreport(player->cnum, N_SHP_BOMB, ship.shp_own, 1);
634         if (ship.shp_own) {
635             wu(0, ship.shp_own, "%s bombs did %d damage to %s at %s\n",
636                cname(player->cnum), dam,
637                prship(&ship),
638                xyas(target->sct_x, target->sct_y, ship.shp_own));
639         }
640         pr("\n");
641         check_retreat_and_do_shipdamage(&ship, dam);
642         if (ship.shp_rflags & RET_BOMBED)
643             if (((ship.shp_rflags & RET_INJURED) == 0) || !dam)
644                 retreat_ship(&ship, 'b');
645         putship(ship.shp_uid, &ship);
646         getship(n, &ship);
647         if (!ship.shp_own) {
648             pr("%s at %s sunk!\n",
649                prship(&ship),
650                xyas(target->sct_x, target->sct_y, player->cnum));
651         }
652         collateral_damage(target->sct_x, target->sct_y, dam / 2, list);
653       next:
654         ;
655     }
656     s = head.next;
657     while (s != (struct shiplook *)0) {
658         s2 = s;
659         s = s->next;
660         free(s2);
661     }
662 }
663
664 static void
665 plane_bomb(struct emp_qelem *list, struct sctstr *target)
666 {
667     int dam;
668     s_char *q;
669     int n;
670     natid own;
671     struct plnstr plane;
672     struct emp_qelem *qp;
673     int planeno;
674     int ignore;
675     struct plist *plp;
676     s_char prompt[128];
677     s_char buf[1024];
678     int hitchance;
679     int nukedam;
680     int nplanes;
681
682     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
683         plp = (struct plist *)qp;
684         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
685             continue;
686         nplanes = planesatxy(target->sct_x, target->sct_y, 0, 0, list);
687         if (nplanes == 0) {
688             pr("%s could not find any planes!\n", prplane(&plp->plane));
689             continue;
690         }
691         (void)sprintf(prompt, "%s, %d bombs.  Target ('~' to skip)? ",
692                       prplane(&plp->plane), plp->bombs);
693         planeno = -1;
694         ignore = 0;
695         while (planeno < 0 && !player->aborted && !ignore) {
696             if ((q = getstring(prompt, buf)) == 0 || *q == 0)
697                 continue;
698             if (*q == '~') {
699                 ignore = 1;
700                 continue;
701             }
702             if (*q == '?') {
703                 planesatxy(target->sct_x, target->sct_y, 0, 0, list);
704                 continue;
705             }
706             if (*q == 'd')
707                 goto next;
708             n = atoi(q);
709             if (n < 0)
710                 continue;
711             if (getplane(n, &plane) &&
712                 plane.pln_x == target->sct_x &&
713                 plane.pln_y == target->sct_y &&
714                 ((plane.pln_flags & PLN_LAUNCHED) == 0) &&
715                 (!ac_isflying(&plane, list)))
716                 planeno = n;
717             else
718                 pr("Plane #%d not spotted\n", n);
719         }
720         if (planeno < 0)
721             continue;
722         dam = 0;
723         if (plp->plane.pln_nuketype != -1)
724             hitchance = 100;
725         else {
726             hitchance = pln_hitchance(&plp->plane, 0, EF_PLANE);
727             pr("%d%% hitchance...", hitchance);
728         }
729         if (roll(100) <= hitchance) {
730             /* pinbombing is more accurate than normal bombing */
731             dam =
732                 2 * pln_damage(&plp->plane, target->sct_x, target->sct_y,
733                                'p', &nukedam, 1);
734         } else {
735             pr("thud\n");
736             /* Bombs that miss have to land somewhere! */
737             dam =
738                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
739                            &nukedam, 0);
740             collateral_damage(target->sct_x, target->sct_y, dam, list);
741             dam = 0;
742         }
743         if (dam <= 0)           /* dam == 0 if only nukes were delivered */
744             continue;
745         if (dam > 100)
746             dam = 100;
747         own = plane.pln_own;
748         if (dam > plane.pln_effic) {
749             plane.pln_effic = 0;
750             makelost(EF_PLANE, plane.pln_own, plane.pln_uid, plane.pln_x,
751                      plane.pln_y);
752             plane.pln_own = 0;
753         } else
754             plane.pln_effic -= dam;
755         plane.pln_mobil = (dam * plane.pln_mobil / 100.0);
756         if (own == player->cnum) {
757             pr("%s reports %d%% damage\n", prplane(&plane), dam);
758         } else {
759             if (own != 0)
760                 wu(0, own,
761                    "%s pinpoint bombing raid did %d%% damage to %s\n",
762                    cname(player->cnum), dam, prplane(&plane));
763         }
764         nreport(player->cnum, N_DOWN_PLANE, own, 1);
765         if (own != 0)
766             wu(0, own, "%s bombs did %d%% damage to %s at %s\n",
767                cname(player->cnum), dam, prplane(&plane),
768                xyas(target->sct_x, target->sct_y, own));
769         putplane(plane.pln_uid, &plane);
770         collateral_damage(plane.pln_x, plane.pln_y, dam, list);
771       next:
772         ;
773     }
774 }
775
776 static void
777 land_bomb(struct emp_qelem *list, struct sctstr *target)
778 {
779     int dam;
780     s_char *q;
781     int n;
782     natid own;
783     s_char prompt[128];
784     s_char buf[1024];
785     s_char msg[128];
786     struct lndstr land;
787     struct lchrstr *lcp;
788     struct emp_qelem *qp;
789     int unitno;
790     int ignore, flak, hitchance;
791     struct plist *plp;
792     int nukedam;
793     int nunits;
794
795     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
796         plp = (struct plist *)qp;
797         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
798             continue;
799         nunits = unitsatxy(target->sct_x, target->sct_y, 0, 0);
800         if (nunits == 0) {
801             pr("%s could not find any units!\n", prplane(&plp->plane));
802             continue;
803         }
804         (void)sprintf(prompt, "%s, %d bombs.  Target ('~' to skip)? ",
805                       prplane(&plp->plane), plp->bombs);
806         unitno = -1;
807         ignore = 0;
808         while (unitno < 0 && !player->aborted && !ignore) {
809             if ((q = getstring(prompt, buf)) == 0 || *q == 0)
810                 continue;
811             if (*q == '~') {
812                 ignore = 1;
813                 continue;
814             }
815             if (*q == '?') {
816                 unitsatxy(target->sct_x, target->sct_y, 0, 0);
817                 continue;
818             }
819             if (*q == 'd')
820                 goto next;
821             n = atoi(q);
822             if (n < 0)
823                 continue;
824             if (getland(n, &land) &&
825                 land.lnd_x == target->sct_x && land.lnd_y == target->sct_y)
826                 unitno = n;
827             else
828                 pr("Unit #%d not spotted\n", n);
829         }
830         if (unitno < 0)
831             continue;
832
833         lcp = &lchr[(int)land.lnd_type];
834
835         flak = (int)(techfact(land.lnd_tech, (double)land.lnd_aaf) * 3.0);
836         if (flak) {
837             sprintf(msg,
838                     "Flak! Firing flak guns from unit %s (aa rating %d)\n",
839                     prland(&land), land.lnd_aaf);
840             PR(land.lnd_own, msg);
841             if (pinflak_planedamage
842                 (&plp->plane, plp->pcp, land.lnd_own, flak))
843                 continue;
844         }
845
846         dam = 0;
847         if (plp->plane.pln_nuketype != -1)
848             hitchance = 100;
849         else {
850             hitchance =
851                 pln_hitchance(&plp->plane, lnd_hardtarget(&land), EF_LAND);
852             pr("%d%% hitchance...", hitchance);
853         }
854         if (roll(100) <= hitchance) {
855             dam =
856                 2 * pln_damage(&plp->plane, target->sct_x, target->sct_y,
857                                'p', &nukedam, 1);
858         } else {
859             pr("thud\n");
860             /* Bombs that miss have to land somewhere! */
861             dam =
862                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
863                            &nukedam, 0);
864             collateral_damage(target->sct_x, target->sct_y, dam, list);
865             dam = 0;
866         }
867         if (dam <= 0)           /* dam == 0 if only nukes were delivered */
868             continue;
869         if (dam > 100)
870             dam = 100;
871         own = land.lnd_own;
872         if (own != 0)
873             mpr(own,
874                 "%s pinpoint bombing raid did %d damage to %s\n",
875                 cname(player->cnum), dam, prland(&land));
876         check_retreat_and_do_landdamage(&land, dam);
877
878         if (land.lnd_rflags & RET_BOMBED)
879             if (((land.lnd_rflags & RET_INJURED) == 0) || !dam)
880                 retreat_land(&land, 'b');
881         nreport(player->cnum, N_UNIT_BOMB, own, 1);
882         putland(land.lnd_uid, &land);
883         collateral_damage(land.lnd_x, land.lnd_y, dam, list);
884       next:
885         ;
886     }
887 }
888
889 static void
890 strat_bomb(struct emp_qelem *list, struct sctstr *target)
891 {
892     register struct plist *plp;
893     int dam = 0;
894     struct emp_qelem *qp;
895     struct sctstr sect;
896     int nukedam;
897
898     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
899         plp = (struct plist *)qp;
900         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
901             continue;
902         if (plp->bombs || plp->plane.pln_nuketype != -1)
903             dam +=
904                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 's',
905                            &nukedam, 1);
906     }
907     if (dam <= 0)               /* dam == 0 if only nukes were delivered */
908         return;
909     getsect(target->sct_x, target->sct_y, &sect);
910     target = &sect;
911     if (target->sct_own)
912         wu(0, target->sct_own, "%s bombing raid did %d damage in %s\n",
913            cname(player->cnum), PERCENT_DAMAGE(dam),
914            xyas(target->sct_x, target->sct_y, target->sct_own));
915
916     sectdamage(target, dam, list);
917
918     pr("did %d damage in %s\n", PERCENT_DAMAGE(dam),
919        xyas(target->sct_x, target->sct_y, player->cnum));
920     putsect(&sect);
921 }
922
923 #define FLAK_MAX 15
924 float lflaktable[16] = { 0.20, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50,
925     0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85,
926 };
927
928 static int
929 pinflak_planedamage(struct plnstr *pp, struct plchrstr *pcp, natid from,
930                     int flak)
931 {
932     extern double flakscale;
933     int disp;
934     s_char dmess[255];
935     int eff;
936     s_char mesg[128];
937     struct shpstr ship;
938     struct lndstr land;
939     natid plane_owner;
940     int dam;
941     float mult;
942
943     flak -= (pp->pln_def + 1);
944     if (pcp->pl_flags & P_X)
945         flak -= 2;
946     if (pcp->pl_flags & P_H)
947         flak -= 1;
948     if (flak > 8)
949         mult = lflaktable[FLAK_MAX] * 1.33;
950     else if (flak < -7)
951         mult = lflaktable[0] * 0.66;
952     else {
953         flak += 7;
954         mult = lflaktable[flak];
955     }
956     mult *= flakscale;
957     dam = (int)((roll(8) + 2) * mult);
958     if (dam > 100)
959         dam = 100;
960
961     disp = 0;
962     plane_owner = pp->pln_own;
963     eff = pp->pln_effic;
964     if (dam <= 0)
965         return 0;
966     memset(dmess, 0, sizeof(dmess));
967     eff -= dam;
968     if (eff < 0)
969         eff = 0;
970     if (eff < PLANE_MINEFF) {
971         sprintf(dmess, " -- shot down");
972         disp = 1;
973     } else if (chance((100 - eff) / 100.0)) {
974         sprintf(dmess, " -- aborted with %d%%%% damage", 100 - eff);
975         disp = 2;
976     }
977     sprintf(mesg, "    Flak! %s %s takes %d%s.\n",
978             cname(pp->pln_own), prplane(pp), dam, dmess);
979     PR(plane_owner, mesg);
980
981     pp->pln_effic = eff;
982     if (disp == 1) {
983         if (from != 0)
984             nreport(from, N_DOWN_PLANE, pp->pln_own, 1);
985         if (pp->pln_ship >= 0) {
986             getship(pp->pln_ship, &ship);
987             take_plane_off_ship(pp, &ship);
988         }
989         if (pp->pln_land >= 0) {
990             getland(pp->pln_land, &land);
991             take_plane_off_land(pp, &land);
992         }
993         makelost(EF_PLANE, pp->pln_own, pp->pln_uid, pp->pln_x, pp->pln_y);
994         pp->pln_own = 0;
995         putplane(pp->pln_uid, pp);
996     } else
997         putplane(pp->pln_uid, pp);
998
999     if (disp > 0)
1000         return 1;
1001     return 0;
1002 }