1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2021, 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  *  shpsub.c: Ship subroutine stuff
28  *
29  *  Known contributors to this file:
30  *     Ken Stevens, 1995
31  *     Steve McClure, 1996-2000
32  *     Markus Armbruster, 2006-2015
33  */
34 
35 #include <config.h>
36 
37 #include <stdlib.h>
38 #include "chance.h"
39 #include "damage.h"
40 #include "empobj.h"
41 #include "map.h"
42 #include "misc.h"
43 #include "mission.h"
44 #include "news.h"
45 #include "nsc.h"
46 #include "optlist.h"
47 #include "path.h"
48 #include "player.h"
49 #include "prototypes.h"
50 #include "queue.h"
51 #include "unit.h"
52 #include "xy.h"
53 
54 static void shp_nav_put_one(struct ulist *);
55 static int shp_check_one_mines(struct ulist *);
56 static int shp_hit_mine(struct shpstr *);
57 
58 static struct ulist *
shp_find_capable(struct emp_qelem * list,int flags)59 shp_find_capable(struct emp_qelem *list, int flags)
60 {
61     struct emp_qelem *qp;
62     struct ulist *mlp;
63 
64     for (qp = list->q_back; qp != list; qp = qp->q_back) {
65 	mlp = (struct ulist *)qp;
66 	if (mchr[mlp->unit.ship.shp_type].m_flags & flags)
67 	    return mlp;
68     }
69     return NULL;
70 }
71 
72 int
shp_may_nav(struct shpstr * sp,struct shpstr * flg,char * suffix)73 shp_may_nav(struct shpstr *sp, struct shpstr *flg, char *suffix)
74 {
75     struct sctstr sect;
76 
77     if (!sp->shp_own || !getsect(sp->shp_x, sp->shp_y, &sect)) {
78 	CANT_REACH();
79 	return 0;
80     }
81 
82     if (opt_MARKET && ontradingblock(EF_SHIP, sp)) {
83 	mpr(sp->shp_own, "%s is on the trading block%s\n",
84 	    prship(sp), suffix);
85 	return 0;
86     }
87 
88     if (sp->shp_item[I_MILIT] == 0 && sp->shp_item[I_CIVIL] == 0) {
89 	mpr(sp->shp_own, "%s is crewless%s\n", prship(sp), suffix);
90 	return 0;
91     }
92 
93     switch (shp_check_nav(sp, &sect)) {
94     case SHP_STUCK_NOT:
95 	break;
96     case SHP_STUCK_CONSTRUCTION:
97 	mpr(sp->shp_own, "%s is caught in a construction zone%s\n",
98 	    prship(sp), suffix);
99 	return 0;
100     default:
101 	CANT_REACH();
102 	/* fall through */
103     case SHP_STUCK_CANAL:
104     case SHP_STUCK_IMPASSABLE:
105 	mpr(sp->shp_own, "%s is landlocked%s\n", prship(sp), suffix);
106 	return 0;
107     }
108 
109     if (flg && (sp->shp_x != flg->shp_x || sp->shp_y != flg->shp_y)) {
110 	mpr(sp->shp_own, "%s is not with the flagship%s\n",
111 	    prship(sp), suffix);
112 	return 0;
113     }
114     return 1;
115 }
116 
117 void
shp_sel(struct nstr_item * ni,struct emp_qelem * list)118 shp_sel(struct nstr_item *ni, struct emp_qelem *list)
119 {
120     struct shpstr ship, *flg = NULL;
121     struct ulist *mlp;
122 
123     emp_initque(list);
124     while (nxtitem(ni, &ship)) {
125 	/*
126 	 * It would be nice to let deities navigate foreign ships, but
127 	 * much of the code assumes that only the ship's owner can
128 	 * navigate it.
129 	 */
130 	if (!ship.shp_own || ship.shp_own != player->cnum)
131 	    continue;
132 	if (!shp_may_nav(&ship, flg, ""))
133 	    continue;
134 
135 	ship.shp_mission = 0;
136 	ship.shp_rflags = 0;
137 	memset(ship.shp_rpath, 0, sizeof(ship.shp_rpath));
138 	putship(ship.shp_uid, &ship);
139 	mlp = shp_insque(&ship, list);
140 	if (!flg)
141 	    flg = &mlp->unit.ship;
142     }
143 }
144 
145 /*
146  * Append @sp to @list.
147  * Return the new list link.
148  */
149 struct ulist *
shp_insque(struct shpstr * sp,struct emp_qelem * list)150 shp_insque(struct shpstr *sp, struct emp_qelem *list)
151 {
152     struct ulist *mlp = malloc(sizeof(struct ulist));
153 
154     mlp->unit.ship = *sp;
155     mlp->mobil = sp->shp_mobil;
156     emp_insque(&mlp->queue, list);
157     return mlp;
158 }
159 
160 void
shp_nav_stay_behind(struct emp_qelem * list,natid actor)161 shp_nav_stay_behind(struct emp_qelem *list, natid actor)
162 {
163     struct emp_qelem *qp;
164     struct emp_qelem *next;
165     struct ulist *mlp;
166     struct shpstr *sp, *flg = NULL;
167     char and_stays[32];
168 
169     for (qp = list->q_back; qp != list; qp = next) {
170 	next = qp->q_back;
171 	mlp = (struct ulist *)qp;
172 	sp = &mlp->unit.ship;
173 	getship(sp->shp_uid, sp);
174 
175 	if (sp->shp_own != actor) {
176 	    mpr(actor, "%s was sunk at %s\n",
177 		prship(sp), xyas(sp->shp_x, sp->shp_y, actor));
178 	    emp_remque(&mlp->queue);
179 	    free(mlp);
180 	    continue;
181 	}
182 
183 	snprintf(and_stays, sizeof(and_stays), " & stays in %s",
184 		 xyas(sp->shp_x, sp->shp_y, actor));
185 	if (!shp_may_nav(sp, flg, and_stays)) {
186 	    shp_nav_put_one(mlp);
187 	    continue;
188 	}
189 
190 	if (!flg)
191 	    flg = sp;
192 	if (sp->shp_mobil + 1 < (int)mlp->mobil) {
193 	    mlp->mobil = sp->shp_mobil;
194 	}
195     }
196 }
197 
198 void
shp_nav_put(struct emp_qelem * list,natid actor)199 shp_nav_put(struct emp_qelem *list, natid actor)
200 {
201     struct emp_qelem *qp, *next;
202     struct ulist *mlp;
203     struct shpstr *sp;
204 
205     for (qp = list->q_back; qp != list; qp = next) {
206 	next = qp->q_back;
207 	mlp = (struct ulist *)qp;
208 	sp = &mlp->unit.ship;
209 	mpr(actor, "%s stopped at %s\n",
210 	    prship(sp), xyas(sp->shp_x, sp->shp_y, actor));
211 	shp_nav_put_one(mlp);
212     }
213 }
214 
215 static void
shp_nav_put_one(struct ulist * mlp)216 shp_nav_put_one(struct ulist *mlp)
217 {
218     mlp->unit.ship.shp_mobil = (int)mlp->mobil;
219     putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
220     emp_remque(&mlp->queue);
221     free(mlp);
222 }
223 
224 /*
225  * Sweep sea mines with engineers in @ship_list for @actor.
226  * All ships in @ship_list must be in the same sector.
227  * If @explicit is non-zero, this is for an explicit sweep command from
228  * a player.  Else it's an automatic "on the move" sweep.
229  * If @takemob is non-zero, require and charge mobility.
230  * Return non-zero when the ships should stop.
231  */
232 int
shp_sweep(struct emp_qelem * ship_list,int explicit,int takemob,natid actor)233 shp_sweep(struct emp_qelem *ship_list, int explicit, int takemob,
234 	  natid actor)
235 {
236     struct emp_qelem *qp;
237     struct emp_qelem *next;
238     struct ulist *mlp;
239     struct sctstr sect;
240     int mines, m, max, shells;
241     int changed = 0;
242     int stopping = 0, first = 1;
243 
244     mlp = shp_find_capable(ship_list, M_SWEEP);
245     if (!mlp) {
246 	if (explicit)
247 	    mpr(actor, "No minesweepers!\n");
248 	return 0;
249     }
250 
251     getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, &sect);
252     if (sect.sct_type != SCT_WATER) {
253 	if (explicit)
254 	    mpr(actor, "%s is a %s.  No sea mines there!\n",
255 	       xyas(sect.sct_x, sect.sct_y, actor),
256 	       dchr[sect.sct_type].d_name);
257 	return 0;
258     }
259 
260     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
261 	next = qp->q_back;
262 	mlp = (struct ulist *)qp;
263 	if (!(mchr[mlp->unit.ship.shp_type].m_flags & M_SWEEP))
264 	    continue;
265 	if (takemob) {
266 	    if (mlp->mobil <= 0.0) {
267 		if (explicit)
268 		    mpr(actor, "%s is out of mobility!\n",
269 			prship(&mlp->unit.ship));
270 		continue;
271 	    }
272 	    mlp->mobil -= shp_mobcost(&mlp->unit.ship);
273 	    mlp->unit.ship.shp_mobil = (int)mlp->mobil;
274 	}
275 	putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
276 	getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, &sect);
277 	if (!(mines = sect.sct_mines))
278 	    continue;
279 	max = mchr[mlp->unit.ship.shp_type].m_item[I_SHELL];
280 	shells = mlp->unit.ship.shp_item[I_SHELL];
281 	for (m = 0; mines > 0 && m < 5; m++) {
282 	    if (chance(0.66)) {
283 		if (first) {
284 		    mpr(actor, "Approaching minefield at %s...\n",
285 			xyas(sect.sct_x, sect.sct_y, actor));
286 		    first = 0;
287 		}
288 		mpr(actor, "Sweep...\n");
289 		mines--;
290 		shells = MIN(max, shells + 1);
291 		changed |= map_set(actor, sect.sct_x, sect.sct_y, 'X', 0);
292 	    }
293 	}
294 	sect.sct_mines = mines;
295 	mlp->unit.ship.shp_item[I_SHELL] = shells;
296 	putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
297 	putsect(&sect);
298 	stopping |= shp_check_one_mines(mlp);
299     }
300     if (changed)
301 	writemap(actor);
302     return stopping;
303 }
304 
305 static int
shp_check_one_mines(struct ulist * mlp)306 shp_check_one_mines(struct ulist *mlp)
307 {
308     struct sctstr sect;
309     int actor;
310 
311     getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, &sect);
312     if (sect.sct_type != SCT_WATER)
313 	return 0;
314     if (!sect.sct_mines)
315 	return 0;
316     if (chance(DMINE_HITCHANCE(sect.sct_mines))) {
317 	actor = mlp->unit.ship.shp_own;
318 	shp_hit_mine(&mlp->unit.ship);
319 	sect.sct_mines--;
320 	if (map_set(actor, sect.sct_x, sect.sct_y, 'X', 0))
321 	    writemap(actor);
322 	putsect(&sect);
323 	putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
324 	if (!mlp->unit.ship.shp_own) {
325 	    emp_remque(&mlp->queue);
326 	    free(mlp);
327 	}
328 	return 1;
329     }
330     return 0;
331 }
332 
333 static int
shp_check_mines(struct emp_qelem * ship_list)334 shp_check_mines(struct emp_qelem *ship_list)
335 {
336     struct emp_qelem *qp;
337     struct emp_qelem *next;
338     int stopping = 0;
339 
340     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
341 	next = qp->q_back;
342 	stopping |= shp_check_one_mines((struct ulist *)qp);
343     }
344     return stopping;
345 }
346 
347 /*
348  * Return whether and why @sp would be stuck in @sectp.
349  */
350 enum shp_stuck
shp_check_nav(struct shpstr * sp,struct sctstr * sectp)351 shp_check_nav(struct shpstr *sp, struct sctstr *sectp)
352 {
353     switch (dchr[sectp->sct_type].d_nav) {
354     case NAVOK:
355 	break;
356     case NAV_CANAL:
357 	if (!(mchr[sp->shp_type].m_flags & M_CANAL)) {
358 	    return SHP_STUCK_CANAL;
359 	}
360 	/* fall through */
361     case NAV_02:
362 	if (sectp->sct_effic < 2)
363 	    return SHP_STUCK_CONSTRUCTION;
364 	break;
365     case NAV_60:
366 	if (sectp->sct_effic < 60)
367 	    return SHP_STUCK_CONSTRUCTION;
368 	break;
369     default:
370 	CANT_REACH();
371 	/* fall through */
372     case NAV_NONE:
373 	return SHP_STUCK_IMPASSABLE;
374     }
375     return SHP_STUCK_NOT;
376 }
377 
378 int
sect_has_dock(struct sctstr * sect)379 sect_has_dock(struct sctstr *sect)
380 {
381     switch (dchr[sect->sct_type].d_nav) {
382     case NAV_02:
383     case NAV_CANAL:
384 	return 1;
385     default:
386 	return 0;
387     }
388 }
389 
390 static int
shp_count(struct emp_qelem * list,int wantflags,int nowantflags,int x,int y)391 shp_count(struct emp_qelem *list, int wantflags, int nowantflags,
392 	  int x, int y)
393 {
394     struct emp_qelem *qp;
395     struct emp_qelem *next;
396     struct ulist *mlp;
397     int count = 0;
398 
399     for (qp = list->q_back; qp != list; qp = next) {
400 	next = qp->q_back;
401 	mlp = (struct ulist *)qp;
402 	if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
403 	    continue;
404 	if (wantflags &&
405 	    (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
406 	    continue;
407 	if (nowantflags &&
408 	    mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
409 	    continue;
410 	++count;
411     }
412     return count;
413 }
414 
415 static void
shp_damage_one(struct ulist * mlp,int dam)416 shp_damage_one(struct ulist *mlp, int dam)
417 {
418     /* ship might have changed (launched interceptors, missile defense) */
419     getship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
420     shipdamage(&mlp->unit.ship, dam);
421     putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
422     if (!mlp->unit.ship.shp_own) {
423 	emp_remque(&mlp->queue);
424 	free(mlp);
425     }
426 }
427 
428 static int
shp_damage(struct emp_qelem * list,int totdam,int wantflags,int nowantflags,int x,int y)429 shp_damage(struct emp_qelem *list, int totdam, int wantflags,
430 	   int nowantflags, int x, int y)
431 {
432     struct emp_qelem *qp;
433     struct emp_qelem *next;
434     struct ulist *mlp;
435     int dam;
436     int count;
437 
438     if (!totdam
439 	|| !(count = shp_count(list, wantflags, nowantflags, x, y)))
440 	return 0;
441     dam = ldround((double)totdam / count, 1);
442     for (qp = list->q_back; qp != list; qp = next) {
443 	next = qp->q_back;
444 	mlp = (struct ulist *)qp;
445 	if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
446 	    continue;
447 	if (wantflags &&
448 	    (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
449 	    continue;
450 	if (nowantflags &&
451 	    mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
452 	    continue;
453 	shp_damage_one(mlp, dam);
454     }
455     return dam;
456 }
457 
458 static int
shp_contains(struct emp_qelem * list,int newx,int newy,int wantflags,int nowantflags)459 shp_contains(struct emp_qelem *list, int newx, int newy, int wantflags,
460 	     int nowantflags)
461 {
462     struct emp_qelem *qp;
463     struct emp_qelem *next;
464     struct ulist *mlp;
465 
466     for (qp = list->q_back; qp != list; qp = next) {
467 	next = qp->q_back;
468 	mlp = (struct ulist *)qp;
469 /* If the ship isn't in the requested sector, then continue */
470 	if (newx != mlp->unit.ship.shp_x || newy != mlp->unit.ship.shp_y)
471 	    continue;
472 	if (wantflags &&
473 	    (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
474 	    continue;
475 	if (nowantflags &&
476 	    mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
477 	    continue;
478 	return 1;
479     }
480     return 0;
481 }
482 
483 static struct ulist *
most_valuable_ship(struct emp_qelem * list,coord x,coord y)484 most_valuable_ship(struct emp_qelem *list, coord x, coord y)
485 {
486     struct emp_qelem *qp;
487     struct emp_qelem *next;
488     struct ulist *mlp;
489     struct ulist *mvs = NULL;
490 
491     for (qp = list->q_back; qp != list; qp = next) {
492 	next = qp->q_back;
493 	mlp = (struct ulist *)qp;
494 	if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
495 	    continue;
496 	if (mchr[mlp->unit.ship.shp_type].m_flags & M_SUB)
497 	    continue;
498 	if (!mchr[mlp->unit.ship.shp_type].m_nxlight &&
499 	    !mchr[mlp->unit.ship.shp_type].m_nchoppers &&
500 	    mchr[mlp->unit.ship.shp_type].m_cost < 1000 &&
501 	    !mchr[mlp->unit.ship.shp_type].m_nplanes &&
502 	    !mchr[mlp->unit.ship.shp_type].m_nland)
503 	    continue;
504 	if (!mvs) {
505 	    mvs = mlp;
506 	    continue;
507 	}
508 	if (mchr[mlp->unit.ship.shp_type].m_cost * mlp->unit.ship.shp_effic >
509 	    mchr[mvs->unit.ship.shp_type].m_cost * mvs->unit.ship.shp_effic)
510 	    mvs = mlp;
511     }
512     return mvs;
513 }
514 
515 static int
shp_easiest_target(struct emp_qelem * list,int wantflags,int nowantflags)516 shp_easiest_target(struct emp_qelem *list, int wantflags, int nowantflags)
517 {
518     struct emp_qelem *qp;
519     struct emp_qelem *next;
520     struct ulist *mlp;
521     int hard;
522     int easiest = 9876;		/* things start great for victim */
523     int count = 0;
524 
525     for (qp = list->q_back; qp != list; qp = next) {
526 	next = qp->q_back;
527 	mlp = (struct ulist *)qp;
528 	if (wantflags &&
529 	    (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
530 	    continue;
531 	if (nowantflags &&
532 	    mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
533 	    continue;
534 	hard = shp_hardtarget(&mlp->unit.ship);
535 	if (hard < easiest)
536 	    easiest = hard;	/* things get worse for victim */
537 	++count;
538     }
539     return easiest - count;
540 }
541 
542 static int
shp_missile_interdiction(struct emp_qelem * list,coord newx,coord newy,natid victim)543 shp_missile_interdiction(struct emp_qelem *list, coord newx, coord newy,
544 			 natid victim)
545 {
546     int dam, sublaunch;
547     int stopping = 0;
548     struct emp_qelem msl_list, *qp, *newqp;
549     struct plist *plp;
550     struct ulist *mvs;
551 
552     mvs = most_valuable_ship(list, newx, newy);
553     if (!mvs)
554 	return 0;
555 
556     msl_sel(&msl_list, newx, newy, victim, P_T | P_MAR, 0, MI_INTERDICT);
557 
558     for (qp = msl_list.q_back; qp != &msl_list; qp = newqp) {
559 	newqp = qp->q_back;
560 	plp = (struct plist *)qp;
561 
562 	if (mvs && mission_pln_equip(plp, NULL, 'p') >= 0) {
563 	    if (msl_launch(&plp->plane, EF_SHIP, prship(&mvs->unit.ship),
564 			   newx, newy, victim, &sublaunch) < 0)
565 		goto use_up_msl;
566 	    stopping = 1;
567 	    if (msl_hit(&plp->plane,
568 			shp_hardtarget(&mvs->unit.ship), EF_SHIP,
569 			N_SHP_MISS, N_SHP_SMISS, sublaunch, victim)) {
570 		dam = pln_damage(&plp->plane, 'p', "");
571 		mpr(victim,
572 		    "missile interdiction mission does %d damage to %s!\n",
573 		    dam, prship(&mvs->unit.ship));
574 		shp_damage_one(mvs, dam);
575 	    } else {
576 		dam = pln_damage(&plp->plane, 'p', NULL);
577 		collateral_damage(newx, newy, dam);
578 	    }
579 	    mvs = most_valuable_ship(list, newx, newy);
580 	use_up_msl:
581 	    plp->plane.pln_effic = 0;
582 	    putplane(plp->plane.pln_uid, &plp->plane);
583 	}
584 	emp_remque(qp);
585 	free(qp);
586     }
587 
588     return stopping;
589 }
590 
591 /* Note that this function has a side effect - it uses coastwatch
592  * ranges to see if it should fire upon a ship.  So, this function
593  * is expected to return positive if a ship is in range, and 0 if a
594  * ship is not in range. */
595 static int
notify_coastguard(struct emp_qelem * list,int trange,struct sctstr * sectp)596 notify_coastguard(struct emp_qelem *list, int trange, struct sctstr *sectp)
597 {
598     struct emp_qelem *qp;
599     struct emp_qelem *next;
600     struct ulist *mlp;
601     struct natstr *natp;
602     int vrange;
603 
604     natp = getnatp(sectp->sct_own);
605 
606     vrange = sectp->sct_type == SCT_RADAR ? 14 : 4;
607     vrange *= tfact(sectp->sct_own, 1.0) * sectp->sct_effic / 100.0;
608 
609     if (vrange < 1)
610 	vrange = 1;
611 
612     if (vrange < trange)
613 	return 0;
614 
615     for (qp = list->q_back; qp != list; qp = next) {
616 	next = qp->q_back;
617 	mlp = (struct ulist *)qp;
618 	if (mchr[mlp->unit.ship.shp_type].m_flags & M_SUB)
619 	    continue;
620 	if (natp->nat_flags & NF_COASTWATCH)
621 	    wu(0, sectp->sct_own,
622 	       "%s %s sighted at %s\n",
623 	       cname(mlp->unit.ship.shp_own),
624 	       prship(&mlp->unit.ship),
625 	       xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
626 		    sectp->sct_own));
627 	if (opt_HIDDEN)
628 	    setcont(sectp->sct_own, mlp->unit.ship.shp_own, FOUND_COAST);
629     }
630 
631     return 1;
632 }
633 
634 static int
shp_fort_interdiction(struct emp_qelem * list,coord newx,coord newy,natid victim)635 shp_fort_interdiction(struct emp_qelem *list, coord newx, coord newy,
636 		      natid victim)
637 {
638     struct nstr_sect ns;
639     struct sctstr fsect;
640     int trange, range;
641     int dam;
642     int stopping = 0;
643     int totdam = 0;
644     signed char notified[MAXNOC];
645     int i;
646 
647     /* Inform neutral and worse */
648     for (i = 0; i < MAXNOC; ++i) {
649 	if (relations_with(i, victim) <= NEUTRAL)
650 	    notified[i] = 0;
651 	else
652 	    notified[i] = 1;
653     }
654 
655     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
656     while (nxtsct(&ns, &fsect)) {
657 	if (!fsect.sct_own)
658 	    continue;
659 	if (fsect.sct_own == victim)
660 	    continue;
661 	if (notified[fsect.sct_own])
662 	    continue;
663 	trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
664 	if (notify_coastguard(list, trange, &fsect))
665 	    notified[fsect.sct_own] = 1;
666     }
667     if (opt_NO_FORT_FIRE)
668 	return 0;		/* Only coastwatch notify in nofortfire */
669     /* Only fire at Hostile ships */
670     for (i = 0; i < MAXNOC; ++i) {
671 	if (relations_with(i, victim) >= NEUTRAL)
672 	    notified[i] = 0;
673     }
674     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
675     while (nxtsct(&ns, &fsect)) {
676 	if (!notified[fsect.sct_own])
677 	    continue;
678 	range = roundrange(fortrange(&fsect));
679 	trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
680 	if (trange > range)
681 	    continue;
682 	dam = fort_fire(&fsect);
683 	putsect(&fsect);
684 	if (dam < 0)
685 	    continue;
686 	stopping = 1;
687 	totdam += dam;
688 	mpr(victim, "Incoming fire does %d damage!\n", dam);
689 #if 0
690 	mpr(victim, "%s fires at you for %d!\n",
691 	    xyas(fsect.sct_x, fsect.sct_y, victim), dam);
692 #endif
693 	wu(0, fsect.sct_own,
694 	   "%s fires at %s ships in %s for %d!\n",
695 	   xyas(fsect.sct_x, fsect.sct_y,
696 		fsect.sct_own),
697 	   cname(victim), xyas(newx, newy, fsect.sct_own), dam);
698 	nreport(fsect.sct_own, N_SHP_SHELL, victim, 1);
699     }
700     if (totdam > 0)
701 	shp_damage(list, totdam, 0, M_SUB, newx, newy);
702     return stopping;
703 }
704 
705 static int
shp_mission_interdiction(struct emp_qelem * list,coord x,coord y,natid victim,int subs)706 shp_mission_interdiction(struct emp_qelem *list, coord x, coord y,
707 			 natid victim, int subs)
708 {
709     char *what = subs ? "subs" : "ships";
710     int wantflags = subs ? M_SUB : 0;
711     int nowantflags = subs ? 0 : M_SUB;
712     int mission = subs ? MI_SINTERDICT : MI_INTERDICT;
713     int dam;
714 
715     dam = unit_interdict(x, y, victim, what,
716 			 shp_easiest_target(list, wantflags, nowantflags),
717 			 mission);
718     if (dam >= 0)
719 	shp_damage(list, dam, wantflags, nowantflags, x, y);
720     return dam >= 0;
721 }
722 
723 static int
shp_interdict(struct emp_qelem * list,coord newx,coord newy,natid victim)724 shp_interdict(struct emp_qelem *list, coord newx, coord newy, natid victim)
725 {
726     int stopping = 0;
727 
728     if (shp_contains(list, newx, newy, 0, M_SUB)) {
729 	stopping |= shp_fort_interdiction(list, newx, newy, victim);
730 
731 	if (shp_contains(list, newx, newy, 0, M_SUB)) {
732 	    stopping |= shp_mission_interdiction(list, newx, newy, victim, 0);
733 	    stopping |= shp_missile_interdiction(list, newx, newy, victim);
734 	}
735     }
736     if (shp_contains(list, newx, newy, M_SUB, 0))
737 	stopping |= shp_mission_interdiction(list, newx, newy, victim, 1);
738     return stopping;
739 }
740 
741 /* high value of hardtarget is harder to hit */
742 int
shp_hardtarget(struct shpstr * sp)743 shp_hardtarget(struct shpstr *sp)
744 {
745     struct sctstr sect;
746     int vis, onsea;
747     struct mchrstr *mcp = mchr + sp->shp_type;
748 
749     vis = shp_visib(sp);
750     getsect(sp->shp_x, sp->shp_y, &sect);
751     onsea = sect.sct_type == SCT_WATER;
752     if (mcp->m_flags & M_SUB)
753 	vis *= 4;
754     return (int)((sp->shp_effic / 100.0) *
755 		 (20 + shp_speed(sp) * onsea / 2.0 - vis));
756 }
757 
758 static int
shp_hit_mine(struct shpstr * sp)759 shp_hit_mine(struct shpstr *sp)
760 {
761     double m;
762 
763     mpr(sp->shp_own, "Kawhomp! Mine detected in %s!\n",
764 	xyas(sp->shp_x, sp->shp_y, sp->shp_own));
765 
766     nreport(sp->shp_own, N_HIT_MINE, 0, 1);
767 
768     m = MINE_DAMAGE();
769     if (mchr[sp->shp_type].m_flags & M_SWEEP)
770 	m /= 2.0;
771 
772     shipdamage(sp, ldround(m, 1));
773 
774     return (int)m;
775 }
776 
777 int
shp_nav_dir(struct emp_qelem * list,int dir,natid actor)778 shp_nav_dir(struct emp_qelem *list, int dir, natid actor)
779 {
780     struct sctstr sect;
781     struct emp_qelem *qp;
782     struct emp_qelem *next;
783     struct ulist *mlp;
784     coord dx;
785     coord dy;
786     coord newx;
787     coord newy;
788     int move;
789     enum shp_stuck stuck;
790     double mobcost;
791 
792     if (CANT_HAPPEN(QEMPTY(list)))
793 	return 1;
794 
795     if (dir <= DIR_STOP || dir > DIR_LAST) {
796 	CANT_HAPPEN(dir != DIR_STOP);
797 	shp_nav_put(list, actor);
798 	return 1;
799     }
800     dx = diroff[dir][0];
801     dy = diroff[dir][1];
802 
803     mlp = (struct ulist *)list->q_back;
804     newx = xnorm(mlp->unit.ship.shp_x + dx);
805     newy = ynorm(mlp->unit.ship.shp_y + dy);
806     getsect(newx, newy, &sect);
807 
808     if (sect.sct_own && relations_with(sect.sct_own, actor) < FRIENDLY) {
809 	mpr(actor, "can't go to %s\n", xyas(newx, newy, actor));
810 	return 1;
811     }
812 
813     move = 0;
814     for (qp = list->q_back; qp != list; qp = next) {
815 	next = qp->q_back;
816 	mlp = (struct ulist *)qp;
817 	switch (shp_check_nav(&mlp->unit.ship, &sect)) {
818 	case SHP_STUCK_NOT:
819 	    move = 1;
820 	    break;
821 	case SHP_STUCK_CANAL:
822 	    break;
823 	default:
824 	    CANT_REACH();
825 	    /* fall through */
826 	case SHP_STUCK_CONSTRUCTION:
827 	case SHP_STUCK_IMPASSABLE:
828 	    mpr(actor, "can't go to %s\n", xyas(newx, newy, actor));
829 	    return 1;
830 	}
831     }
832     if (!move) {
833 	mpr(actor, "is too large to fit into the canal system at %s\n",
834 	    xyas(newx, newy, actor));
835 	return 1;
836     }
837 
838     for (qp = list->q_back; qp != list; qp = next) {
839 	next = qp->q_back;
840 	mlp = (struct ulist *)qp;
841 	stuck = shp_check_nav(&mlp->unit.ship, &sect);
842 	if (stuck == SHP_STUCK_CANAL) {
843 	    mpr(actor,
844 		"%s is too large to fit into the canal system at %s"
845 		" & stays in %s\n",
846 		prship(&mlp->unit.ship), xyas(newx, newy, actor),
847 		xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, actor));
848 	    shp_nav_put_one(mlp);
849 	    continue;
850 	} else if (CANT_HAPPEN(stuck != SHP_STUCK_NOT)) {
851 	    mpr(actor, "%s can't go to %s & stays in %s\n",
852 		prship(&mlp->unit.ship), xyas(newx, newy, actor),
853 		xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, actor));
854 	    shp_nav_put_one(mlp);
855 	    continue;
856 	}
857 
858 	if (mlp->mobil <= 0.0) {
859 	    mpr(actor, "%s is out of mobility & stays in %s\n",
860 		prship(&mlp->unit.ship),
861 		xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, actor));
862 	    shp_nav_put_one(mlp);
863 	    continue;
864 	}
865 	mobcost = shp_mobcost(&mlp->unit.ship);
866 	mlp->unit.ship.shp_x = newx;
867 	mlp->unit.ship.shp_y = newy;
868 	if (mlp->mobil - mobcost < -127) {
869 	    mlp->mobil = -127;
870 	} else {
871 	    mlp->mobil -= mobcost;
872 	}
873 	mlp->unit.ship.shp_mobil = (int)mlp->mobil;
874 	putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
875     }
876 
877     return QEMPTY(list);
878 }
879 
880 int
shp_nav_gauntlet(struct emp_qelem * list,int interdict,natid actor)881 shp_nav_gauntlet(struct emp_qelem *list, int interdict, natid actor)
882 {
883     struct ulist *mlp = (struct ulist *)list->q_back;
884     coord newx, newy;
885     int stopping;
886 
887     if (CANT_HAPPEN(QEMPTY(list)))
888 	return 1;
889     newx = mlp->unit.ship.shp_x;
890     newy = mlp->unit.ship.shp_y;
891     stopping = shp_sweep(list, 0, 0, actor);
892     if (QEMPTY(list))
893 	return 1;
894     stopping |= shp_check_mines(list);
895     if (QEMPTY(list))
896 	return 1;
897     if (interdict)
898 	stopping |= shp_interdict(list, newx, newy, actor);
899 
900     return stopping;
901 }
902 
903 /*
904  * shp_miss_defence
905  * Check for incoming missiles with a P_MAR flag.
906  * Return True=1 if the missile was shotdown.
907  * Or False=0
908  *
909  * Chad Zabel, July 95
910  */
911 
912 int
shp_missile_defense(coord dx,coord dy,natid bombown,int hardtarget)913 shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
914 {
915     struct nstr_item ni;
916     struct shpstr ship;
917     int hitchance, hit;
918     double gun, eff, teff;
919 
920     snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
921 
922     while (nxtitem(&ni, &ship)) {
923 	if (!ship.shp_own)
924 	    continue;
925 
926 	if (!(mchr[(int)ship.shp_type].m_flags & M_ANTIMISSILE))
927 	    continue;
928 
929 	if (relations_with(ship.shp_own, bombown) >= NEUTRAL)
930 	    continue;
931 
932 	if (ship.shp_effic < 60)
933 	    continue;
934 
935 	if (ship.shp_item[I_MILIT] < 1)	/* do we have mil? */
936 	    continue;
937 	if (ship.shp_item[I_GUN] < 1)	/* we need at least 1 gun */
938 	    continue;
939 	if (!shp_supply(&ship, I_SHELL, 2))
940 	    continue;
941 	ship.shp_item[I_SHELL] -= 2;
942 	putship(ship.shp_uid, &ship);
943 
944 	/* now calculate the odds */
945 	gun = shp_usable_guns(&ship);
946 	eff = ship.shp_effic / 100.0;
947 	teff = ship.shp_tech / (ship.shp_tech + 200.0);
948 	/* raise 4.5 for better interception -KHS */
949 	hitchance = (int)(gun * eff * teff * 4.5) - hardtarget;
950 	hitchance = LIMIT_TO(hitchance, 0, 100);
951 	hit = pct_chance(hitchance);
952 
953 	mpr(bombown, "%s anti-missile system activated...%s\n",
954 	    cname(ship.shp_own),
955 	    hit ? "KABOOOM!! Missile destroyed\n"
956 	    : "SWOOSH!!  anti-missile system failed!!");
957 	mpr(ship.shp_own, "Ship #%d anti-missile system activated!\n",
958 	    ship.shp_uid);
959 	mpr(ship.shp_own, "%d%% hit chance...%s\n", hitchance,
960 	    hit ? "KABOOOM!!  Incoming missile destroyed!\n"
961 	    : "SWOOSH!!  Missile evades anti-missile systems\n");
962 
963 	if (hit)
964 	    return 1;
965     }
966     return 0;			/* all attempts failed */
967 }
968 
969 
970 /* Fire missiles at a ship which has fired shells */
971 void
shp_missdef(struct shpstr * sp,natid victim)972 shp_missdef(struct shpstr *sp, natid victim)
973 {
974     struct emp_qelem list;
975     struct ulist *mlp;
976     int eff;
977     char buf[512];
978 
979     emp_initque(&list);
980     mlp = shp_insque(sp, &list);
981     sprintf(buf, "%s", prship(&mlp->unit.ship));
982 
983     eff = sp->shp_effic;
984     shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
985     getship(sp->shp_uid, sp);
986 
987     if (!sp->shp_own) {
988 	wu(0, victim,
989 	   "missiles launched in defense did 100%% damage to %s\n",
990 	   buf);
991 	wu(0, victim, "%s sunk!\n", buf);
992     } else if (eff > 0 && sp->shp_effic < eff) {
993 	wu(0, victim,
994 	   "missiles launched in defense did %d%% damage to %s\n",
995 	   100 * (eff - sp->shp_effic) / eff, buf);
996     }
997     if (!QEMPTY(&list))
998 	free(mlp);
999 }
1000 
1001 double
shp_mobcost(struct shpstr * sp)1002 shp_mobcost(struct shpstr *sp)
1003 {
1004     return speed_factor(sp->shp_effic * 0.01 * shp_speed(sp),
1005 			sp->shp_tech);
1006 }
1007 
1008 /*
1009  * Set @sp's tech to @tlev along with everything else that depends on it.
1010  */
1011 void
shp_set_tech(struct shpstr * sp,int tlev)1012 shp_set_tech(struct shpstr *sp, int tlev)
1013 {
1014     struct mchrstr *mcp = mchr + sp->shp_type;
1015 
1016     if (CANT_HAPPEN(tlev < mcp->m_tech))
1017 	tlev = mcp->m_tech;
1018 
1019     sp->shp_tech = tlev;
1020 }
1021