]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
Move declarations for chance.c to new chance.h
[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                 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
165                 continue;
166             }
167             if (item.ship.shp_item[I_GUN] == 0) {
168                 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
169                 continue;
170             }
171             if (item.ship.shp_item[I_SHELL] == 0) {
172                 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
173                 continue;
174             }
175             if (item.ship.shp_effic < 60) {
176                 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
177                 continue;
178             }
179             fx = fship.shp_x;
180             fy = fship.shp_y;
181         } else {
182             if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
183                 continue;
184             if (item.sect.sct_own != player->cnum)
185                 continue;
186             if (item.sect.sct_type != SCT_FORTR)
187                 continue;
188             if (item.sect.sct_effic < FORTEFF) {
189                 pr("Fort not efficient enough to fire!\n");
190                 continue;
191             }
192             if (item.sect.sct_item[I_GUN] == 0) {
193                 pr("Not enough guns in sector %s!\n",
194                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
195                 continue;
196             }
197             if (item.sect.sct_item[I_SHELL] == 0) {
198                 pr("Not enough shells in sector %s!\n",
199                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
200                 continue;
201             }
202             if (item.sect.sct_item[I_MILIT] < 5) {
203                 pr("Not enough military in sector %s!\n",
204                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
205                 continue;
206             }
207             pr("\nSector %s firing\n",
208                xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
209             fx = fsect.sct_x;
210             fy = fsect.sct_y;
211         }
212
213         ptr = getstarg(player->argp[3], "Firing at? ", buf);
214         if (!ptr)
215             return RET_FAIL;
216         if (!*ptr)
217             continue;
218         if (!issector(ptr)) {
219             vshipno = atoi(ptr);
220             if (vshipno < 0 || !getship(vshipno, &vship) ||
221                 (!vship.shp_own)) {
222                 pr("No such ship exists!\n");
223                 continue;
224             }
225             target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
226                 targ_sub : targ_ship;
227             vict = vship.shp_own;
228             x = vship.shp_x;
229             y = vship.shp_y;
230             if (!getsect(x, y, &vsect)) {
231                 pr("No such sector exists!\n");
232                 continue;
233             }
234         } else {
235             if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
236                 pr("No such sector exists!\n");
237                 continue;
238             }
239             /* We check the sector type, but we only use it for damage, not
240                reporting.  That way, you don't get extra information you wouldn't
241                normally get.  Besides, what if they want to slam water? :)  */
242             if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
243                 target = targ_bogus;
244             else
245                 target = targ_land;
246             vict = vsect.sct_own;
247             x = vsect.sct_x;
248             y = vsect.sct_y;
249         }
250
251         trange = mapdist(x, y, fx, fy);
252
253         if (type == EF_SHIP) {
254             if (!check_ship_ok(&fship))
255                 return RET_FAIL;
256             if (fship.shp_own != player->cnum) {
257                 pr("Not your ship!\n");
258                 continue;
259             }
260             if (target == targ_sub || target == targ_ship) {
261                 if (fship.shp_uid == vship.shp_uid) {
262                     pr("You can't fire upon yourself!\n");
263                     continue;
264                 }
265             }
266             if (fship.shp_item[I_MILIT] < 1) {
267                 pr("Not enough military for firing crew.\n");
268                 continue;
269             }
270             if (fship.shp_effic < 60) {
271                 pr("Ship #%d is crippled (%d%%)\n",
272                    fship.shp_uid, fship.shp_effic);
273                 continue;
274             }
275             range = shp_fire_range(&fship);
276             range2 = roundrange(range);
277             pr("range is %d.00 (%.2f)\n", range2, range);
278             if (target == targ_sub
279                 && trange <= range2
280                 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
281                 dam = shp_dchrg(&fship);
282             } else {
283                 if (target == targ_sub)
284                     /* Don't tell it's a sub */
285                     range2 = -1;
286                 if (fship.shp_item[I_GUN] == 0) {
287                     pr("Insufficient arms.\n");
288                     continue;
289                 }
290                 dam = shp_fire(&fship);
291             }
292             putship(fship.shp_uid, &fship);
293             if (dam <= 0) {
294                 pr("Klick!     ...\n");
295                 continue;
296             }
297             if (opt_NOMOBCOST == 0) {
298                 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
299                 putship(fship.shp_uid, &fship);
300             }
301         } else if (type == EF_LAND) {
302             if (!check_land_ok(&fland))
303                 return RET_FAIL;
304             if (fland.lnd_own != player->cnum) {
305                 pr("Not your unit!\n");
306                 continue;
307             }
308
309             if (target == targ_land) {
310                 if (fland.lnd_x == vsect.sct_x
311                     && fland.lnd_y == vsect.sct_y) {
312                     pr("You can't fire upon yourself!\n");
313                     continue;
314                 }
315             }
316
317             if (lchr[fland.lnd_type].l_dam == 0) {
318                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
319                 continue;
320             }
321             if (fland.lnd_item[I_GUN] == 0) {
322                 pr("%s -- not enough guns\n", prland(&fland));
323                 continue;
324             }
325
326             range = lnd_fire_range(&fland);
327             range2 = roundrange(range);
328             pr("range is %d.00 (%.2f)\n", range2, range);
329             if (target == targ_sub) {
330                 /* Don't tell it's a sub */
331                 range2 = -1;
332             }
333
334             dam = lnd_fire(&fland);
335             putland(fland.lnd_uid, &fland);
336             if (dam < 0) {
337                 pr("Klick!     ...\n");
338                 continue;
339             }
340             if (target == targ_ship) {
341                 if (chance(lnd_acc(&fland) / 100.0))
342                     dam = ldround(dam / 2.0, 1);
343             }
344         } else {
345             if (!check_sect_ok(&fsect))
346                 return RET_FAIL;
347             if (fsect.sct_own != player->cnum ||
348                 fsect.sct_type != SCT_FORTR) {
349                 pr("No fortress at %s\n",
350                    xyas(fsect.sct_x, fsect.sct_y, player->cnum));
351                 continue;
352             }
353             if (target == targ_land) {
354                 if (fsect.sct_x == vsect.sct_x
355                     && fsect.sct_y == vsect.sct_y) {
356                     pr("You can't fire upon yourself!\n");
357                     continue;
358                 }
359             }
360             if (fsect.sct_item[I_GUN] == 0) {
361                 pr("Insufficient arms.\n");
362                 continue;
363             }
364             if (fsect.sct_item[I_MILIT] < 5) {
365                 pr("Not enough military for firing crew.\n");
366                 continue;
367             }
368             dam = fort_fire(&fsect);
369             putsect(&fsect);
370             if (dam < 0) {
371                 pr("Klick!     ...\n");
372                 continue;
373             }
374             range = fortrange(&fsect);
375             range2 = roundrange(range);
376             pr("range is %d.00 (%.2f)\n", range2, range);
377             if (target == targ_sub) {
378                 /* Don't tell it's a sub */
379                 range2 = -1;
380             }
381         }
382         if (trange > range2) {
383             pr("Target out of range.\n");
384             switch (type) {
385             case EF_SECTOR:
386                 putsect(&fsect);
387                 break;
388             case EF_LAND:
389                 fland.lnd_mission = 0;
390                 putland(fland.lnd_uid, &fland);
391                 break;
392             case EF_SHIP:
393                 fship.shp_mission = 0;
394                 putship(fship.shp_uid, &fship);
395                 break;
396             default:
397                 CANT_REACH();
398             }
399             continue;
400         }
401         switch (target) {
402         case targ_ship:
403             if (!trechk(player->cnum, vict, SEAFIR))
404                 continue;
405             break;
406         case targ_sub:
407             if (!trechk(player->cnum, vict, SUBFIR))
408                 continue;
409             break;
410         case targ_land:
411             if (!trechk(player->cnum, vict, LANFIR))
412                 continue;
413             break;
414         default:
415             break;
416         }
417
418         nfiring++;
419         switch (target) {
420         case targ_sub:
421             pr_beep();
422             pr("Kawhomp!!!\n");
423             break;
424         default:
425             pr_beep();
426             pr("Kaboom!!!\n");
427             break;
428         }
429         switch (target) {
430         case targ_bogus:
431         case targ_land:
432             nreport(player->cnum, N_SCT_SHELL, vict, 1);
433             if (vict && vict != player->cnum)
434                 wu(0, vict,
435                    "Country #%d shelled sector %s for %d damage.\n",
436                    player->cnum, xyas(x, y, vict), dam);
437             pr("Shells hit sector %s for %d damage.\n",
438                xyas(x, y, player->cnum), dam);
439             break;
440         case targ_ship:
441             nreport(player->cnum, N_SHP_SHELL, vict, 1);
442             /* fall through */
443         default:
444             if (vict) {
445                 wu(0, vict,
446                    "Country #%d shelled %s in %s for %d damage.\n",
447                    player->cnum, prship(&vship),
448                    xyas(vship.shp_x, vship.shp_y, vict), dam);
449             }
450             pr("Shells hit %s in %s for %d damage.\n",
451                prsub(&vship),
452                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
453             break;
454         }
455         /*  Ok, now, check if we had a bogus target.  If so,
456            just continue on, since there is no defender. */
457         if (target == targ_bogus)
458             continue;
459         attgp = &item.gen;
460         if (type == EF_LAND) {
461             getsect(fland.lnd_x, fland.lnd_y, &fsect);
462             attgp = (struct empobj *)&fsect;
463         }
464         totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
465         switch (target) {
466         case targ_land:
467             getsect(x, y, &vsect);
468             sectdamage(&vsect, dam);
469             putsect(&vsect);
470             break;
471         default:
472             getship(vshipno, &vship);
473             check_retreat_and_do_shipdamage(&vship, dam);
474             if (vship.shp_effic < SHIP_MINEFF)
475                 pr("%s sunk!\n", prsub(&vship));
476             else if (target == targ_sub
477                 && (vship.shp_rflags & RET_DCHRGED)
478                 && !(vship.shp_rflags & RET_INJURED))
479                 retreat_ship(&vship, 'd');
480             putship(vship.shp_uid, &vship);
481             break;
482         }
483         if (totaldefdam == 0 && target == targ_ship
484             && (vship.shp_rflags & RET_HELPLESS)
485             && !(vship.shp_rflags & RET_INJURED))
486             retreat_ship(&vship, 'h');
487         switch (attgp->ef_type) {
488         case EF_SECTOR:
489             putsect(&fsect);
490             break;
491         case EF_SHIP:
492             if ((target == targ_ship) || (target == targ_sub)) {
493                 if (fship.shp_effic > SHIP_MINEFF) {
494                     shp_missdef(&fship, vict);
495                 };
496             };
497             putship(fship.shp_uid, &fship);
498             break;
499         default:
500             CANT_REACH();
501         }
502     }
503
504     free_flist(&defended);
505     if (nfiring)
506         odds = ((double)ndefending) / ((double)nfiring);
507     else
508         odds = 1.0;
509     do_defdam(&fired, odds);
510     return RET_OK;
511 }
512
513 static int
514 defend(struct emp_qelem *al, struct emp_qelem *dl,
515        struct empobj *attgp, natid vict, int *nd)
516 {
517     int dam;
518     int nfiring = 0;
519
520     dam = quiet_bigdef(attgp->ef_type, dl, vict,
521                        attgp->own, attgp->x, attgp->y, &nfiring);
522     if (dam) {
523         if (nfiring > *nd)
524             *nd = nfiring;
525         add_to_flist(al, attgp, dam, vict);
526     }
527
528     return dam;
529 }
530
531 static void
532 do_defdam(struct emp_qelem *list, double odds)
533 {
534
535     int dam, first = 1;
536     natid vict;
537     struct flist *fp;
538     struct shpstr ship;
539     struct sctstr sect;
540     struct emp_qelem *qp, *next;
541
542     for (qp = list->q_forw; qp != list; qp = next) {
543         next = qp->q_forw;
544         fp = (struct flist *)qp;
545         if (fp->type == EF_SHIP) {
546             if (!getship(fp->uid, &ship) || !ship.shp_own)
547                 continue;
548         }
549         if (first) {
550             pr_beep();
551             pr("\nDefenders fire back!\n");
552             first = 0;
553         }
554         dam = odds * fp->defdam;
555
556         if (fp->type == EF_SHIP) {
557             vict = fp->victim;
558             pr("Return fire hit %s in %s for %d damage.\n",
559                prship(&ship),
560                xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
561             if (vict)
562                 wu(0, vict,
563                    "Return fire hit %s in %s for %d damage.\n",
564                    prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
565             shipdamage(&ship, dam);
566             putship(ship.shp_uid, &ship);
567         } else {
568             CANT_HAPPEN(fp->type != EF_SECTOR);
569             getsect(fp->x, fp->y, &sect);
570             vict = fp->victim;
571             pr("Return fire hit sector %s for %d damage.\n",
572                xyas(fp->x, fp->y, player->cnum), dam);
573             sectdamage(&sect, dam);
574             putsect(&sect);
575             if (vict)
576                 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
577                    xyas(fp->x, fp->y, vict), dam);
578         }
579         emp_remque(&fp->queue);
580         free(fp);
581     }
582 }
583
584 static int
585 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
586              coord ax, coord ay, int *nfiring)
587 {
588     double erange;
589     struct shpstr ship;
590     struct lndstr land;
591     struct nstr_item ni;
592     int dam, dam2;
593     struct sctstr firing;
594     struct nstr_sect ns;
595     struct flist *fp;
596
597     if (own == 0)
598         return 0;
599     dam = 0;
600     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
601     while (nxtitem(&ni, &ship)) {
602         if (!feels_like_helping(ship.shp_own, own, aown))
603             continue;
604
605         if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
606             continue;
607
608         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
609             erange = torprange(&ship);
610             if (roundrange(erange) < ni.curdist)
611                 continue;
612             if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
613                 continue;
614             fp = search_flist(list, (struct empobj *)&ship);
615             if (fp)
616                 dam2 = fp->defdam;
617             else {
618                 dam2 = shp_torp(&ship, 0);
619                 putship(ship.shp_uid, &ship);
620             }
621             if (dam2 < 0)
622                 continue;
623             if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
624                 dam2 = 0;
625         } else {
626             erange = shp_fire_range(&ship);
627             if (roundrange(erange) < ni.curdist)
628                 continue;
629             fp = search_flist(list, (struct empobj *)&ship);
630             if (fp)
631                 dam2 = fp->defdam;
632             else {
633                 dam2 = shp_fire(&ship);
634                 putship(ship.shp_uid, &ship);
635             }
636             if (dam2 < 0)
637                 continue;
638             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
639         }
640         (*nfiring)++;
641         if (!fp)
642             add_to_flist(list, (struct empobj *)&ship, dam2, 0);
643         dam += dam2;
644     }
645     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
646     while (nxtitem(&ni, &land)) {
647         if (!feels_like_helping(land.lnd_own, own, aown))
648             continue;
649
650         erange = lnd_fire_range(&land);
651         if (roundrange(erange) < ni.curdist)
652             continue;
653
654         fp = search_flist(list, (struct empobj *)&land);
655         if (fp)
656             dam2 = fp->defdam;
657         else {
658             dam2 = lnd_fire(&land);
659             putland(land.lnd_uid, &land);
660         }
661         if (dam2 < 0)
662             continue;
663
664         (*nfiring)++;
665         if (!fp)
666             add_to_flist(list, (struct empobj *)&land, dam2, 0);
667         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
668         if (type == EF_SHIP) {
669             if (chance(lnd_acc(&land) / 100.0))
670                 dam2 = ldround(dam2 / 2.0, 1);
671         }
672         dam += dam2;
673     }
674
675     /*
676      * Determine if any nearby gun-equipped sectors are within
677      * range and able to fire at an attacker.  Firing sectors
678      * need to have guns, shells, and military.  Sector being
679      * attacked is x,y -- attacker is at ax,ay.
680      */
681
682     if (!opt_NO_FORT_FIRE) {
683         snxtsct_dist(&ns, ax, ay, 8);
684         while (nxtsct(&ns, &firing)) {
685             if (!feels_like_helping(firing.sct_own, own, aown))
686                 continue;
687
688             erange = fortrange(&firing);
689             if (roundrange(erange) < ns.curdist)
690                 continue;
691
692             fp = search_flist(list, (struct empobj *)&firing);
693             if (fp)
694                 dam2 = fp->defdam;
695             else {
696                 dam2 = fort_fire(&firing);
697                 putsect(&firing);
698             }
699             if (dam2 < 0)
700                 continue;
701             (*nfiring)++;
702             if (!fp)
703                 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
704             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
705             dam += dam2;
706         }
707     }
708
709     return *nfiring == 0 ? 0 : dam / *nfiring;
710 }
711
712 static void
713 add_to_flist(struct emp_qelem *list,
714              struct empobj *gp, int dam, natid victim)
715 {
716     struct flist *fp;
717
718     fp = malloc(sizeof(struct flist));
719     fp->type = gp->ef_type;
720     fp->uid = gp->uid;
721     fp->x = gp->x;
722     fp->y = gp->y;
723     fp->defdam = dam;
724     fp->victim = victim;
725     emp_insque(&fp->queue, list);
726 }
727
728 static void
729 free_flist(struct emp_qelem *list)
730 {
731     struct emp_qelem *qp, *next;
732     struct flist *fp;
733
734     for (qp = list->q_forw; qp != list; qp = next) {
735         next = qp->q_forw;
736         fp = (struct flist *)qp;
737         emp_remque(&fp->queue);
738         free(fp);
739     }
740 }
741
742 static int
743 uid_eq(struct emp_qelem *elem, void *key)
744 {
745     return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
746 }
747
748 static struct flist *
749 search_flist(struct emp_qelem *list, struct empobj *gp)
750 {
751     return (struct flist *)emp_searchque(list, gp, uid_eq);
752 }