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