]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
Permit ships that can drop depth charges, but not fire
[empserver] / src / lib / commands / mfir.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2013, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  multifire.c: Fire at other sectors/ships
28  *
29  *  Known contributors to this file:
30  *     Steve McClure, 2000
31  *     Markus Armbruster, 2004-2011
32  */
33
34 #include <config.h>
35
36 #include "chance.h"
37 #include "commands.h"
38 #include "empobj.h"
39 #include "news.h"
40 #include "optlist.h"
41 #include "retreat.h"
42
43 enum targ_type {
44     targ_land, targ_ship, targ_sub, targ_bogus
45 };
46
47 struct flist {
48     struct emp_qelem queue;     /* list of fired things */
49     short type;                 /* EF_SECTOR, EF_SHIP or EF_LAND */
50     int uid;
51     coord x, y;
52     int defdam;                 /* damage defenders did */
53     natid victim;
54 };
55
56 static int defend(struct emp_qelem *, struct emp_qelem *,
57                   struct empobj *, natid, int *);
58 static void do_defdam(struct emp_qelem *, double);
59 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
60                         coord, int *);
61 static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
62 static void free_flist(struct emp_qelem *);
63 static struct flist *search_flist(struct emp_qelem *, struct empobj *);
64
65 int
66 multifire(void)
67 {
68     static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
69     char *ptr;
70     double range;
71     int trange, range2;
72     coord fx;
73     coord fy;
74     coord x;
75     coord y;
76     int dam;
77     int totaldefdam = 0;
78     int vshipno;
79     natid vict;
80     struct shpstr fship;
81     struct lndstr fland;
82     struct sctstr fsect;
83     struct shpstr vship;
84     struct sctstr vsect;
85     enum targ_type target;
86     struct nstr_item nbst;
87     int type;
88     struct empobj *attgp;
89     char *p;
90     int nfiring = 0;
91     int ndefending = 0;
92     union empobj_storage item;
93     struct emp_qelem fired, defended;
94     double odds;
95     char buf[1024];
96
97     emp_initque(&fired);
98     emp_initque(&defended);
99     p = getstarg(player->argp[1],
100                  "Firing from ship(s), sect(s), or land unit(s)? ", buf);
101     if (!p)
102         return RET_SYN;
103     type = ef_byname_from(p, ef_with_guns);
104     if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
105         pr("Fort firing is disabled.\n");
106         return RET_FAIL;
107     }
108     if (type < 0) {
109         pr("Ships, land units or sectors only!\n");
110         return RET_SYN;
111     }
112     if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
113         return RET_SYN;
114
115     while (nxtitem(&nbst, &item)) {
116         if (type == EF_LAND) {
117             if (!getland(item.land.lnd_uid, &fland))
118                 continue;
119             if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
120                 continue;
121             if (item.land.lnd_own != player->cnum)
122                 continue;
123
124             if (lchr[fland.lnd_type].l_dam == 0) {
125                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
126                 continue;
127             }
128             if (fland.lnd_item[I_MILIT] < 1) {
129                 pr("Unit %d cannot fire because it has no military!\n",
130                    fland.lnd_uid);
131                 continue;
132             }
133             if (fland.lnd_ship >= 0) {
134                 pr("Unit %d cannot fire because it is on a ship!\n",
135                    fland.lnd_uid);
136                 continue;
137             }
138             if (fland.lnd_land >= 0) {
139                 pr("Unit %d cannot fire because it is on a land unit!\n",
140                    fland.lnd_uid);
141                 continue;
142             }
143             if (fland.lnd_effic < LAND_MINFIREEFF) {
144                 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
145                    fland.lnd_uid, LAND_MINFIREEFF);
146                 continue;
147             }
148             if (fland.lnd_item[I_SHELL] == 0) {
149                 pr("%s -- not enough shells\n", prland(&fland));
150                 continue;
151             }
152             fx = fland.lnd_x;
153             fy = fland.lnd_y;
154         } else if (type == EF_SHIP) {
155             if (!getship(item.ship.shp_uid, &fship))
156                 continue;
157             if (item.ship.shp_own != player->cnum)
158                 continue;
159             if (item.ship.shp_item[I_MILIT] < 1) {
160                 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
161                 continue;
162             }
163             if (mchr[item.ship.shp_type].m_glim == 0
164                 && !(mchr[fship.shp_type].m_flags & M_DCH)) {
165                 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
166                 continue;
167             }
168             if (item.ship.shp_item[I_GUN] == 0) {
169                 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
170                 continue;
171             }
172             if (item.ship.shp_item[I_SHELL] == 0) {
173                 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
174                 continue;
175             }
176             if (item.ship.shp_effic < 60) {
177                 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
178                 continue;
179             }
180             fx = fship.shp_x;
181             fy = fship.shp_y;
182         } else {
183             if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
184                 continue;
185             if (item.sect.sct_own != player->cnum)
186                 continue;
187             if (item.sect.sct_type != SCT_FORTR)
188                 continue;
189             if (item.sect.sct_effic < FORTEFF) {
190                 pr("Fort not efficient enough to fire!\n");
191                 continue;
192             }
193             if (item.sect.sct_item[I_GUN] == 0) {
194                 pr("Not enough guns in sector %s!\n",
195                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
196                 continue;
197             }
198             if (item.sect.sct_item[I_SHELL] == 0) {
199                 pr("Not enough shells in sector %s!\n",
200                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
201                 continue;
202             }
203             if (item.sect.sct_item[I_MILIT] < 5) {
204                 pr("Not enough military in sector %s!\n",
205                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
206                 continue;
207             }
208             pr("\nSector %s firing\n",
209                xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
210             fx = fsect.sct_x;
211             fy = fsect.sct_y;
212         }
213
214         ptr = getstarg(player->argp[3], "Firing at? ", buf);
215         if (!ptr)
216             return RET_FAIL;
217         if (!*ptr)
218             continue;
219         if (!issector(ptr)) {
220             vshipno = atoi(ptr);
221             if (vshipno < 0 || !getship(vshipno, &vship) ||
222                 (!vship.shp_own)) {
223                 pr("No such ship exists!\n");
224                 continue;
225             }
226             target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
227                 targ_sub : targ_ship;
228             vict = vship.shp_own;
229             x = vship.shp_x;
230             y = vship.shp_y;
231             if (!getsect(x, y, &vsect)) {
232                 pr("No such sector exists!\n");
233                 continue;
234             }
235         } else {
236             if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
237                 pr("No such sector exists!\n");
238                 continue;
239             }
240             /* We check the sector type, but we only use it for damage, not
241                reporting.  That way, you don't get extra information you wouldn't
242                normally get.  Besides, what if they want to slam water? :)  */
243             if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
244                 target = targ_bogus;
245             else
246                 target = targ_land;
247             vict = vsect.sct_own;
248             x = vsect.sct_x;
249             y = vsect.sct_y;
250         }
251
252         trange = mapdist(x, y, fx, fy);
253
254         if (type == EF_SHIP) {
255             if (!check_ship_ok(&fship))
256                 return RET_FAIL;
257             if (fship.shp_own != player->cnum) {
258                 pr("Not your ship!\n");
259                 continue;
260             }
261             if (target == targ_sub || target == targ_ship) {
262                 if (fship.shp_uid == vship.shp_uid) {
263                     pr("You can't fire upon yourself!\n");
264                     continue;
265                 }
266             }
267             if (fship.shp_item[I_MILIT] < 1) {
268                 pr("Not enough military for firing crew.\n");
269                 continue;
270             }
271             if (fship.shp_effic < 60) {
272                 pr("Ship #%d is crippled (%d%%)\n",
273                    fship.shp_uid, fship.shp_effic);
274                 continue;
275             }
276             range = shp_fire_range(&fship);
277             range2 = roundrange(range);
278             pr("range is %d.00 (%.2f)\n", range2, range);
279             if (target == targ_sub
280                 && trange <= range2
281                 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
282                 dam = shp_dchrg(&fship);
283             } else {
284                 if (target == targ_sub)
285                     /* Don't tell it's a sub */
286                     range2 = -1;
287                 if (fship.shp_item[I_GUN] == 0) {
288                     pr("Insufficient arms.\n");
289                     continue;
290                 }
291                 dam = shp_fire(&fship);
292             }
293             putship(fship.shp_uid, &fship);
294             if (dam <= 0) {
295                 pr("Klick!     ...\n");
296                 continue;
297             }
298             if (opt_NOMOBCOST == 0) {
299                 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
300                 putship(fship.shp_uid, &fship);
301             }
302         } else if (type == EF_LAND) {
303             if (!check_land_ok(&fland))
304                 return RET_FAIL;
305             if (fland.lnd_own != player->cnum) {
306                 pr("Not your unit!\n");
307                 continue;
308             }
309
310             if (target == targ_land) {
311                 if (fland.lnd_x == vsect.sct_x
312                     && fland.lnd_y == vsect.sct_y) {
313                     pr("You can't fire upon yourself!\n");
314                     continue;
315                 }
316             }
317
318             if (lchr[fland.lnd_type].l_dam == 0) {
319                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
320                 continue;
321             }
322             if (fland.lnd_item[I_GUN] == 0) {
323                 pr("%s -- not enough guns\n", prland(&fland));
324                 continue;
325             }
326
327             range = lnd_fire_range(&fland);
328             range2 = roundrange(range);
329             pr("range is %d.00 (%.2f)\n", range2, range);
330             if (target == targ_sub) {
331                 /* Don't tell it's a sub */
332                 range2 = -1;
333             }
334
335             dam = lnd_fire(&fland);
336             putland(fland.lnd_uid, &fland);
337             if (dam < 0) {
338                 pr("Klick!     ...\n");
339                 continue;
340             }
341             if (target == targ_ship) {
342                 if (chance(lnd_acc(&fland) / 100.0))
343                     dam = ldround(dam / 2.0, 1);
344             }
345         } else {
346             if (!check_sect_ok(&fsect))
347                 return RET_FAIL;
348             if (fsect.sct_own != player->cnum ||
349                 fsect.sct_type != SCT_FORTR) {
350                 pr("No fortress at %s\n",
351                    xyas(fsect.sct_x, fsect.sct_y, player->cnum));
352                 continue;
353             }
354             if (target == targ_land) {
355                 if (fsect.sct_x == vsect.sct_x
356                     && fsect.sct_y == vsect.sct_y) {
357                     pr("You can't fire upon yourself!\n");
358                     continue;
359                 }
360             }
361             if (fsect.sct_item[I_GUN] == 0) {
362                 pr("Insufficient arms.\n");
363                 continue;
364             }
365             if (fsect.sct_item[I_MILIT] < 5) {
366                 pr("Not enough military for firing crew.\n");
367                 continue;
368             }
369             dam = fort_fire(&fsect);
370             putsect(&fsect);
371             if (dam < 0) {
372                 pr("Klick!     ...\n");
373                 continue;
374             }
375             range = fortrange(&fsect);
376             range2 = roundrange(range);
377             pr("range is %d.00 (%.2f)\n", range2, range);
378             if (target == targ_sub) {
379                 /* Don't tell it's a sub */
380                 range2 = -1;
381             }
382         }
383         if (trange > range2) {
384             pr("Target out of range.\n");
385             switch (type) {
386             case EF_SECTOR:
387                 putsect(&fsect);
388                 break;
389             case EF_LAND:
390                 fland.lnd_mission = 0;
391                 putland(fland.lnd_uid, &fland);
392                 break;
393             case EF_SHIP:
394                 fship.shp_mission = 0;
395                 putship(fship.shp_uid, &fship);
396                 break;
397             default:
398                 CANT_REACH();
399             }
400             continue;
401         }
402         switch (target) {
403         case targ_ship:
404             if (!trechk(player->cnum, vict, SEAFIR))
405                 continue;
406             break;
407         case targ_sub:
408             if (!trechk(player->cnum, vict, SUBFIR))
409                 continue;
410             break;
411         case targ_land:
412             if (!trechk(player->cnum, vict, LANFIR))
413                 continue;
414             break;
415         default:
416             break;
417         }
418
419         nfiring++;
420         switch (target) {
421         case targ_sub:
422             pr_beep();
423             pr("Kawhomp!!!\n");
424             break;
425         default:
426             pr_beep();
427             pr("Kaboom!!!\n");
428             break;
429         }
430         switch (target) {
431         case targ_bogus:
432         case targ_land:
433             nreport(player->cnum, N_SCT_SHELL, vict, 1);
434             if (vict && vict != player->cnum)
435                 wu(0, vict,
436                    "Country #%d shelled sector %s for %d damage.\n",
437                    player->cnum, xyas(x, y, vict), dam);
438             pr("Shells hit sector %s for %d damage.\n",
439                xyas(x, y, player->cnum), dam);
440             break;
441         case targ_ship:
442             nreport(player->cnum, N_SHP_SHELL, vict, 1);
443             /* fall through */
444         default:
445             if (vict) {
446                 wu(0, vict,
447                    "Country #%d shelled %s in %s for %d damage.\n",
448                    player->cnum, prship(&vship),
449                    xyas(vship.shp_x, vship.shp_y, vict), dam);
450             }
451             pr("Shells hit %s in %s for %d damage.\n",
452                prsub(&vship),
453                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
454             break;
455         }
456         /*  Ok, now, check if we had a bogus target.  If so,
457            just continue on, since there is no defender. */
458         if (target == targ_bogus)
459             continue;
460         attgp = &item.gen;
461         if (type == EF_LAND) {
462             getsect(fland.lnd_x, fland.lnd_y, &fsect);
463             attgp = (struct empobj *)&fsect;
464         }
465         totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
466         switch (target) {
467         case targ_land:
468             getsect(x, y, &vsect);
469             sectdamage(&vsect, dam);
470             putsect(&vsect);
471             break;
472         default:
473             getship(vshipno, &vship);
474             check_retreat_and_do_shipdamage(&vship, dam);
475             if (vship.shp_effic < SHIP_MINEFF)
476                 pr("%s sunk!\n", prsub(&vship));
477             else if (target == targ_sub
478                 && (vship.shp_rflags & RET_DCHRGED)
479                 && !(vship.shp_rflags & RET_INJURED))
480                 retreat_ship(&vship, 'd');
481             putship(vship.shp_uid, &vship);
482             break;
483         }
484         if (totaldefdam == 0 && target == targ_ship
485             && (vship.shp_rflags & RET_HELPLESS)
486             && !(vship.shp_rflags & RET_INJURED))
487             retreat_ship(&vship, 'h');
488         switch (attgp->ef_type) {
489         case EF_SECTOR:
490             putsect(&fsect);
491             break;
492         case EF_SHIP:
493             if ((target == targ_ship) || (target == targ_sub)) {
494                 if (fship.shp_effic > SHIP_MINEFF) {
495                     shp_missdef(&fship, vict);
496                 };
497             };
498             putship(fship.shp_uid, &fship);
499             break;
500         default:
501             CANT_REACH();
502         }
503     }
504
505     free_flist(&defended);
506     if (nfiring)
507         odds = ((double)ndefending) / ((double)nfiring);
508     else
509         odds = 1.0;
510     do_defdam(&fired, odds);
511     return RET_OK;
512 }
513
514 static int
515 defend(struct emp_qelem *al, struct emp_qelem *dl,
516        struct empobj *attgp, natid vict, int *nd)
517 {
518     int dam;
519     int nfiring = 0;
520
521     dam = quiet_bigdef(attgp->ef_type, dl, vict,
522                        attgp->own, attgp->x, attgp->y, &nfiring);
523     if (dam) {
524         if (nfiring > *nd)
525             *nd = nfiring;
526         add_to_flist(al, attgp, dam, vict);
527     }
528
529     return dam;
530 }
531
532 static void
533 do_defdam(struct emp_qelem *list, double odds)
534 {
535
536     int dam, first = 1;
537     natid vict;
538     struct flist *fp;
539     struct shpstr ship;
540     struct sctstr sect;
541     struct emp_qelem *qp, *next;
542
543     for (qp = list->q_forw; qp != list; qp = next) {
544         next = qp->q_forw;
545         fp = (struct flist *)qp;
546         if (fp->type == EF_SHIP) {
547             if (!getship(fp->uid, &ship) || !ship.shp_own)
548                 continue;
549         }
550         if (first) {
551             pr_beep();
552             pr("\nDefenders fire back!\n");
553             first = 0;
554         }
555         dam = odds * fp->defdam;
556
557         if (fp->type == EF_SHIP) {
558             vict = fp->victim;
559             pr("Return fire hit %s in %s for %d damage.\n",
560                prship(&ship),
561                xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
562             if (vict)
563                 wu(0, vict,
564                    "Return fire hit %s in %s for %d damage.\n",
565                    prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
566             shipdamage(&ship, dam);
567             putship(ship.shp_uid, &ship);
568         } else {
569             CANT_HAPPEN(fp->type != EF_SECTOR);
570             getsect(fp->x, fp->y, &sect);
571             vict = fp->victim;
572             pr("Return fire hit sector %s for %d damage.\n",
573                xyas(fp->x, fp->y, player->cnum), dam);
574             sectdamage(&sect, dam);
575             putsect(&sect);
576             if (vict)
577                 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
578                    xyas(fp->x, fp->y, vict), dam);
579         }
580         emp_remque(&fp->queue);
581         free(fp);
582     }
583 }
584
585 static int
586 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
587              coord ax, coord ay, int *nfiring)
588 {
589     double erange;
590     struct shpstr ship;
591     struct lndstr land;
592     struct nstr_item ni;
593     int dam, dam2;
594     struct sctstr firing;
595     struct nstr_sect ns;
596     struct flist *fp;
597
598     if (own == 0)
599         return 0;
600     dam = 0;
601     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
602     while (nxtitem(&ni, &ship)) {
603         if (!feels_like_helping(ship.shp_own, own, aown))
604             continue;
605
606         if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
607             continue;
608
609         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
610             erange = torprange(&ship);
611             if (roundrange(erange) < ni.curdist)
612                 continue;
613             if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
614                 continue;
615             fp = search_flist(list, (struct empobj *)&ship);
616             if (fp)
617                 dam2 = fp->defdam;
618             else {
619                 dam2 = shp_torp(&ship, 0);
620                 putship(ship.shp_uid, &ship);
621             }
622             if (dam2 < 0)
623                 continue;
624             if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
625                 dam2 = 0;
626         } else {
627             erange = shp_fire_range(&ship);
628             if (roundrange(erange) < ni.curdist)
629                 continue;
630             fp = search_flist(list, (struct empobj *)&ship);
631             if (fp)
632                 dam2 = fp->defdam;
633             else {
634                 dam2 = shp_fire(&ship);
635                 putship(ship.shp_uid, &ship);
636             }
637             if (dam2 < 0)
638                 continue;
639             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
640         }
641         (*nfiring)++;
642         if (!fp)
643             add_to_flist(list, (struct empobj *)&ship, dam2, 0);
644         dam += dam2;
645     }
646     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
647     while (nxtitem(&ni, &land)) {
648         if (!feels_like_helping(land.lnd_own, own, aown))
649             continue;
650
651         erange = lnd_fire_range(&land);
652         if (roundrange(erange) < ni.curdist)
653             continue;
654
655         fp = search_flist(list, (struct empobj *)&land);
656         if (fp)
657             dam2 = fp->defdam;
658         else {
659             dam2 = lnd_fire(&land);
660             putland(land.lnd_uid, &land);
661         }
662         if (dam2 < 0)
663             continue;
664
665         (*nfiring)++;
666         if (!fp)
667             add_to_flist(list, (struct empobj *)&land, dam2, 0);
668         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
669         if (type == EF_SHIP) {
670             if (chance(lnd_acc(&land) / 100.0))
671                 dam2 = ldround(dam2 / 2.0, 1);
672         }
673         dam += dam2;
674     }
675
676     /*
677      * Determine if any nearby gun-equipped sectors are within
678      * range and able to fire at an attacker.  Firing sectors
679      * need to have guns, shells, and military.  Sector being
680      * attacked is x,y -- attacker is at ax,ay.
681      */
682
683     if (!opt_NO_FORT_FIRE) {
684         snxtsct_dist(&ns, ax, ay, 8);
685         while (nxtsct(&ns, &firing)) {
686             if (!feels_like_helping(firing.sct_own, own, aown))
687                 continue;
688
689             erange = fortrange(&firing);
690             if (roundrange(erange) < ns.curdist)
691                 continue;
692
693             fp = search_flist(list, (struct empobj *)&firing);
694             if (fp)
695                 dam2 = fp->defdam;
696             else {
697                 dam2 = fort_fire(&firing);
698                 putsect(&firing);
699             }
700             if (dam2 < 0)
701                 continue;
702             (*nfiring)++;
703             if (!fp)
704                 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
705             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
706             dam += dam2;
707         }
708     }
709
710     return *nfiring == 0 ? 0 : dam / *nfiring;
711 }
712
713 static void
714 add_to_flist(struct emp_qelem *list,
715              struct empobj *gp, int dam, natid victim)
716 {
717     struct flist *fp;
718
719     fp = malloc(sizeof(struct flist));
720     fp->type = gp->ef_type;
721     fp->uid = gp->uid;
722     fp->x = gp->x;
723     fp->y = gp->y;
724     fp->defdam = dam;
725     fp->victim = victim;
726     emp_insque(&fp->queue, list);
727 }
728
729 static void
730 free_flist(struct emp_qelem *list)
731 {
732     struct emp_qelem *qp, *next;
733     struct flist *fp;
734
735     for (qp = list->q_forw; qp != list; qp = next) {
736         next = qp->q_forw;
737         fp = (struct flist *)qp;
738         emp_remque(&fp->queue);
739         free(fp);
740     }
741 }
742
743 static int
744 uid_eq(struct emp_qelem *elem, void *key)
745 {
746     return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
747 }
748
749 static struct flist *
750 search_flist(struct emp_qelem *list, struct empobj *gp)
751 {
752     return (struct flist *)emp_searchque(list, gp, uid_eq);
753 }