2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2016, 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-2015
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);
434 defend(struct emp_qelem *al, struct emp_qelem *dl,
435 struct empobj *attgp, natid vict, int *nd)
440 dam = quiet_bigdef(attgp->ef_type, dl, vict,
441 attgp->own, attgp->x, attgp->y, &nfiring);
445 add_to_flist(al, attgp, dam, vict);
452 do_defdam(struct emp_qelem *list, double odds)
460 struct emp_qelem *qp, *next;
462 for (qp = list->q_forw; qp != list; qp = next) {
464 fp = (struct flist *)qp;
465 if (fp->type == EF_SHIP) {
466 if (!getship(fp->uid, &ship) || !ship.shp_own)
471 pr("\nDefenders fire back!\n");
474 dam = odds * fp->defdam;
476 if (fp->type == EF_SHIP) {
478 pr("Return fire hit %s in %s for %d damage.\n",
480 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
483 "Return fire hit %s in %s for %d damage.\n",
484 prship(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
485 shipdamage(&ship, dam);
486 putship(ship.shp_uid, &ship);
488 CANT_HAPPEN(fp->type != EF_SECTOR);
489 getsect(fp->x, fp->y, §);
491 pr("Return fire hit sector %s for %d damage.\n",
492 xyas(fp->x, fp->y, player->cnum), dam);
493 sectdamage(§, dam);
496 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
497 xyas(fp->x, fp->y, vict), dam);
499 emp_remque(&fp->queue);
505 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
506 coord ax, coord ay, int *nfiring)
513 struct sctstr firing;
520 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
521 while (nxtitem(&ni, &ship)) {
522 if (!feels_like_helping(ship.shp_own, own, aown))
525 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
528 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
529 erange = torprange(&ship);
530 if (roundrange(erange) < ni.curdist)
532 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
534 fp = search_flist(list, (struct empobj *)&ship);
538 dam2 = shp_torp(&ship, 0);
539 putship(ship.shp_uid, &ship);
543 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
546 erange = shp_fire_range(&ship);
547 if (roundrange(erange) < ni.curdist)
549 fp = search_flist(list, (struct empobj *)&ship);
553 dam2 = shp_fire(&ship);
554 putship(ship.shp_uid, &ship);
558 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
562 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
565 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
566 while (nxtitem(&ni, &land)) {
567 if (!feels_like_helping(land.lnd_own, own, aown))
570 erange = lnd_fire_range(&land);
571 if (roundrange(erange) < ni.curdist)
574 fp = search_flist(list, (struct empobj *)&land);
578 dam2 = lnd_fire(&land);
579 putland(land.lnd_uid, &land);
586 add_to_flist(list, (struct empobj *)&land, dam2, 0);
587 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
588 if (type == EF_SHIP) {
589 if (chance(lnd_acc(&land) / 100.0))
590 dam2 = ldround(dam2 / 2.0, 1);
596 * Determine if any nearby gun-equipped sectors are within
597 * range and able to fire at an attacker. Firing sectors
598 * need to have guns, shells, and military. Sector being
599 * attacked is x,y -- attacker is at ax,ay.
602 if (!opt_NO_FORT_FIRE) {
603 snxtsct_dist(&ns, ax, ay, 8);
604 while (nxtsct(&ns, &firing)) {
605 if (!feels_like_helping(firing.sct_own, own, aown))
608 erange = fortrange(&firing);
609 if (roundrange(erange) < ns.curdist)
612 fp = search_flist(list, (struct empobj *)&firing);
616 dam2 = fort_fire(&firing);
623 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
624 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
629 return *nfiring == 0 ? 0 : dam / *nfiring;
633 add_to_flist(struct emp_qelem *list,
634 struct empobj *gp, int dam, natid victim)
638 fp = malloc(sizeof(struct flist));
639 fp->type = gp->ef_type;
645 emp_insque(&fp->queue, list);
649 free_flist(struct emp_qelem *list)
651 struct emp_qelem *qp, *next;
654 for (qp = list->q_forw; qp != list; qp = next) {
656 fp = (struct flist *)qp;
657 emp_remque(&fp->queue);
663 uid_eq(struct emp_qelem *elem, void *key)
665 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
668 static struct flist *
669 search_flist(struct emp_qelem *list, struct empobj *gp)
671 return (struct flist *)emp_searchque(list, gp, uid_eq);