2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2020, 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 * multifire.c: Fire at other sectors/ships
29 * Known contributors to this file:
31 * Markus Armbruster, 2004-2016
43 enum targ_type { /* Targeting... */
44 targ_land, /* a sector with guns */
45 targ_ship, /* a ship with guns */
46 targ_sub, /* a submarine with depth charges */
47 targ_bogus /* a bogus sector with guns */
51 struct emp_qelem queue; /* list of fired things */
52 short type; /* EF_SECTOR, EF_SHIP or EF_LAND */
55 int defdam; /* damage defenders did */
59 static int defend(struct emp_qelem *, struct emp_qelem *,
60 struct empobj *, natid, int *);
61 static void do_defdam(struct emp_qelem *, double);
62 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
64 static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
65 static void free_flist(struct emp_qelem *);
66 static struct flist *search_flist(struct emp_qelem *, struct empobj *);
71 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
89 enum targ_type target;
90 struct nstr_item nbst;
96 union empobj_storage item;
97 struct emp_qelem fired, defended;
102 emp_initque(&defended);
103 p = getstarg(player->argp[1],
104 "Firing from ship(s), sect(s), or land unit(s)? ", buf);
107 type = ef_byname_from(p, ef_with_guns);
108 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
109 pr("Fort firing is disabled.\n");
113 pr("Ships, land units or sectors only!\n");
116 if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
119 while (nxtitem(&nbst, &item)) {
120 if (type == EF_LAND) {
121 if (!getland(item.land.lnd_uid, &fland))
123 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
125 if (item.land.lnd_own != player->cnum)
128 if (lchr[fland.lnd_type].l_dam == 0) {
129 pr("Unit %d cannot fire!\n", fland.lnd_uid);
132 if (fland.lnd_item[I_MILIT] < 1) {
133 pr("Unit %d cannot fire because it has no military!\n",
137 if (fland.lnd_ship >= 0) {
138 pr("Unit %d cannot fire because it is on a ship!\n",
142 if (fland.lnd_land >= 0) {
143 pr("Unit %d cannot fire because it is on a land unit!\n",
147 if (fland.lnd_effic < LAND_MINFIREEFF) {
148 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
149 fland.lnd_uid, LAND_MINFIREEFF);
152 if (fland.lnd_item[I_GUN] == 0) {
153 pr("%s -- not enough guns\n", prland(&fland));
157 if (fland.lnd_item[I_SHELL] == 0) {
158 pr("%s -- not enough shells\n", prland(&fland));
161 pr("%s%s ready to fire\n", sep, prland(&fland));
164 } else if (type == EF_SHIP) {
165 if (!getship(item.ship.shp_uid, &fship))
167 if (item.ship.shp_own != player->cnum)
169 if (item.ship.shp_item[I_MILIT] < 1) {
170 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
173 if (mchr[item.ship.shp_type].m_glim == 0) {
174 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
177 if (item.ship.shp_item[I_GUN] == 0) {
178 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
181 if (item.ship.shp_item[I_SHELL] == 0) {
182 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
185 if (item.ship.shp_effic < 60) {
186 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
189 pr("%s%s ready to fire\n", sep, prship(&fship));
193 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
195 if (item.sect.sct_own != player->cnum)
197 if (item.sect.sct_type != SCT_FORTR)
199 if (item.sect.sct_effic < FORTEFF) {
200 pr("Fort not efficient enough to fire!\n");
203 if (item.sect.sct_item[I_GUN] == 0) {
204 pr("Not enough guns in sector %s!\n",
205 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
208 if (item.sect.sct_item[I_SHELL] == 0) {
209 pr("Not enough shells in sector %s!\n",
210 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
213 if (item.sect.sct_item[I_MILIT] < 5) {
214 pr("Not enough military in sector %s!\n",
215 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
218 pr("%sSector %s ready to fire\n", sep,
219 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
225 ptr = getstarg(player->argp[3], "Firing at? ", buf);
230 if (!issector(ptr)) {
232 if (vshipno < 0 || !getship(vshipno, &vship) ||
234 pr("No such ship exists!\n");
237 target = targ_ship; /* targ_ship vs. targ_sub decided below */
238 vict = vship.shp_own;
241 if (!getsect(x, y, &vsect)) {
242 pr("No such sector exists!\n");
246 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
247 pr("No such sector exists!\n");
250 /* We check the sector type, but we only use it for damage, not
251 reporting. That way, you don't get extra information you wouldn't
252 normally get. Besides, what if they want to slam water? :) */
253 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
257 vict = vsect.sct_own;
262 trange = mapdist(x, y, fx, fy);
264 if (type == EF_SHIP) {
265 if (!check_ship_ok(&fship))
267 if (target == targ_ship) {
268 if (fship.shp_uid == vship.shp_uid) {
269 pr("You can't fire upon yourself!\n");
273 range = shp_fire_range(&fship);
274 range2 = roundrange(range);
275 /* Use depth charges against subs, but only when in range */
276 if (target == targ_ship && trange <= range2
277 && (mchr[vship.shp_type].m_flags & M_SUB)
278 && (mchr[fship.shp_type].m_flags & M_DCH))
280 if (target == targ_sub)
281 dam = shp_dchrg(&fship);
283 dam = shp_fire(&fship);
284 fship.shp_mission = 0;
285 putship(fship.shp_uid, &fship);
286 if (opt_NOMOBCOST == 0) {
287 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
288 putship(fship.shp_uid, &fship);
290 } else if (type == EF_LAND) {
291 if (!check_land_ok(&fland))
293 if (target == targ_land) {
294 if (fland.lnd_x == vsect.sct_x
295 && fland.lnd_y == vsect.sct_y) {
296 pr("You can't fire upon yourself!\n");
300 range = lnd_fire_range(&fland);
301 range2 = roundrange(range);
302 dam = lnd_fire(&fland);
303 fland.lnd_mission = 0;
304 putland(fland.lnd_uid, &fland);
305 if (target == targ_ship) {
306 if (chance(lnd_acc(&fland) / 100.0))
307 dam = ldround(dam / 2.0, 1);
310 if (!check_sect_ok(&fsect))
312 if (target == targ_land) {
313 if (fsect.sct_x == vsect.sct_x
314 && fsect.sct_y == vsect.sct_y) {
315 pr("You can't fire upon yourself!\n");
319 dam = fort_fire(&fsect);
321 range = fortrange(&fsect);
322 range2 = roundrange(range);
325 if (CANT_HAPPEN(dam < 0)) {
329 pr("range is %d.00 (%.2f)\n", range2, range);
343 * If the player fires guns at a submarine, take care not to
344 * disclose it's a submarine: pretend the target is out of range.
346 if (target == targ_ship && (mchr[vship.shp_type].m_flags & M_SUB))
348 if (trange > range2) {
349 pr("Target out of range.\n");
356 nreport(player->cnum, N_SCT_SHELL, vict, 1);
357 if (vict && vict != player->cnum)
359 "Country #%d shelled sector %s for %d damage.\n",
360 player->cnum, xyas(x, y, vict), dam);
361 pr("Shells hit sector %s for %d damage.\n",
362 xyas(x, y, player->cnum), dam);
365 nreport(player->cnum, N_SHP_SHELL, vict, 1);
368 if (vict && vict != player->cnum) {
370 "Country #%d shelled %s in %s for %d damage.\n",
371 player->cnum, prship(&vship),
372 xyas(vship.shp_x, vship.shp_y, vict), dam);
374 pr("Shells hit %s in %s for %d damage.\n",
376 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
379 /* Ok, now, check if we had a bogus target. If so,
380 just continue on, since there is no defender. */
381 if (target == targ_bogus)
384 if (type == EF_LAND) {
385 getsect(fland.lnd_x, fland.lnd_y, &fsect);
386 attgp = (struct empobj *)&fsect;
388 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
391 getsect(x, y, &vsect);
392 sectdamage(&vsect, dam);
396 getship(vshipno, &vship);
397 shipdamage(&vship, dam);
398 if (vship.shp_effic < SHIP_MINEFF)
399 pr("%s sunk!\n", prsub(&vship));
400 if (dam && (vship.shp_rflags & RET_INJURED))
401 retreat_ship(&vship, vict, 'i');
402 else if (target == targ_sub && (vship.shp_rflags & RET_DCHRGED))
403 retreat_ship(&vship, vict, 'd');
404 else if (totaldefdam == 0 && (vship.shp_rflags & RET_HELPLESS))
405 retreat_ship(&vship, vict, 'h');
406 putship(vship.shp_uid, &vship);
409 switch (attgp->ef_type) {
413 if ((target == targ_ship) || (target == targ_sub)) {
414 if (fship.shp_effic > SHIP_MINEFF) {
415 shp_missdef(&fship, vict);
424 free_flist(&defended);
426 odds = ((double)ndefending) / ((double)nfiring);
429 do_defdam(&fired, odds);
435 defend(struct emp_qelem *al, struct emp_qelem *dl,
436 struct empobj *attgp, natid vict, int *nd)
441 dam = quiet_bigdef(attgp->ef_type, dl, vict,
442 attgp->own, attgp->x, attgp->y, &nfiring);
446 add_to_flist(al, attgp, dam, vict);
453 do_defdam(struct emp_qelem *list, double odds)
461 struct emp_qelem *qp, *next;
463 for (qp = list->q_forw; qp != list; qp = next) {
465 fp = (struct flist *)qp;
466 if (fp->type == EF_SHIP) {
467 if (!getship(fp->uid, &ship) || !ship.shp_own)
472 pr("\nDefenders fire back!\n");
475 dam = odds * fp->defdam;
477 if (fp->type == EF_SHIP) {
479 pr("Return fire hit %s in %s for %d damage.\n",
481 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
484 "Return fire hit %s in %s for %d damage.\n",
485 prship(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
486 shipdamage(&ship, dam);
487 putship(ship.shp_uid, &ship);
489 CANT_HAPPEN(fp->type != EF_SECTOR);
490 getsect(fp->x, fp->y, §);
492 pr("Return fire hit sector %s for %d damage.\n",
493 xyas(fp->x, fp->y, player->cnum), dam);
494 sectdamage(§, dam);
497 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
498 xyas(fp->x, fp->y, vict), dam);
504 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
505 coord ax, coord ay, int *nfiring)
512 struct sctstr firing;
519 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
520 while (nxtitem(&ni, &ship)) {
521 if (!feels_like_helping(ship.shp_own, own, aown))
524 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
527 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
528 erange = torprange(&ship);
529 if (roundrange(erange) < ni.curdist)
531 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
533 fp = search_flist(list, (struct empobj *)&ship);
537 dam2 = shp_torp(&ship, 0);
538 putship(ship.shp_uid, &ship);
542 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
545 erange = shp_fire_range(&ship);
546 if (roundrange(erange) < ni.curdist)
548 fp = search_flist(list, (struct empobj *)&ship);
552 dam2 = shp_fire(&ship);
553 putship(ship.shp_uid, &ship);
557 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
561 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
564 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
565 while (nxtitem(&ni, &land)) {
566 if (!feels_like_helping(land.lnd_own, own, aown))
569 erange = lnd_fire_range(&land);
570 if (roundrange(erange) < ni.curdist)
573 fp = search_flist(list, (struct empobj *)&land);
577 dam2 = lnd_fire(&land);
578 putland(land.lnd_uid, &land);
585 add_to_flist(list, (struct empobj *)&land, dam2, 0);
586 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
587 if (type == EF_SHIP) {
588 if (chance(lnd_acc(&land) / 100.0))
589 dam2 = ldround(dam2 / 2.0, 1);
595 * Determine if any nearby gun-equipped sectors are within
596 * range and able to fire at an attacker. Firing sectors
597 * need to have guns, shells, and military. Sector being
598 * attacked is x,y -- attacker is at ax,ay.
601 if (!opt_NO_FORT_FIRE) {
602 snxtsct_dist(&ns, ax, ay, 8);
603 while (nxtsct(&ns, &firing)) {
604 if (!feels_like_helping(firing.sct_own, own, aown))
607 erange = fortrange(&firing);
608 if (roundrange(erange) < ns.curdist)
611 fp = search_flist(list, (struct empobj *)&firing);
615 dam2 = fort_fire(&firing);
622 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
623 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
628 return *nfiring == 0 ? 0 : dam / *nfiring;
632 add_to_flist(struct emp_qelem *list,
633 struct empobj *gp, int dam, natid victim)
637 fp = malloc(sizeof(struct flist));
638 fp->type = gp->ef_type;
644 emp_insque(&fp->queue, list);
648 free_flist(struct emp_qelem *list)
650 struct emp_qelem *qp, *next;
653 for (qp = list->q_forw; qp != list; qp = next) {
655 fp = (struct flist *)qp;
656 emp_remque(&fp->queue);
662 flist_eq(struct emp_qelem *elem, void *key)
664 struct flist *e = (struct flist *)elem;
665 struct flist *k = key;
667 return e->type == k->type && e->uid == k->uid;
670 static struct flist *
671 search_flist(struct emp_qelem *list, struct empobj *gp)
673 return (struct flist *)emp_searchque(list, gp, flist_eq);