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, §)) {
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, §)) {
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, §);
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, §);
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(§);
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, §);
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(§);
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, §);
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, §);
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, §)) {
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, §);
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