2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * See files README, COPYING and CREDITS in the root of the source
23 * tree for related information and legal notices. It is expected
24 * that future projects/authors will amend these files as needed.
28 * shpsub.c: Ship subroutine stuff
30 * Known contributors to this file:
32 * Steve McClure, 1996-2000
33 * Markus Armbruster, 2006
48 #include "prototypes.h"
55 static int shp_check_one_mines(struct ulist *);
56 static int shp_hit_mine(struct shpstr *, struct mchrstr *);
57 static void shp_mess(char *, struct ulist *);
60 shp_sel(struct nstr_item *ni, struct emp_qelem *list)
72 while (nxtitem(ni, &ship)) {
75 mcp = &mchr[(int)ship.shp_type];
76 /* if (wantflags && (mcp->m_flags & wantflags) != wantflags)
78 if (nowantflags && mcp->m_flags & nowantflags)
82 if (ontradingblock(EF_SHIP, &ship)) {
83 pr("ship #%d inelligible - it's for sale.\n",
88 /* This abuse is better fixed by building a ship with the normal negative
89 mobility that everything else is built with */
92 if (ship.shp_effic < 21 &&
93 ship.shp_mobil < etu_per_update) {
94 pr("%s needs at least %d mob to navigate.\n",
95 prship(&ship), etu_per_update);
100 ship.shp_mission = 0;
102 memset(ship.shp_rpath, 0, sizeof(ship.shp_rpath));
103 putship(ship.shp_uid, &ship);
104 mlp = malloc(sizeof(struct ulist));
105 mlp->chrp = (struct empobj_chr *)mcp;
106 mlp->unit.ship = ship;
107 mlp->mobil = ship.shp_mobil;
108 emp_insque(&mlp->queue, list);
112 /* This function assumes that the list was created by shp_sel */
114 shp_nav(struct emp_qelem *list, double *minmobp, double *maxmobp,
115 int *togetherp, natid actor)
117 struct emp_qelem *qp;
118 struct emp_qelem *next;
129 for (qp = list->q_back; qp != list; qp = next) {
131 mlp = (struct ulist *)qp;
132 getship(mlp->unit.ship.shp_uid, &ship);
133 if (ship.shp_own != actor) {
134 mpr(actor, "%s was sunk at %s\n",
135 prship(&ship), xyas(ship.shp_x, ship.shp_y, actor));
136 emp_remque((struct emp_qelem *)mlp);
141 if (*ship.shp_path && !update_running) {
142 shp_mess("has a sail path", mlp);
143 mpr(actor, "Use `sail <#> -' to reset\n");
147 /* check crew - uws don't count */
148 if (ship.shp_item[I_MILIT] == 0 && ship.shp_item[I_CIVIL] == 0) {
149 shp_mess("is crewless", mlp);
152 if (!getsect(ship.shp_x, ship.shp_y, §)) {
153 shp_mess("was sucked into the sky by a strange looking spaceship", mlp); /* heh -KHS */
156 switch (shp_check_nav(§, &ship)) {
157 case CN_CONSTRUCTION:
158 shp_mess("is caught in a construction zone", mlp);
161 shp_mess("is landlocked", mlp);
167 shp_mess("was just swallowed by a big green worm", mlp);
175 if (ship.shp_x != allx || ship.shp_y != ally)
177 if (ship.shp_mobil + 1 < (int)mlp->mobil) {
178 mlp->mobil = ship.shp_mobil;
180 if (mlp->mobil < *minmobp)
181 *minmobp = mlp->mobil;
182 if (mlp->mobil > *maxmobp)
183 *maxmobp = mlp->mobil;
184 mlp->unit.ship = ship;
189 shp_sweep(struct emp_qelem *ship_list, int verbose, int takemob, natid actor)
191 struct emp_qelem *qp;
192 struct emp_qelem *next;
195 int mines, m, max, shells;
199 for (qp = ship_list->q_back; qp != ship_list; qp = next) {
201 mlp = (struct ulist *)qp;
202 if (!(((struct mchrstr *)mlp->chrp)->m_flags & M_SWEEP)) {
204 mpr(actor, "%s doesn't have minesweeping capability!\n",
205 prship(&mlp->unit.ship));
208 if (takemob && mlp->mobil <= 0.0) {
210 mpr(actor, "%s is out of mobility!\n",
211 prship(&mlp->unit.ship));
214 getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, §);
215 if (sect.sct_type != SCT_WATER) {
217 mpr(actor, "%s is not at sea. No mines there!\n",
218 prship(&mlp->unit.ship));
222 mlp->mobil -= shp_mobcost(&mlp->unit.ship);
223 mlp->unit.ship.shp_mobil = (int)mlp->mobil;
225 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
226 if (!(mines = sect.sct_mines))
228 max = ((struct mchrstr *)mlp->chrp)->m_item[I_SHELL];
229 shells = mlp->unit.ship.shp_item[I_SHELL];
230 for (m = 0; mines > 0 && m < 5; m++) {
232 mpr(actor, "Sweep...\n");
234 shells = MIN(max, shells + 1);
235 changed |= map_set(actor, sect.sct_x, sect.sct_y, 'X', 0);
238 sect.sct_mines = mines;
239 mlp->unit.ship.shp_item[I_SHELL] = shells;
240 if (shp_check_one_mines(mlp)) {
245 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
254 shp_check_one_mines(struct ulist *mlp)
259 getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, §);
260 if (sect.sct_type != SCT_WATER)
264 if (chance(DMINE_HITCHANCE(sect.sct_mines))) {
265 actor = mlp->unit.ship.shp_own;
266 shp_hit_mine(&mlp->unit.ship, ((struct mchrstr *)mlp->chrp));
268 if (map_set(actor, sect.sct_x, sect.sct_y, 'X', 0))
271 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
272 if (!mlp->unit.ship.shp_own)
279 shp_check_mines(struct emp_qelem *ship_list)
281 struct emp_qelem *qp;
282 struct emp_qelem *next;
286 for (qp = ship_list->q_back; qp != ship_list; qp = next) {
288 mlp = (struct ulist *)qp;
289 if (shp_check_one_mines(mlp)) {
300 shp_mess(char *str, struct ulist *mlp)
302 mpr(mlp->unit.ship.shp_own, "%s %s & stays in %s\n",
303 prship(&mlp->unit.ship),
304 str, xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
305 mlp->unit.ship.shp_own));
306 mlp->unit.ship.shp_mobil = (int)mlp->mobil;
307 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
308 emp_remque((struct emp_qelem *)mlp);
313 shp_check_nav(struct sctstr *sect, struct shpstr *shp)
315 switch (dchr[sect->sct_type].d_nav) {
319 if (mchr[(int)shp->shp_type].m_flags & M_CANAL) {
320 if (sect->sct_effic < 2)
321 return CN_CONSTRUCTION;
323 return CN_LANDLOCKED;
326 if (sect->sct_effic < 2)
327 return CN_CONSTRUCTION;
330 if (sect->sct_effic < 60)
331 return CN_CONSTRUCTION;
334 return CN_LANDLOCKED;
340 sect_has_dock(struct sctstr *sect)
342 switch (dchr[sect->sct_type].d_nav) {
352 shp_count(struct emp_qelem *list, int wantflags, int nowantflags,
355 struct emp_qelem *qp;
356 struct emp_qelem *next;
360 for (qp = list->q_back; qp != list; qp = next) {
362 mlp = (struct ulist *)qp;
363 if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
366 (((struct mchrstr *)mlp->chrp)->m_flags & wantflags) != wantflags)
369 ((struct mchrstr *)mlp->chrp)->m_flags & nowantflags)
377 shp_damage_one(struct ulist *mlp, int dam)
379 shipdamage(&mlp->unit.ship, dam);
380 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
381 if (!mlp->unit.ship.shp_own) {
382 emp_remque((struct emp_qelem *)mlp);
388 shp_damage(struct emp_qelem *list, int totdam, int wantflags,
389 int nowantflags, int x, int y)
391 struct emp_qelem *qp;
392 struct emp_qelem *next;
398 || !(count = shp_count(list, wantflags, nowantflags, x, y)))
400 dam = ldround((double)totdam / count, 1);
401 for (qp = list->q_back; qp != list; qp = next) {
403 mlp = (struct ulist *)qp;
404 if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
407 (((struct mchrstr *)mlp->chrp)->m_flags & wantflags) != wantflags)
410 ((struct mchrstr *)mlp->chrp)->m_flags & nowantflags)
412 shp_damage_one(mlp, dam);
418 shp_contains(struct emp_qelem *list, int newx, int newy, int wantflags,
421 struct emp_qelem *qp;
422 struct emp_qelem *next;
425 for (qp = list->q_back; qp != list; qp = next) {
427 mlp = (struct ulist *)qp;
428 /* If the ship isn't in the requested sector, then continue */
429 if (newx != mlp->unit.ship.shp_x || newy != mlp->unit.ship.shp_y)
432 (((struct mchrstr *)mlp->chrp)->m_flags & wantflags) != wantflags)
435 ((struct mchrstr *)mlp->chrp)->m_flags & nowantflags)
442 static struct ulist *
443 most_valuable_ship(struct emp_qelem *list)
445 struct emp_qelem *qp;
446 struct emp_qelem *next;
448 struct ulist *mvs = 0;
450 for (qp = list->q_back; qp != list; qp = next) {
452 mlp = (struct ulist *)qp;
453 if (((struct mchrstr *)mlp->chrp)->m_flags & M_SUB)
455 if (!((struct mchrstr *)mlp->chrp)->m_nxlight &&
456 !((struct mchrstr *)mlp->chrp)->m_nchoppers &&
457 ((struct mchrstr *)mlp->chrp)->m_cost < 1000 &&
458 !((struct mchrstr *)mlp->chrp)->m_nplanes &&
459 !((struct mchrstr *)mlp->chrp)->m_nland)
465 if (((struct mchrstr *)mlp->chrp)->m_cost * mlp->unit.ship.shp_effic >
466 ((struct mchrstr *)mlp->chrp)->m_cost * mvs->unit.ship.shp_effic)
473 shp_easiest_target(struct emp_qelem *list, int wantflags, int nowantflags)
475 struct emp_qelem *qp;
476 struct emp_qelem *next;
479 int easiest = 9876; /* things start great for victim */
482 for (qp = list->q_back; qp != list; qp = next) {
484 mlp = (struct ulist *)qp;
486 (((struct mchrstr *)mlp->chrp)->m_flags & wantflags) != wantflags)
489 ((struct mchrstr *)mlp->chrp)->m_flags & nowantflags)
491 hard = shp_hardtarget(&mlp->unit.ship);
493 easiest = hard; /* things get worse for victim */
496 return easiest - count;
500 shp_missile_interdiction(struct emp_qelem *list, coord newx, coord newy,
506 struct emp_qelem msl_list, *qp, *newqp;
510 msl_sel(&msl_list, newx, newy, victim, P_T | P_MAR, 0, MI_INTERDICT);
513 while (!QEMPTY(&msl_list) && (mvs = most_valuable_ship(list))) {
514 sprintf(what, "%s", prship(&mvs->unit.ship));
515 dam = msl_launch_mindam(&msl_list, newx, newy,
516 shp_hardtarget(&mvs->unit.ship),
517 EF_SHIP, 1, what, victim, MI_INTERDICT);
520 "missile interdiction mission does %d damage to %s!\n",
522 shp_damage_one(mvs, dam);
525 } else if (++twotries >= 2) {
529 qp = msl_list.q_forw;
530 while (qp != msl_list.q_forw) {
540 /* Note that this function has a side effect - it uses coastwatch
541 * ranges to see if it should fire upon a ship. So, this function
542 * is expected to return positive if a ship is in range, and 0 if a
543 * ship is not in range. */
545 notify_coastguard(struct emp_qelem *list, int trange, struct sctstr *sectp)
547 struct emp_qelem *qp;
548 struct emp_qelem *next;
553 natp = getnatp(sectp->sct_own);
555 vrange = sectp->sct_type == SCT_RADAR ? 14 : 4;
556 vrange *= tfact(sectp->sct_own, 1.0) * sectp->sct_effic / 100.0;
564 for (qp = list->q_back; qp != list; qp = next) {
566 mlp = (struct ulist *)qp;
567 if (((struct mchrstr *)mlp->chrp)->m_flags & M_SUB)
569 if (natp->nat_flags & NF_COASTWATCH)
570 wu(0, sectp->sct_own,
571 "%s %s sighted at %s\n",
572 cname(mlp->unit.ship.shp_own),
573 prship(&mlp->unit.ship),
574 xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
577 setcont(sectp->sct_own, mlp->unit.ship.shp_own, FOUND_COAST);
584 shp_fort_interdiction(struct emp_qelem *list, coord newx, coord newy,
594 signed char notified[MAXNOC];
597 /* Inform neutral and worse */
598 for (i = 0; i < MAXNOC; ++i) {
599 if (getrel(getnatp(i), victim) <= NEUTRAL)
605 snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
606 while (nxtsct(&ns, &fsect)) {
609 if (fsect.sct_own == victim)
611 if (notified[fsect.sct_own])
613 trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
614 if (notify_coastguard(list, trange, &fsect))
615 notified[fsect.sct_own] = 1;
617 if (opt_NO_FORT_FIRE)
618 return 0; /* Only coastwatch notify in nofortfire */
619 /* Only fire at Hostile ships */
620 for (i = 0; i < MAXNOC; ++i) {
621 if (getrel(getnatp(i), victim) >= NEUTRAL)
624 snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
625 while (nxtsct(&ns, &fsect)) {
626 if (!notified[fsect.sct_own])
628 gun = fsect.sct_item[I_GUN];
631 range = roundrange(fortrange(&fsect));
632 trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
635 if (fsect.sct_item[I_MILIT] < 5)
637 shell = fsect.sct_item[I_SHELL];
639 shell += supply_commod(fsect.sct_own, fsect.sct_x, fsect.sct_y,
644 fsect.sct_item[I_SHELL] = shell;
648 guneff = landgun((int)fsect.sct_effic, gun);
651 mpr(victim, "Incoming fire does %d damage!\n", dam);
653 mpr(victim, "%s fires at you for %d!\n",
654 xyas(fsect.sct_x,fsect.sct_y,victim),
658 "%s fires at %s ships in %s for %d!\n",
659 xyas(fsect.sct_x, fsect.sct_y,
661 cname(victim), xyas(newx, newy, fsect.sct_own), dam);
662 nreport(fsect.sct_own, N_SHP_SHELL, victim, 1);
665 return shp_damage(list, totdam, 0, M_SUB, newx, newy);
670 shp_interdict(struct emp_qelem *list, coord newx, coord newy, natid victim)
674 if (shp_contains(list, newx, newy, 0, M_SUB)) {
675 stopping |= shp_fort_interdiction(list, newx, newy, victim);
677 if (shp_contains(list, newx, newy, 0, M_SUB)) {
680 unit_interdict(newx, newy, victim, "ships",
681 shp_easiest_target(list, 0, M_SUB),
683 0, M_SUB, newx, newy);
684 if (most_valuable_ship(list)) {
686 shp_missile_interdiction(list, newx, newy, victim);
690 if (shp_contains(list, newx, newy, M_SUB, 0)) {
693 unit_interdict(newx, newy, victim, "subs",
694 shp_easiest_target(list, M_SUB, 0),
696 M_SUB, 0, newx, newy);
701 /* high value of hardtarget is harder to hit */
703 shp_hardtarget(struct shpstr *sp)
707 struct mchrstr *mcp = mchr + sp->shp_type;
710 getsect(sp->shp_x, sp->shp_y, §);
711 onsea = sect.sct_type == SCT_WATER;
712 if (mcp->m_flags & M_SUB)
714 return (int)((sp->shp_effic / 100.0) *
715 (20 + sp->shp_speed * onsea / 2.0 - vis));
719 shp_hit_mine(struct shpstr *sp, struct mchrstr *mcp)
723 mpr(sp->shp_own, "Kawhomp! Mine detected in %s!\n",
724 xyas(sp->shp_x, sp->shp_y, sp->shp_own));
726 nreport(sp->shp_own, N_HIT_MINE, 0, 1);
729 if (mcp->m_flags & M_SWEEP)
732 shipdamage(sp, ldround(m, 1));
738 shp_nav_one_sector(struct emp_qelem *list, int dir, natid actor,
742 struct emp_qelem *qp;
743 struct emp_qelem *next;
745 struct emp_qelem done;
752 double tech; /* for mapping */
753 double tf; /* for mapping */
757 if (dir <= DIR_STOP || dir >= DIR_VIEW) {
758 unit_put(list, actor);
763 for (qp = list->q_back; qp != list; qp = next) {
765 mlp = (struct ulist *)qp;
766 newx = xnorm(mlp->unit.ship.shp_x + dx);
767 newy = ynorm(mlp->unit.ship.shp_y + dy);
768 getsect(newx, newy, §);
769 navigate = shp_check_nav(§, &mlp->unit.ship);
770 if (navigate != CN_NAVIGABLE ||
771 (sect.sct_own && actor != sect.sct_own &&
772 getrel(getnatp(sect.sct_own), actor) < FRIENDLY)) {
773 if (dchr[sect.sct_type].d_nav == NAV_CANAL &&
774 !(((struct mchrstr *)mlp->chrp)->m_flags & M_CANAL) &&
775 navigate == CN_LANDLOCKED)
777 "is too large to fit into the canal system at %s",
778 xyas(newx, newy, actor));
780 sprintf(dp, "can't go to %s", xyas(newx, newy, actor));
782 mpr(actor, "%s\n", dp);
790 if (mlp->mobil <= 0.0) {
791 shp_mess("is out of mobility", mlp);
794 mobcost = shp_mobcost(&mlp->unit.ship);
795 mlp->unit.ship.shp_x = newx;
796 mlp->unit.ship.shp_y = newy;
797 if (mlp->mobil - mobcost < -127) {
800 mlp->mobil -= mobcost;
802 mlp->unit.ship.shp_mobil = (int)mlp->mobil;
803 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
805 /* Now update the map for this ship */
806 tech = techfact(mlp->unit.ship.shp_tech,
807 ((struct mchrstr *)mlp->chrp)->m_vrnge);
808 if (((struct mchrstr *)mlp->chrp)->m_flags & M_SONAR)
809 tf = techfact(mlp->unit.ship.shp_tech, 1.0);
812 radmapupd(mlp->unit.ship.shp_own,
813 mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
814 (int)mlp->unit.ship.shp_effic, (int)tech, tf);
818 stopping |= shp_sweep(list, 0, 0, actor);
821 stopping |= shp_check_mines(list);
825 /* interdict ships sector by sector */
827 while (!QEMPTY(list)) {
828 mlp = (struct ulist *)list->q_back;
829 newx = mlp->unit.ship.shp_x;
830 newy = mlp->unit.ship.shp_y;
831 stopping |= shp_interdict(list, newx, newy, actor);
832 /* move survivors in this sector to done */
833 for (qp = list->q_back; qp != list; qp = next) {
835 mlp = (struct ulist *)qp;
836 if (mlp->unit.ship.shp_x == newx &&
837 mlp->unit.ship.shp_y == newy) {
839 emp_insque(qp, &done);
843 /* assign surviving ships back to list */
844 emp_insque(list, &done);
852 * Check for incoming missiles with a P_MAR flag.
853 * Return True=1 if the missile was shotdown.
856 * Chad Zabel, July 95
860 shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
866 double gun, eff, teff;
868 snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
870 while (nxtitem(&ni, &ship)) {
874 if (!(mchr[(int)ship.shp_type].m_flags & M_ANTIMISSILE))
877 if (getrel(getnatp(ship.shp_own), bombown) >= NEUTRAL)
880 if (ship.shp_effic < 60)
883 shell = ship.shp_item[I_SHELL];
884 if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */
886 if (shell < 2) { /* do we need shells */
887 shell += supply_commod(ship.shp_own, ship.shp_x, ship.shp_y,
892 if (ship.shp_item[I_GUN] < 1) /* we need at least 1 gun */
895 /* now calculate the odds */
896 gun = MIN(ship.shp_item[I_GUN], ship.shp_glim);
897 eff = ship.shp_effic / 100.0;
898 teff = ship.shp_tech / (ship.shp_tech + 200.0);
899 /* raise 4.5 for better interception -KHS */
900 hitchance = (int)(gun * eff * teff * 4.5) - hardtarget;
906 mpr(bombown, "%s anti-missile system activated...",
907 cname(ship.shp_own));
908 mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n",
910 mpr(ship.shp_own, "%d%% hitchance...", hitchance);
912 ship.shp_item[I_SHELL] = shell - 2;
913 putship(ship.shp_uid, &ship);
915 if (roll(100) <= hitchance) {
916 mpr(bombown, "KABOOOM!! Missile destroyed\n\n");
918 "KABOOOM!! Incoming missile destroyed!\n\n");
921 mpr(bombown, "SWOOSH!! anti-missile system failed!!\n");
923 "SWOOSH!! Missile evades anti-missile systems\n\n");
926 return 0; /* all attempts failed */
930 /* Fire missiles at a ship which has fired shells */
932 shp_missdef(struct shpstr *sp, natid victim)
934 struct emp_qelem list;
941 mlp = malloc(sizeof(struct ulist));
942 mlp->chrp = (struct empobj_chr *)&mchr[(int)sp->shp_type];
943 mlp->unit.ship = *sp;
944 mlp->mobil = sp->shp_mobil;
945 emp_insque(&mlp->queue, &list);
946 sprintf(buf, "%s", prship(&mlp->unit.ship));
949 if (most_valuable_ship(&list)) {
950 shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
951 getship(sp->shp_uid, sp);
955 "missiles launched in defense did 100%% damage to %s\n",
957 wu(0, victim, "%s sunk!\n", buf);
958 } else if (eff > 0 && sp->shp_effic < eff) {
960 "missiles launched in defense did %d%% damage to %s\n",
961 100 * (eff - sp->shp_effic) / eff, buf);
969 shp_mobcost(struct shpstr *sp)
971 return speed_factor(sp->shp_effic * 0.01 * sp->shp_speed,
976 * Set SP's tech to TLEV along with everything else that depends on it.
979 shp_set_tech(struct shpstr *sp, int tlev)
981 struct mchrstr *mcp = mchr + sp->shp_type;
983 if (CANT_HAPPEN(tlev < mcp->m_tech))
987 sp->shp_armor = m_armor(mcp, tlev);
988 sp->shp_speed = m_speed(mcp, tlev);
989 sp->shp_visib = m_visib(mcp, tlev);
990 sp->shp_frnge = m_frnge(mcp, tlev);
991 sp->shp_glim = m_glim(mcp, tlev);