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