2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2015, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure, Markus Armbruster
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.
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, see <http://www.gnu.org/licenses/>.
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.
27 * shpsub.c: Ship subroutine stuff
29 * Known contributors to this file:
31 * Steve McClure, 1996-2000
32 * Markus Armbruster, 2006-2015
50 #include "prototypes.h"
56 static void shp_nav_put_one(struct ulist *);
57 static int shp_check_one_mines(struct ulist *);
58 static int shp_hit_mine(struct shpstr *);
59 static void shp_stays(natid, char *, struct ulist *);
62 shp_find_capable(struct emp_qelem *list, int flags)
67 for (qp = list->q_back; qp != list; qp = qp->q_back) {
68 mlp = (struct ulist *)qp;
69 if (mchr[mlp->unit.ship.shp_type].m_flags & flags)
76 shp_may_nav(struct shpstr *sp, struct shpstr *flg, char *suffix)
80 if (!sp->shp_own || !getsect(sp->shp_x, sp->shp_y, §)) {
85 if (sp->shp_item[I_MILIT] == 0 && sp->shp_item[I_CIVIL] == 0) {
86 mpr(sp->shp_own, "%s is crewless%s\n", prship(sp), suffix);
90 switch (shp_check_nav(sp, §)) {
93 case SHP_STUCK_CONSTRUCTION:
94 mpr(sp->shp_own, "%s is caught in a construction zone%s\n",
100 case SHP_STUCK_CANAL:
101 case SHP_STUCK_IMPASSABLE:
102 mpr(sp->shp_own, "%s is landlocked%s\n", prship(sp), suffix);
106 if (flg && (sp->shp_x != flg->shp_x || sp->shp_y != flg->shp_y)) {
107 mpr(sp->shp_own, "%s is not with the flagship%s\n",
115 shp_sel(struct nstr_item *ni, struct emp_qelem *list)
117 struct shpstr ship, *flg = NULL;
121 while (nxtitem(ni, &ship)) {
123 * It would be nice to let deities navigate foreign ships, but
124 * much of the code assumes that only the ship's owner can
127 if (!ship.shp_own || ship.shp_own != player->cnum)
130 if (ontradingblock(EF_SHIP, &ship)) {
131 pr("ship #%d inelligible - it's for sale.\n",
136 if (!shp_may_nav(&ship, flg, ""))
139 ship.shp_mission = 0;
141 memset(ship.shp_rpath, 0, sizeof(ship.shp_rpath));
142 putship(ship.shp_uid, &ship);
143 mlp = shp_insque(&ship, list);
145 flg = &mlp->unit.ship;
151 * Return the new list link.
154 shp_insque(struct shpstr *sp, struct emp_qelem *list)
156 struct ulist *mlp = malloc(sizeof(struct ulist));
158 mlp->unit.ship = *sp;
159 mlp->mobil = sp->shp_mobil;
160 emp_insque(&mlp->queue, list);
164 /* This function assumes that the list was created by shp_sel */
166 shp_nav(struct emp_qelem *list, double *minmobp, double *maxmobp,
169 struct emp_qelem *qp;
170 struct emp_qelem *next;
172 struct shpstr *sp, *flg = NULL;
177 for (qp = list->q_back; qp != list; qp = next) {
179 mlp = (struct ulist *)qp;
180 sp = &mlp->unit.ship;
181 getship(sp->shp_uid, sp);
183 if (sp->shp_own != actor) {
184 mpr(actor, "%s was sunk at %s\n",
185 prship(sp), xyas(sp->shp_x, sp->shp_y, actor));
186 emp_remque(&mlp->queue);
191 snprintf(and_stays, sizeof(and_stays), " & stays in %s",
192 xyas(sp->shp_x, sp->shp_y, actor));
193 if (!shp_may_nav(sp, flg, and_stays)) {
194 shp_nav_put_one(mlp);
200 if (sp->shp_mobil + 1 < (int)mlp->mobil) {
201 mlp->mobil = sp->shp_mobil;
203 if (mlp->mobil < *minmobp)
204 *minmobp = mlp->mobil;
205 if (mlp->mobil > *maxmobp)
206 *maxmobp = mlp->mobil;
211 shp_nav_put(struct emp_qelem *list, natid actor)
213 struct emp_qelem *qp, *next;
217 for (qp = list->q_back; qp != list; qp = next) {
219 mlp = (struct ulist *)qp;
220 sp = &mlp->unit.ship;
221 mpr(actor, "%s stopped at %s\n",
222 prship(sp), xyas(sp->shp_x, sp->shp_y, actor));
223 shp_nav_put_one(mlp);
228 shp_nav_put_one(struct ulist *mlp)
230 mlp->unit.ship.shp_mobil = (int)mlp->mobil;
231 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
232 emp_remque(&mlp->queue);
237 * Sweep seamines with engineers in SHIP_LIST for ACTOR.
238 * All ships in SHIP_LIST must be in the same sector.
239 * If EXPLICIT is non-zero, this is for an explicit sweep command from
240 * a player. Else it's an automatic "on the move" sweep.
241 * If TAKEMOB is non-zero, require and charge mobility.
242 * Return non-zero when the ships should stop.
245 shp_sweep(struct emp_qelem *ship_list, int explicit, int takemob,
248 struct emp_qelem *qp;
249 struct emp_qelem *next;
252 int mines, m, max, shells;
254 int stopping = 0, first = 1;
256 mlp = shp_find_capable(ship_list, M_SWEEP);
259 mpr(actor, "No minesweepers!\n");
263 getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, §);
264 if (sect.sct_type != SCT_WATER) {
266 mpr(actor, "%s is a %s. No seamines there!\n",
267 xyas(sect.sct_x, sect.sct_y, actor),
268 dchr[sect.sct_type].d_name);
272 for (qp = ship_list->q_back; qp != ship_list; qp = next) {
274 mlp = (struct ulist *)qp;
275 if (!(mchr[mlp->unit.ship.shp_type].m_flags & M_SWEEP))
278 if (mlp->mobil <= 0.0) {
280 mpr(actor, "%s is out of mobility!\n",
281 prship(&mlp->unit.ship));
284 mlp->mobil -= shp_mobcost(&mlp->unit.ship);
285 mlp->unit.ship.shp_mobil = (int)mlp->mobil;
287 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
288 getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, §);
289 if (!(mines = sect.sct_mines))
291 max = mchr[mlp->unit.ship.shp_type].m_item[I_SHELL];
292 shells = mlp->unit.ship.shp_item[I_SHELL];
293 for (m = 0; mines > 0 && m < 5; m++) {
296 mpr(actor, "Approaching minefield at %s...\n",
297 xyas(sect.sct_x, sect.sct_y, actor));
300 mpr(actor, "Sweep...\n");
302 shells = MIN(max, shells + 1);
303 changed |= map_set(actor, sect.sct_x, sect.sct_y, 'X', 0);
306 sect.sct_mines = mines;
307 mlp->unit.ship.shp_item[I_SHELL] = shells;
308 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
310 if (shp_check_one_mines(mlp)) {
322 shp_check_one_mines(struct ulist *mlp)
327 getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, §);
328 if (sect.sct_type != SCT_WATER)
332 if (chance(DMINE_HITCHANCE(sect.sct_mines))) {
333 actor = mlp->unit.ship.shp_own;
334 shp_hit_mine(&mlp->unit.ship);
336 if (map_set(actor, sect.sct_x, sect.sct_y, 'X', 0))
339 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
340 if (!mlp->unit.ship.shp_own)
347 shp_check_mines(struct emp_qelem *ship_list)
349 struct emp_qelem *qp;
350 struct emp_qelem *next;
354 for (qp = ship_list->q_back; qp != ship_list; qp = next) {
356 mlp = (struct ulist *)qp;
357 if (shp_check_one_mines(mlp)) {
368 shp_stays(natid actor, char *str, struct ulist *mlp)
370 mpr(actor, "%s %s & stays in %s\n",
371 prship(&mlp->unit.ship), str,
372 xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, actor));
373 shp_nav_put_one(mlp);
377 * Return whether and why SP would be stuck in SECTP.
380 shp_check_nav(struct shpstr *sp, struct sctstr *sectp)
382 switch (dchr[sectp->sct_type].d_nav) {
386 if (!(mchr[sp->shp_type].m_flags & M_CANAL)) {
387 return SHP_STUCK_CANAL;
391 if (sectp->sct_effic < 2)
392 return SHP_STUCK_CONSTRUCTION;
395 if (sectp->sct_effic < 60)
396 return SHP_STUCK_CONSTRUCTION;
402 return SHP_STUCK_IMPASSABLE;
404 return SHP_STUCK_NOT;
408 sect_has_dock(struct sctstr *sect)
410 switch (dchr[sect->sct_type].d_nav) {
420 shp_count(struct emp_qelem *list, int wantflags, int nowantflags,
423 struct emp_qelem *qp;
424 struct emp_qelem *next;
428 for (qp = list->q_back; qp != list; qp = next) {
430 mlp = (struct ulist *)qp;
431 if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
434 (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
437 mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
445 shp_damage_one(struct ulist *mlp, int dam)
447 /* ship might have changed (launched interceptors, missile defense) */
448 getship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
449 shipdamage(&mlp->unit.ship, dam);
450 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
451 if (!mlp->unit.ship.shp_own) {
452 emp_remque(&mlp->queue);
458 shp_damage(struct emp_qelem *list, int totdam, int wantflags,
459 int nowantflags, int x, int y)
461 struct emp_qelem *qp;
462 struct emp_qelem *next;
468 || !(count = shp_count(list, wantflags, nowantflags, x, y)))
470 dam = ldround((double)totdam / count, 1);
471 for (qp = list->q_back; qp != list; qp = next) {
473 mlp = (struct ulist *)qp;
474 if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
477 (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
480 mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
482 shp_damage_one(mlp, dam);
488 shp_contains(struct emp_qelem *list, int newx, int newy, int wantflags,
491 struct emp_qelem *qp;
492 struct emp_qelem *next;
495 for (qp = list->q_back; qp != list; qp = next) {
497 mlp = (struct ulist *)qp;
498 /* If the ship isn't in the requested sector, then continue */
499 if (newx != mlp->unit.ship.shp_x || newy != mlp->unit.ship.shp_y)
502 (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
505 mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
512 static struct ulist *
513 most_valuable_ship(struct emp_qelem *list, coord x, coord y)
515 struct emp_qelem *qp;
516 struct emp_qelem *next;
518 struct ulist *mvs = NULL;
520 for (qp = list->q_back; qp != list; qp = next) {
522 mlp = (struct ulist *)qp;
523 if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
525 if (mchr[mlp->unit.ship.shp_type].m_flags & M_SUB)
527 if (!mchr[mlp->unit.ship.shp_type].m_nxlight &&
528 !mchr[mlp->unit.ship.shp_type].m_nchoppers &&
529 mchr[mlp->unit.ship.shp_type].m_cost < 1000 &&
530 !mchr[mlp->unit.ship.shp_type].m_nplanes &&
531 !mchr[mlp->unit.ship.shp_type].m_nland)
537 if (mchr[mlp->unit.ship.shp_type].m_cost * mlp->unit.ship.shp_effic >
538 mchr[mvs->unit.ship.shp_type].m_cost * mvs->unit.ship.shp_effic)
545 shp_easiest_target(struct emp_qelem *list, int wantflags, int nowantflags)
547 struct emp_qelem *qp;
548 struct emp_qelem *next;
551 int easiest = 9876; /* things start great for victim */
554 for (qp = list->q_back; qp != list; qp = next) {
556 mlp = (struct ulist *)qp;
558 (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
561 mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
563 hard = shp_hardtarget(&mlp->unit.ship);
565 easiest = hard; /* things get worse for victim */
568 return easiest - count;
572 shp_missile_interdiction(struct emp_qelem *list, coord newx, coord newy,
577 struct emp_qelem msl_list, *qp, *newqp;
581 mvs = most_valuable_ship(list, newx, newy);
585 msl_sel(&msl_list, newx, newy, victim, P_T | P_MAR, 0, MI_INTERDICT);
587 for (qp = msl_list.q_back; qp != &msl_list; qp = newqp) {
589 plp = (struct plist *)qp;
591 if (mvs && mission_pln_equip(plp, NULL, 'p') >= 0) {
592 if (msl_launch(&plp->plane, EF_SHIP, prship(&mvs->unit.ship),
593 newx, newy, victim, &sublaunch) < 0)
596 if (msl_hit(&plp->plane,
597 shp_hardtarget(&mvs->unit.ship), EF_SHIP,
598 N_SHP_MISS, N_SHP_SMISS, sublaunch, victim)) {
599 dam = pln_damage(&plp->plane, 'p', 1);
601 "missile interdiction mission does %d damage to %s!\n",
602 dam, prship(&mvs->unit.ship));
603 shp_damage_one(mvs, dam);
605 dam = pln_damage(&plp->plane, 'p', 0);
606 collateral_damage(newx, newy, dam);
608 mvs = most_valuable_ship(list, newx, newy);
610 plp->plane.pln_effic = 0;
611 putplane(plp->plane.pln_uid, &plp->plane);
620 /* Note that this function has a side effect - it uses coastwatch
621 * ranges to see if it should fire upon a ship. So, this function
622 * is expected to return positive if a ship is in range, and 0 if a
623 * ship is not in range. */
625 notify_coastguard(struct emp_qelem *list, int trange, struct sctstr *sectp)
627 struct emp_qelem *qp;
628 struct emp_qelem *next;
633 natp = getnatp(sectp->sct_own);
635 vrange = sectp->sct_type == SCT_RADAR ? 14 : 4;
636 vrange *= tfact(sectp->sct_own, 1.0) * sectp->sct_effic / 100.0;
644 for (qp = list->q_back; qp != list; qp = next) {
646 mlp = (struct ulist *)qp;
647 if (mchr[mlp->unit.ship.shp_type].m_flags & M_SUB)
649 if (natp->nat_flags & NF_COASTWATCH)
650 wu(0, sectp->sct_own,
651 "%s %s sighted at %s\n",
652 cname(mlp->unit.ship.shp_own),
653 prship(&mlp->unit.ship),
654 xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
657 setcont(sectp->sct_own, mlp->unit.ship.shp_own, FOUND_COAST);
664 shp_fort_interdiction(struct emp_qelem *list, coord newx, coord newy,
673 signed char notified[MAXNOC];
676 /* Inform neutral and worse */
677 for (i = 0; i < MAXNOC; ++i) {
678 if (relations_with(i, victim) <= NEUTRAL)
684 snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
685 while (nxtsct(&ns, &fsect)) {
688 if (fsect.sct_own == victim)
690 if (notified[fsect.sct_own])
692 trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
693 if (notify_coastguard(list, trange, &fsect))
694 notified[fsect.sct_own] = 1;
696 if (opt_NO_FORT_FIRE)
697 return 0; /* Only coastwatch notify in nofortfire */
698 /* Only fire at Hostile ships */
699 for (i = 0; i < MAXNOC; ++i) {
700 if (relations_with(i, victim) >= NEUTRAL)
703 snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
704 while (nxtsct(&ns, &fsect)) {
705 if (!notified[fsect.sct_own])
707 range = roundrange(fortrange(&fsect));
708 trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
711 dam = fort_fire(&fsect);
717 mpr(victim, "Incoming fire does %d damage!\n", dam);
719 mpr(victim, "%s fires at you for %d!\n",
720 xyas(fsect.sct_x, fsect.sct_y, victim), dam);
723 "%s fires at %s ships in %s for %d!\n",
724 xyas(fsect.sct_x, fsect.sct_y,
726 cname(victim), xyas(newx, newy, fsect.sct_own), dam);
727 nreport(fsect.sct_own, N_SHP_SHELL, victim, 1);
730 shp_damage(list, totdam, 0, M_SUB, newx, newy);
735 shp_mission_interdiction(struct emp_qelem *list, coord x, coord y,
736 natid victim, int subs)
738 char *what = subs ? "subs" : "ships";
739 int wantflags = subs ? M_SUB : 0;
740 int nowantflags = subs ? 0 : M_SUB;
741 int mission = subs ? MI_SINTERDICT : MI_INTERDICT;
744 dam = unit_interdict(x, y, victim, what,
745 shp_easiest_target(list, wantflags, nowantflags),
748 shp_damage(list, dam, wantflags, nowantflags, x, y);
753 shp_interdict(struct emp_qelem *list, coord newx, coord newy, natid victim)
757 if (shp_contains(list, newx, newy, 0, M_SUB)) {
758 stopping |= shp_fort_interdiction(list, newx, newy, victim);
760 if (shp_contains(list, newx, newy, 0, M_SUB)) {
761 stopping |= shp_mission_interdiction(list, newx, newy, victim, 0);
762 stopping |= shp_missile_interdiction(list, newx, newy, victim);
765 if (shp_contains(list, newx, newy, M_SUB, 0))
766 stopping |= shp_mission_interdiction(list, newx, newy, victim, 1);
770 /* high value of hardtarget is harder to hit */
772 shp_hardtarget(struct shpstr *sp)
776 struct mchrstr *mcp = mchr + sp->shp_type;
779 getsect(sp->shp_x, sp->shp_y, §);
780 onsea = sect.sct_type == SCT_WATER;
781 if (mcp->m_flags & M_SUB)
783 return (int)((sp->shp_effic / 100.0) *
784 (20 + shp_speed(sp) * onsea / 2.0 - vis));
788 shp_hit_mine(struct shpstr *sp)
792 mpr(sp->shp_own, "Kawhomp! Mine detected in %s!\n",
793 xyas(sp->shp_x, sp->shp_y, sp->shp_own));
795 nreport(sp->shp_own, N_HIT_MINE, 0, 1);
798 if (mchr[sp->shp_type].m_flags & M_SWEEP)
801 shipdamage(sp, ldround(m, 1));
807 shp_nav_one_sector(struct emp_qelem *list, int dir, natid actor)
810 struct emp_qelem *qp;
811 struct emp_qelem *next;
818 enum shp_stuck stuck;
823 if (CANT_HAPPEN(QEMPTY(list)))
826 if (dir <= DIR_STOP || dir > DIR_LAST) {
827 CANT_HAPPEN(dir != DIR_STOP);
828 shp_nav_put(list, actor);
834 mlp = (struct ulist *)list->q_back;
835 newx = xnorm(mlp->unit.ship.shp_x + dx);
836 newy = ynorm(mlp->unit.ship.shp_y + dy);
837 getsect(newx, newy, §);
839 if (sect.sct_own && relations_with(sect.sct_own, actor) < FRIENDLY) {
840 mpr(actor, "can't go to %s\n", xyas(newx, newy, actor));
845 for (qp = list->q_back; qp != list; qp = next) {
847 mlp = (struct ulist *)qp;
848 switch (shp_check_nav(&mlp->unit.ship, §)) {
852 case SHP_STUCK_CANAL:
857 case SHP_STUCK_CONSTRUCTION:
858 case SHP_STUCK_IMPASSABLE:
859 mpr(actor, "can't go to %s\n", xyas(newx, newy, actor));
864 mpr(actor, "is too large to fit into the canal system at %s\n",
865 xyas(newx, newy, actor));
869 for (qp = list->q_back; qp != list; qp = next) {
871 mlp = (struct ulist *)qp;
872 stuck = shp_check_nav(&mlp->unit.ship, §);
873 if (stuck == SHP_STUCK_CANAL) {
875 "is too large to fit into the canal system at %s",
876 xyas(newx, newy, actor));
877 shp_stays(actor, dp, mlp);
879 } else if (CANT_HAPPEN(stuck != SHP_STUCK_NOT)) {
880 sprintf(dp, "can't go to %s", xyas(newx, newy, actor));
881 shp_stays(actor, dp, mlp);
885 if (mlp->mobil <= 0.0) {
886 shp_stays(actor, "is out of mobility", mlp);
889 mobcost = shp_mobcost(&mlp->unit.ship);
890 mlp->unit.ship.shp_x = newx;
891 mlp->unit.ship.shp_y = newy;
892 if (mlp->mobil - mobcost < -127) {
895 mlp->mobil -= mobcost;
897 mlp->unit.ship.shp_mobil = (int)mlp->mobil;
898 putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
900 /* Now update the map for this ship */
901 rad_map_set(mlp->unit.ship.shp_own,
902 mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
903 mlp->unit.ship.shp_effic, mlp->unit.ship.shp_tech,
904 mchr[mlp->unit.ship.shp_type].m_vrnge);
908 stopping |= shp_sweep(list, 0, 0, actor);
911 stopping |= shp_check_mines(list);
914 stopping |= shp_interdict(list, newx, newy, actor);
921 * Check for incoming missiles with a P_MAR flag.
922 * Return True=1 if the missile was shotdown.
925 * Chad Zabel, July 95
929 shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
934 double gun, eff, teff;
936 snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
938 while (nxtitem(&ni, &ship)) {
942 if (!(mchr[(int)ship.shp_type].m_flags & M_ANTIMISSILE))
945 if (relations_with(ship.shp_own, bombown) >= NEUTRAL)
948 if (ship.shp_effic < 60)
951 if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */
953 if (ship.shp_item[I_GUN] < 1) /* we need at least 1 gun */
955 if (!shp_supply(&ship, I_SHELL, 2))
957 ship.shp_item[I_SHELL] -= 2;
958 putship(ship.shp_uid, &ship);
960 /* now calculate the odds */
961 gun = shp_usable_guns(&ship);
962 eff = ship.shp_effic / 100.0;
963 teff = ship.shp_tech / (ship.shp_tech + 200.0);
964 /* raise 4.5 for better interception -KHS */
965 hitchance = (int)(gun * eff * teff * 4.5) - hardtarget;
966 hitchance = LIMIT_TO(hitchance, 0, 100);
967 hit = pct_chance(hitchance);
969 mpr(bombown, "%s anti-missile system activated...%s\n",
971 hit ? "KABOOOM!! Missile destroyed\n"
972 : "SWOOSH!! anti-missile system failed!!");
973 mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n",
975 mpr(ship.shp_own, "%d%% hitchance...%s\n", hitchance,
976 hit ? "KABOOOM!! Incoming missile destroyed!\n"
977 : "SWOOSH!! Missile evades anti-missile systems\n");
982 return 0; /* all attempts failed */
986 /* Fire missiles at a ship which has fired shells */
988 shp_missdef(struct shpstr *sp, natid victim)
990 struct emp_qelem list;
996 mlp = shp_insque(sp, &list);
997 sprintf(buf, "%s", prship(&mlp->unit.ship));
1000 shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
1001 getship(sp->shp_uid, sp);
1005 "missiles launched in defense did 100%% damage to %s\n",
1007 wu(0, victim, "%s sunk!\n", buf);
1008 } else if (eff > 0 && sp->shp_effic < eff) {
1010 "missiles launched in defense did %d%% damage to %s\n",
1011 100 * (eff - sp->shp_effic) / eff, buf);
1018 shp_mobcost(struct shpstr *sp)
1020 return speed_factor(sp->shp_effic * 0.01 * shp_speed(sp),
1025 * Set SP's tech to TLEV along with everything else that depends on it.
1028 shp_set_tech(struct shpstr *sp, int tlev)
1030 struct mchrstr *mcp = mchr + sp->shp_type;
1032 if (CANT_HAPPEN(tlev < mcp->m_tech))
1035 sp->shp_tech = tlev;