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