]> git.pond.sub.org Git - empserver/blob - src/lib/commands/bomb.c
(SCT_BLD_WORK, SHP_BLD_WORK, PLN_BLD_WORK, LND_BLD_WORK,
[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(&ni_esc, EF_PLANE,
114                   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     struct dchrstr *dcp;
218     int nplanes;
219     int nships;
220     int type;
221     int bad;
222     s_char *p;
223     struct plist *plp;
224     struct emp_qelem *qp;
225     int bestacc;
226     int nsubs;
227     int nunits;
228     struct natstr *natp;
229     int rel;
230     s_char buf[1024];
231     int i;
232
233     bad = 0;
234     type = target->sct_type;
235     dcp = &dchr[type];
236     pr("Target sector is a %s constructed %s\n",
237        effadv((int)target->sct_effic), dcp->d_name);
238     nsubs = 0;
239     plp = (struct plist *)list->q_forw;
240     if (plp->pcp->pl_flags & P_A) {
241         bestacc = 0;
242         for (qp = list->q_forw; qp != list; qp = qp->q_forw)
243             plp = (struct plist *)qp;
244         if (plp->plane.pln_acc < bestacc)
245             bestacc = plp->plane.pln_acc;
246         nships = num_shipsatxy(target->sct_x, target->sct_y, 0, 0);
247         nsubs = nships - shipsatxy(target->sct_x, target->sct_y, 0, M_SUB);
248         if (nsubs > 0)
249             pr("Some subs are present in the sector.\n");
250     } else {
251         nships = shipsatxy(target->sct_x, target->sct_y, 0, M_SUB);
252     }
253     nplanes = planesatxy(target->sct_x, target->sct_y, 0, 0, list);
254     nunits = unitsatxy(target->sct_x, target->sct_y, 0, 0);
255   retry:
256     p = getstring("Bomb what? (ship, plane, land unit, efficiency, commodities) ",
257                   buf);
258     if (p == 0 || *p == 0) {
259         if (player->aborted)
260             return;
261         bad++;
262         if (bad > 2)
263             return;
264         goto retry;
265     }
266     switch (*p) {
267     case 'l':
268         if (opt_SLOW_WAR) {
269             natp = getnatp(player->cnum);
270             if (target->sct_own) {
271                 rel = getrel(natp, target->sct_own);
272                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
273                     && (target->sct_own) &&
274                     (target->sct_oldown != player->cnum)) {
275                     pr("You're not at war with them!\n");
276                     goto retry;
277                 }
278             }
279         }
280         if (nunits == 0) {
281             pr("no units there\n");
282             goto retry;
283         }
284         land_bomb(list, target);
285         break;
286     case 'p':
287         if (opt_SLOW_WAR) {
288             natp = getnatp(player->cnum);
289             if (target->sct_own) {
290                 rel = getrel(natp, target->sct_own);
291                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
292                     && (target->sct_own) &&
293                     (target->sct_oldown != player->cnum)) {
294                     pr("You're not at war with them!\n");
295                     goto retry;
296                 }
297             }
298         }
299         if (nplanes == 0) {
300             pr("no planes there\n");
301             goto retry;
302         }
303         plane_bomb(list, target);
304         break;
305     case 's':
306         if (nships == 0) {
307             if (((struct plist *)list->q_forw)->pcp->pl_flags & P_A) {
308                 if (nsubs == 0) {
309                     pr("no ships there\n");
310                     goto retry;
311                 }
312             } else {
313                 pr("no ships there\n");
314                 goto retry;
315             }
316         }
317         ship_bomb(list, target);
318         break;
319     case 'c':
320         if (opt_SLOW_WAR) {
321             natp = getnatp(player->cnum);
322             if (target->sct_own) {
323                 rel = getrel(natp, target->sct_own);
324                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
325                     && (target->sct_own) &&
326                     (target->sct_oldown != player->cnum)) {
327                     pr("You're not at war with them!\n");
328                     goto retry;
329                 }
330             }
331         }
332
333         for (i = 0; i < nbomb; i++) {
334             if (!target->sct_item[bombcomm[i]])
335                 continue;
336             break;
337         }
338         if (i >= nbomb) {
339             pr("No bombable commodities in %s\n",
340                xyas(target->sct_x, target->sct_y, player->cnum));
341             goto retry;
342         }
343         comm_bomb(list, target);
344         break;
345     case 'e':
346         if (opt_SLOW_WAR) {
347             natp = getnatp(player->cnum);
348             if (target->sct_own) {
349                 rel = getrel(natp, target->sct_own);
350                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
351                     && (target->sct_own) &&
352                     (target->sct_oldown != player->cnum)) {
353                     pr("You're not at war with them!\n");
354                     goto retry;
355                 }
356             }
357         }
358         eff_bomb(list, target);
359         break;
360     case 'q':
361         pr("Aborting mission.\n");
362         return;
363     default:
364         pr("Bad target type.\n");
365         goto retry;
366     }
367 }
368
369 static void
370 eff_bomb(struct emp_qelem *list, struct sctstr *target)
371 {
372     register struct plist *plp;
373     struct emp_qelem *qp;
374     struct sctstr sect;
375     int oldeff, dam = 0;
376     int nukedam;
377
378     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
379         plp = (struct plist *)qp;
380         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
381             continue;
382         if (plp->bombs || plp->plane.pln_nuketype != -1)
383             dam +=
384                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
385                            &nukedam, 1);
386     }
387     if (dam <= 0)               /* dam == 0 if only nukes were delivered */
388         return;
389     getsect(target->sct_x, target->sct_y, &sect);
390     target = &sect;
391     oldeff = target->sct_effic;
392     target->sct_effic = effdamage(target->sct_effic, dam);
393     target->sct_avail = effdamage(target->sct_avail, dam);
394     target->sct_road = effdamage(target->sct_road, dam);
395     target->sct_rail = effdamage(target->sct_rail, dam);
396     target->sct_defense = effdamage(target->sct_defense, dam);
397     if (!opt_DEFENSE_INFRA)
398         target->sct_defense = target->sct_effic;
399     pr("did %d%% damage to efficiency in %s\n",
400        oldeff - target->sct_effic,
401        xyas(target->sct_x, target->sct_y, player->cnum));
402     if (target->sct_own)
403         wu(0, target->sct_own,
404            "%s bombing raid did %d%% damage in %s\n",
405            cname(player->cnum), oldeff - target->sct_effic,
406            xyas(target->sct_x, target->sct_y, target->sct_own));
407     if (target->sct_effic < SCT_MINEFF) {
408         if (target->sct_type == SCT_BSPAN)
409             knockdown(target, list);
410         else if (target->sct_type == SCT_BTOWER) {
411             knockdown(target, list);
412             bridgefall(target, list);
413         }
414     }
415     putsect(&sect);
416     collateral_damage(target->sct_x, target->sct_y, dam, list);
417 }
418
419 static void
420 comm_bomb(struct emp_qelem *list, struct sctstr *target)
421 {
422     register struct plist *plp;
423     float b;
424     int i;
425     int amt, before;
426     struct ichrstr *ip;
427     struct emp_qelem *qp;
428     struct sctstr sect;
429     int dam = 0;
430     int nukedam;
431
432     for (i = 0; i < nbomb; i++) {
433         if (target->sct_item[bombcomm[i]] == 0)
434             continue;
435         if (opt_SUPER_BARS && bombcomm[i] == I_BAR)
436             continue;
437         ip = &ichr[bombcomm[i]];
438         pr("some %s\n", ip->i_name);
439     }
440     for (;;) {
441         ip = whatitem((s_char *)0, "commodity to bomb? ");
442         if (player->aborted)
443             return;
444         if (!ip)
445             continue;
446
447         for (i = 0; i < nbomb; i++) {
448             if (opt_SUPER_BARS && bombcomm[i] == I_BAR)
449                 continue;
450             if (&ichr[bombcomm[i]] == ip)
451                 break;
452         }
453         if (i == nbomb) {
454             pr("You can't bomb %s!\n", ip->i_name);
455             for (i = 0; i < nbomb; i++) {
456                 if (opt_SUPER_BARS && bombcomm[i] == I_BAR)
457                     continue;
458                 pr(i == 0 ? "Bombable: " : ", ");
459                 pr(ichr[bombcomm[i]].i_name);
460             }
461             pr("\n");
462         } else
463             break;
464     }
465     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
466         plp = (struct plist *)qp;
467         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
468             continue;
469         if (plp->bombs || plp->plane.pln_nuketype != -1)
470             dam +=
471                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
472                            &nukedam, 1);
473     }
474     if (dam <= 0)               /* dam == 0 if only nukes were delivered */
475         return;
476     getsect(target->sct_x, target->sct_y, &sect);
477     target = &sect;
478     before = amt = target->sct_item[ip->i_vtype];
479     target->sct_item[ip->i_vtype] = amt = commdamage(amt, dam, ip->i_vtype);
480     if (before > 0.0)
481         b = 100.0 * (1.0 - ((float)amt / (float)before));
482     else
483         b = 0.0;
484     pr("did %.2f%% damage to %s in %s\n",
485        b, ip->i_name, xyas(target->sct_x, target->sct_y, player->cnum));
486     nreport(player->cnum, N_SCT_BOMB, target->sct_own, 1);
487     if (target->sct_own != 0)
488         wu(0, target->sct_own,
489            "%s precision bombing raid did %.2f%% damage to %s in %s\n",
490            cname(player->cnum), b, ip->i_name,
491            xyas(target->sct_x, target->sct_y, target->sct_own));
492     putsect(&sect);
493     collateral_damage(target->sct_x, target->sct_y, dam, list);
494 }
495
496 static void
497 ship_bomb(struct emp_qelem *list, struct sctstr *target)
498 {
499     struct plist *plp;
500     int onsea;
501     struct mchrstr *mcp;
502     int dam;
503     s_char *q;
504     int n;
505     struct emp_qelem *qp;
506     int shipno;
507     int ignore;
508     struct shpstr ship;
509     int nships = 0;
510     struct shiplook head, *s, *s2;
511     s_char buf[1024];
512     s_char prompt[128];
513     s_char msg[128];
514     int hitchance;
515     int nukedam;
516     int flak;
517     int gun;
518     int shell;
519
520     memset(&head, 0, sizeof(struct shiplook));
521     head.uid = -1;
522     onsea = (target->sct_type == SCT_WATER) ? 1 : 0;
523     for (qp = list->q_forw; qp != list && !player->aborted;
524          qp = qp->q_forw) {
525         if (head.uid != -1) {
526             s = head.next;
527             while (s != (struct shiplook *)0) {
528                 s2 = s;
529                 s = s->next;
530                 free(s2);
531             }
532         }
533         memset(&head, 0, sizeof(struct shiplook));
534         head.uid = -1;
535         plp = (struct plist *)qp;
536         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
537             continue;
538         if (plp->pcp->pl_flags & P_A)
539             nships = asw_shipsatxy(target->sct_x, target->sct_y, 0, 0,
540                                    &plp->plane, &head);
541         else
542             nships = shipsatxy(target->sct_x, target->sct_y, 0, M_SUB);
543         if (nships == 0) {
544             pr("%s could not find any ships!\n", prplane(&plp->plane));
545             continue;
546         }
547         (void)sprintf(prompt, "%s, %d bombs.  Target ('~' to skip)? ",
548                       prplane(&plp->plane), plp->bombs);
549         ignore = 0;
550         shipno = -1;
551         while (shipno < 0 && !player->aborted && !ignore) {
552             if ((q = getstring(prompt, buf)) == 0 || *q == 0)
553                 continue;
554             if (*q == '~') {
555                 ignore = 1;
556                 continue;
557             }
558             if (*q == '?') {
559                 if (plp->pcp->pl_flags & P_A)
560                     print_found(&head);
561                 else
562                     shipsatxy(target->sct_x, target->sct_y, 0, M_SUB);
563                 continue;
564             }
565             if (*q == 'd')
566                 goto next;
567             if (!isdigit(*q))
568                 continue;
569             n = atoi(q);
570             if (n < 0)
571                 continue;
572             if (getship(n, &ship) && ship.shp_x == target->sct_x &&
573                 ship.shp_y == target->sct_y)
574                 shipno = n;
575         }
576         if (shipno < 0)
577             continue;
578
579         shell = gun = 0;
580         gun = min(ship.shp_item[I_GUN], ship.shp_glim);
581         if (gun > 0) {
582             shell = ship.shp_item[I_SHELL];
583             if (shell <= 0)
584                 shell = supply_commod(ship.shp_own, ship.shp_x,
585                                       ship.shp_y, I_SHELL, 1);
586         }
587         mcp = &mchr[(int)ship.shp_type];
588         if (gun > 0 && shell > 0 && !(mcp->m_flags & M_SUB)) {
589             flak = (int)(techfact(ship.shp_tech, (double)gun) * 2.0);
590             ship.shp_item[I_SHELL] = shell;
591             putship(ship.shp_uid, &ship);
592             sprintf(msg, "Flak! Firing %d guns from ship %s\n",
593                     flak, prship(&ship));
594             PR(ship.shp_own, msg);
595             if (pinflak_planedamage(&plp->plane, plp->pcp, ship.shp_own, flak))
596                 continue;
597         }
598
599         dam = 0;
600         if (plp->plane.pln_nuketype != -1)
601             hitchance = 100;
602         else {
603             hitchance =
604                 pln_hitchance(&plp->plane, shp_hardtarget(&ship), EF_SHIP);
605             pr("%d%% hitchance...", hitchance);
606         }
607         if (roll(100) <= hitchance) {
608             /* pinbombing is more accurate than normal bombing */
609             dam =
610                 2 * pln_damage(&plp->plane, target->sct_x, target->sct_y,
611                                'p', &nukedam, 1);
612         } else {
613             pr("splash\n");
614             /* Bombs that miss have to land somewhere! */
615             dam =
616                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
617                            &nukedam, 0);
618             collateral_damage(target->sct_x, target->sct_y, dam, list);
619             dam = 0;
620         }
621         if (dam <= 0)           /* dam == 0 if only nukes were delivered */
622             continue;
623         if (mcp->m_flags & M_SUB)
624             nreport(player->cnum, N_SUB_BOMB, ship.shp_own, 1);
625         else
626             nreport(player->cnum, N_SHP_BOMB, ship.shp_own, 1);
627         if (ship.shp_own) {
628             wu(0, ship.shp_own, "%s bombs did %d damage to %s at %s\n",
629                cname(player->cnum), dam,
630                prship(&ship),
631                xyas(target->sct_x, target->sct_y, ship.shp_own));
632         }
633         pr("\n");
634         check_retreat_and_do_shipdamage(&ship, dam);
635         if (ship.shp_rflags & RET_BOMBED)
636             if (((ship.shp_rflags & RET_INJURED) == 0) || !dam)
637                 retreat_ship(&ship, 'b');
638         putship(ship.shp_uid, &ship);
639         getship(n, &ship);
640         if (!ship.shp_own) {
641             pr("%s at %s sunk!\n",
642                prship(&ship),
643                xyas(target->sct_x, target->sct_y, player->cnum));
644         }
645         collateral_damage(target->sct_x, target->sct_y, dam / 2, list);
646       next:
647         ;
648     }
649     s = head.next;
650     while (s != (struct shiplook *)0) {
651         s2 = s;
652         s = s->next;
653         free(s2);
654     }
655 }
656
657 static void
658 plane_bomb(struct emp_qelem *list, struct sctstr *target)
659 {
660     int dam;
661     s_char *q;
662     int n;
663     natid own;
664     struct plnstr plane;
665     struct emp_qelem *qp;
666     int planeno;
667     int ignore;
668     struct plist *plp;
669     s_char prompt[128];
670     s_char buf[1024];
671     int hitchance;
672     int nukedam;
673     int nplanes;
674
675     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
676         plp = (struct plist *)qp;
677         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
678             continue;
679         nplanes = planesatxy(target->sct_x, target->sct_y, 0, 0, list);
680         if (nplanes == 0) {
681             pr("%s could not find any planes!\n", prplane(&plp->plane));
682             continue;
683         }
684         (void)sprintf(prompt, "%s, %d bombs.  Target ('~' to skip)? ",
685                       prplane(&plp->plane), plp->bombs);
686         planeno = -1;
687         ignore = 0;
688         while (planeno < 0 && !player->aborted && !ignore) {
689             if ((q = getstring(prompt, buf)) == 0 || *q == 0)
690                 continue;
691             if (*q == '~') {
692                 ignore = 1;
693                 continue;
694             }
695             if (*q == '?') {
696                 planesatxy(target->sct_x, target->sct_y, 0, 0, list);
697                 continue;
698             }
699             if (*q == 'd')
700                 goto next;
701             n = atoi(q);
702             if (n < 0)
703                 continue;
704             if (getplane(n, &plane) &&
705                 plane.pln_x == target->sct_x &&
706                 plane.pln_y == target->sct_y &&
707                 ((plane.pln_flags & PLN_LAUNCHED) == 0) &&
708                 (!ac_isflying(&plane, list)))
709                 planeno = n;
710             else
711                 pr("Plane #%d not spotted\n", n);
712         }
713         if (planeno < 0)
714             continue;
715         dam = 0;
716         if (plp->plane.pln_nuketype != -1)
717             hitchance = 100;
718         else {
719             hitchance = pln_hitchance(&plp->plane, 0, EF_PLANE);
720             pr("%d%% hitchance...", hitchance);
721         }
722         if (roll(100) <= hitchance) {
723             /* pinbombing is more accurate than normal bombing */
724             dam =
725                 2 * pln_damage(&plp->plane, target->sct_x, target->sct_y,
726                                'p', &nukedam, 1);
727         } else {
728             pr("thud\n");
729             /* Bombs that miss have to land somewhere! */
730             dam =
731                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
732                            &nukedam, 0);
733             collateral_damage(target->sct_x, target->sct_y, dam, list);
734             dam = 0;
735         }
736         if (dam <= 0)           /* dam == 0 if only nukes were delivered */
737             continue;
738         if (dam > 100)
739             dam = 100;
740         own = plane.pln_own;
741         if (dam > plane.pln_effic) {
742             plane.pln_effic = 0;
743             makelost(EF_PLANE, plane.pln_own, plane.pln_uid, plane.pln_x,
744                      plane.pln_y);
745             plane.pln_own = 0;
746         } else
747             plane.pln_effic -= dam;
748         plane.pln_mobil = (dam * plane.pln_mobil / 100.0);
749         if (own == player->cnum) {
750             pr("%s reports %d%% damage\n", prplane(&plane), dam);
751         } else {
752             if (own != 0)
753                 wu(0, own,
754                    "%s pinpoint bombing raid did %d%% damage to %s\n",
755                    cname(player->cnum), dam, prplane(&plane));
756         }
757         nreport(player->cnum, N_DOWN_PLANE, own, 1);
758         if (own != 0)
759             wu(0, own, "%s bombs did %d%% damage to %s at %s\n",
760                cname(player->cnum), dam, prplane(&plane),
761                xyas(target->sct_x, target->sct_y, own));
762         putplane(plane.pln_uid, &plane);
763         collateral_damage(plane.pln_x, plane.pln_y, dam, list);
764       next:
765         ;
766     }
767 }
768
769 static void
770 land_bomb(struct emp_qelem *list, struct sctstr *target)
771 {
772     int dam;
773     s_char *q;
774     int n;
775     natid own;
776     s_char prompt[128];
777     s_char buf[1024];
778     s_char msg[128];
779     struct lndstr land;
780     struct lchrstr *lcp;
781     struct emp_qelem *qp;
782     int unitno;
783     int ignore, flak, hitchance;
784     struct plist *plp;
785     int nukedam;
786     int nunits;
787
788     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
789         plp = (struct plist *)qp;
790         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
791             continue;
792         nunits = unitsatxy(target->sct_x, target->sct_y, 0, 0);
793         if (nunits == 0) {
794             pr("%s could not find any units!\n", prplane(&plp->plane));
795             continue;
796         }
797         (void)sprintf(prompt, "%s, %d bombs.  Target ('~' to skip)? ",
798                       prplane(&plp->plane), plp->bombs);
799         unitno = -1;
800         ignore = 0;
801         while (unitno < 0 && !player->aborted && !ignore) {
802             if ((q = getstring(prompt, buf)) == 0 || *q == 0)
803                 continue;
804             if (*q == '~') {
805                 ignore = 1;
806                 continue;
807             }
808             if (*q == '?') {
809                 unitsatxy(target->sct_x, target->sct_y, 0, 0);
810                 continue;
811             }
812             if (*q == 'd')
813                 goto next;
814             n = atoi(q);
815             if (n < 0)
816                 continue;
817             if (getland(n, &land) &&
818                 land.lnd_x == target->sct_x && land.lnd_y == target->sct_y)
819                 unitno = n;
820             else
821                 pr("Unit #%d not spotted\n", n);
822         }
823         if (unitno < 0)
824             continue;
825
826         lcp = &lchr[(int)land.lnd_type];
827
828         flak = (int)(techfact(land.lnd_tech, (double)land.lnd_aaf) * 3.0);
829         if (flak) {
830             sprintf(msg,
831                     "Flak! Firing flak guns from unit %s (aa rating %d)\n",
832                     prland(&land), land.lnd_aaf);
833             PR(land.lnd_own, msg);
834             if (pinflak_planedamage(&plp->plane, plp->pcp, land.lnd_own, flak))
835                 continue;
836         }
837
838         dam = 0;
839         if (plp->plane.pln_nuketype != -1)
840             hitchance = 100;
841         else {
842             hitchance =
843                 pln_hitchance(&plp->plane, lnd_hardtarget(&land), EF_LAND);
844             pr("%d%% hitchance...", hitchance);
845         }
846         if (roll(100) <= hitchance) {
847             dam =
848                 2 * pln_damage(&plp->plane, target->sct_x, target->sct_y,
849                                'p', &nukedam, 1);
850         } else {
851             pr("thud\n");
852             /* Bombs that miss have to land somewhere! */
853             dam =
854                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 'p',
855                            &nukedam, 0);
856             collateral_damage(target->sct_x, target->sct_y, dam, list);
857             dam = 0;
858         }
859         if (dam <= 0)           /* dam == 0 if only nukes were delivered */
860             continue;
861         if (dam > 100)
862             dam = 100;
863         own = land.lnd_own;
864         if (own != 0)
865             mpr(own,
866                 "%s pinpoint bombing raid did %d damage to %s\n",
867                 cname(player->cnum), dam, prland(&land));
868         check_retreat_and_do_landdamage(&land, dam);
869
870         if (land.lnd_rflags & RET_BOMBED)
871             if (((land.lnd_rflags & RET_INJURED) == 0) || !dam)
872                 retreat_land(&land, 'b');
873         nreport(player->cnum, N_UNIT_BOMB, own, 1);
874         putland(land.lnd_uid, &land);
875         collateral_damage(land.lnd_x, land.lnd_y, dam, list);
876       next:
877         ;
878     }
879 }
880
881 static void
882 strat_bomb(struct emp_qelem *list, struct sctstr *target)
883 {
884     register struct plist *plp;
885     int dam = 0;
886     struct emp_qelem *qp;
887     struct sctstr sect;
888     int nukedam;
889
890     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
891         plp = (struct plist *)qp;
892         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
893             continue;
894         if (plp->bombs || plp->plane.pln_nuketype != -1)
895             dam +=
896                 pln_damage(&plp->plane, target->sct_x, target->sct_y, 's',
897                            &nukedam, 1);
898     }
899     if (dam <= 0)               /* dam == 0 if only nukes were delivered */
900         return;
901     getsect(target->sct_x, target->sct_y, &sect);
902     target = &sect;
903     if (target->sct_own)
904         wu(0, target->sct_own, "%s bombing raid did %d damage in %s\n",
905            cname(player->cnum), PERCENT_DAMAGE(dam),
906            xyas(target->sct_x, target->sct_y, target->sct_own));
907
908     sectdamage(target, dam, list);
909
910     pr("did %d damage in %s\n", PERCENT_DAMAGE(dam),
911        xyas(target->sct_x, target->sct_y, player->cnum));
912     putsect(&sect);
913 }
914
915 #define FLAK_MAX 15
916 float lflaktable[16] = { 0.20, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50,
917     0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85,
918 };
919
920 static int
921 pinflak_planedamage(struct plnstr *pp, struct plchrstr *pcp, natid from,
922                     int flak)
923 {
924     int disp;
925     s_char dmess[255];
926     int eff;
927     s_char mesg[128];
928     struct shpstr ship;
929     struct lndstr land;
930     natid plane_owner;
931     int dam;
932     float mult;
933
934     flak -= (pp->pln_def + 1);
935     if (pcp->pl_flags & P_X)
936         flak -= 2;
937     if (pcp->pl_flags & P_H)
938         flak -= 1;
939     if (flak > 8)
940         mult = lflaktable[FLAK_MAX] * 1.33;
941     else if (flak < -7)
942         mult = lflaktable[0] * 0.66;
943     else {
944         flak += 7;
945         mult = lflaktable[flak];
946     }
947     mult *= flakscale;
948     dam = (int)((roll(8) + 2) * mult);
949     if (dam > 100)
950         dam = 100;
951
952     disp = 0;
953     plane_owner = pp->pln_own;
954     eff = pp->pln_effic;
955     if (dam <= 0)
956         return 0;
957     memset(dmess, 0, sizeof(dmess));
958     eff -= dam;
959     if (eff < 0)
960         eff = 0;
961     if (eff < PLANE_MINEFF) {
962         sprintf(dmess, " -- shot down");
963         disp = 1;
964     } else if (chance((100 - eff) / 100.0)) {
965         sprintf(dmess, " -- aborted with %d%%%% damage", 100 - eff);
966         disp = 2;
967     }
968     sprintf(mesg, "    Flak! %s %s takes %d%s.\n",
969             cname(pp->pln_own), prplane(pp), dam, dmess);
970     PR(plane_owner, mesg);
971
972     pp->pln_effic = eff;
973     if (disp == 1) {
974         if (from != 0)
975             nreport(from, N_DOWN_PLANE, pp->pln_own, 1);
976         if (pp->pln_ship >= 0) {
977             getship(pp->pln_ship, &ship);
978             take_plane_off_ship(pp, &ship);
979         }
980         if (pp->pln_land >= 0) {
981             getland(pp->pln_land, &land);
982             take_plane_off_land(pp, &land);
983         }
984         makelost(EF_PLANE, pp->pln_own, pp->pln_uid, pp->pln_x, pp->pln_y);
985         pp->pln_own = 0;
986         putplane(pp->pln_uid, pp);
987     } else
988         putplane(pp->pln_uid, pp);
989
990     if (disp > 0)
991         return 1;
992     return 0;
993 }