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