2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2013, 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-2011
43 targ_land, targ_ship, targ_sub, targ_bogus
47 struct emp_qelem queue; /* list of fired things */
48 short type; /* EF_SECTOR, EF_SHIP or EF_LAND */
51 int defdam; /* damage defenders did */
55 static int defend(struct emp_qelem *, struct emp_qelem *,
56 struct empobj *, natid, int *);
57 static void do_defdam(struct emp_qelem *, double);
58 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
60 static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
61 static void free_flist(struct emp_qelem *);
62 static struct flist *search_flist(struct emp_qelem *, struct empobj *);
67 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
84 enum targ_type target;
85 struct nstr_item nbst;
91 union empobj_storage item;
92 struct emp_qelem fired, defended;
97 emp_initque(&defended);
98 p = getstarg(player->argp[1],
99 "Firing from ship(s), sect(s), or land unit(s)? ", buf);
102 type = ef_byname_from(p, ef_with_guns);
103 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
104 pr("Fort firing is disabled.\n");
108 pr("Ships, land units or sectors only!\n");
111 if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
114 while (nxtitem(&nbst, &item)) {
115 if (type == EF_LAND) {
116 if (!getland(item.land.lnd_uid, &fland))
118 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
120 if (item.land.lnd_own != player->cnum)
123 if (lchr[fland.lnd_type].l_dam == 0) {
124 pr("Unit %d cannot fire!\n", fland.lnd_uid);
127 if (fland.lnd_item[I_MILIT] < 1) {
128 pr("Unit %d cannot fire because it has no military!\n",
132 if (fland.lnd_ship >= 0) {
133 pr("Unit %d cannot fire because it is on a ship!\n",
137 if (fland.lnd_land >= 0) {
138 pr("Unit %d cannot fire because it is on a land unit!\n",
142 if (fland.lnd_effic < LAND_MINFIREEFF) {
143 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
144 fland.lnd_uid, LAND_MINFIREEFF);
147 if (fland.lnd_item[I_SHELL] == 0) {
148 pr("%s -- not enough shells\n", prland(&fland));
153 } else if (type == EF_SHIP) {
154 if (!getship(item.ship.shp_uid, &fship))
156 if (item.ship.shp_own != player->cnum)
158 if (item.ship.shp_item[I_MILIT] < 1) {
159 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
162 if (mchr[item.ship.shp_type].m_glim == 0) {
163 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
166 if (item.ship.shp_item[I_GUN] == 0) {
167 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
170 if (item.ship.shp_item[I_SHELL] == 0) {
171 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
174 if (item.ship.shp_effic < 60) {
175 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
181 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
183 if (item.sect.sct_own != player->cnum)
185 if (item.sect.sct_type != SCT_FORTR)
187 if (item.sect.sct_effic < FORTEFF) {
188 pr("Fort not efficient enough to fire!\n");
191 if (item.sect.sct_item[I_GUN] == 0) {
192 pr("Not enough guns in sector %s!\n",
193 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
196 if (item.sect.sct_item[I_SHELL] == 0) {
197 pr("Not enough shells in sector %s!\n",
198 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
201 if (item.sect.sct_item[I_MILIT] < 5) {
202 pr("Not enough military in sector %s!\n",
203 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
206 pr("\nSector %s firing\n",
207 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
212 ptr = getstarg(player->argp[3], "Firing at? ", buf);
217 if (!issector(ptr)) {
219 if (vshipno < 0 || !getship(vshipno, &vship) ||
221 pr("No such ship exists!\n");
224 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
225 targ_sub : targ_ship;
226 vict = vship.shp_own;
229 if (!getsect(x, y, &vsect)) {
230 pr("No such sector exists!\n");
234 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
235 pr("No such sector exists!\n");
238 /* We check the sector type, but we only use it for damage, not
239 reporting. That way, you don't get extra information you wouldn't
240 normally get. Besides, what if they want to slam water? :) */
241 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
245 vict = vsect.sct_own;
250 trange = mapdist(x, y, fx, fy);
252 if (type == EF_SHIP) {
253 if (!check_ship_ok(&fship))
255 if (fship.shp_own != player->cnum) {
256 pr("Not your ship!\n");
259 if (target == targ_sub || target == targ_ship) {
260 if (fship.shp_uid == vship.shp_uid) {
261 pr("You can't fire upon yourself!\n");
265 if (fship.shp_item[I_MILIT] < 1) {
266 pr("Not enough military for firing crew.\n");
269 if (fship.shp_effic < 60) {
270 pr("Ship #%d is crippled (%d%%)\n",
271 fship.shp_uid, fship.shp_effic);
274 range = shp_fire_range(&fship);
275 range2 = roundrange(range);
276 pr("range is %d.00 (%.2f)\n", range2, range);
277 if (target == targ_sub
279 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
280 dam = shp_dchrg(&fship);
282 if (target == targ_sub)
283 /* Don't tell it's a sub */
285 if (fship.shp_item[I_GUN] == 0) {
286 pr("Insufficient arms.\n");
289 dam = shp_fire(&fship);
291 putship(fship.shp_uid, &fship);
296 if (opt_NOMOBCOST == 0) {
297 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
298 putship(fship.shp_uid, &fship);
300 } else if (type == EF_LAND) {
301 if (!check_land_ok(&fland))
303 if (fland.lnd_own != player->cnum) {
304 pr("Not your unit!\n");
308 if (target == targ_land) {
309 if (fland.lnd_x == vsect.sct_x
310 && fland.lnd_y == vsect.sct_y) {
311 pr("You can't fire upon yourself!\n");
316 if (lchr[fland.lnd_type].l_dam == 0) {
317 pr("Unit %d cannot fire!\n", fland.lnd_uid);
320 if (fland.lnd_item[I_GUN] == 0) {
321 pr("%s -- not enough guns\n", prland(&fland));
325 range = lnd_fire_range(&fland);
326 range2 = roundrange(range);
327 pr("range is %d.00 (%.2f)\n", range2, range);
328 if (target == targ_sub) {
329 /* Don't tell it's a sub */
333 dam = lnd_fire(&fland);
334 putland(fland.lnd_uid, &fland);
339 if (target == targ_ship) {
340 if (chance(lnd_acc(&fland) / 100.0))
341 dam = ldround(dam / 2.0, 1);
344 if (!check_sect_ok(&fsect))
346 if (fsect.sct_own != player->cnum ||
347 fsect.sct_type != SCT_FORTR) {
348 pr("No fortress at %s\n",
349 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
352 if (target == targ_land) {
353 if (fsect.sct_x == vsect.sct_x
354 && fsect.sct_y == vsect.sct_y) {
355 pr("You can't fire upon yourself!\n");
359 if (fsect.sct_item[I_GUN] == 0) {
360 pr("Insufficient arms.\n");
363 if (fsect.sct_item[I_MILIT] < 5) {
364 pr("Not enough military for firing crew.\n");
367 dam = fort_fire(&fsect);
373 range = fortrange(&fsect);
374 range2 = roundrange(range);
375 pr("range is %d.00 (%.2f)\n", range2, range);
376 if (target == targ_sub) {
377 /* Don't tell it's a sub */
381 if (trange > range2) {
382 pr("Target out of range.\n");
388 fland.lnd_mission = 0;
389 putland(fland.lnd_uid, &fland);
392 fship.shp_mission = 0;
393 putship(fship.shp_uid, &fship);
402 if (!trechk(player->cnum, vict, SEAFIR))
406 if (!trechk(player->cnum, vict, SUBFIR))
410 if (!trechk(player->cnum, vict, LANFIR))
431 nreport(player->cnum, N_SCT_SHELL, vict, 1);
432 if (vict && vict != player->cnum)
434 "Country #%d shelled sector %s for %d damage.\n",
435 player->cnum, xyas(x, y, vict), dam);
436 pr("Shells hit sector %s for %d damage.\n",
437 xyas(x, y, player->cnum), dam);
440 nreport(player->cnum, N_SHP_SHELL, vict, 1);
445 "Country #%d shelled %s in %s for %d damage.\n",
446 player->cnum, prship(&vship),
447 xyas(vship.shp_x, vship.shp_y, vict), dam);
449 pr("Shells hit %s in %s for %d damage.\n",
451 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
454 /* Ok, now, check if we had a bogus target. If so,
455 just continue on, since there is no defender. */
456 if (target == targ_bogus)
459 if (type == EF_LAND) {
460 getsect(fland.lnd_x, fland.lnd_y, &fsect);
461 attgp = (struct empobj *)&fsect;
463 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
466 getsect(x, y, &vsect);
467 sectdamage(&vsect, dam);
471 getship(vshipno, &vship);
472 check_retreat_and_do_shipdamage(&vship, dam);
473 if (vship.shp_effic < SHIP_MINEFF)
474 pr("%s sunk!\n", prsub(&vship));
475 else if (target == targ_sub
476 && (vship.shp_rflags & RET_DCHRGED)
477 && !(vship.shp_rflags & RET_INJURED))
478 retreat_ship(&vship, 'd');
479 putship(vship.shp_uid, &vship);
482 if (totaldefdam == 0 && target == targ_ship
483 && (vship.shp_rflags & RET_HELPLESS)
484 && !(vship.shp_rflags & RET_INJURED))
485 retreat_ship(&vship, 'h');
486 switch (attgp->ef_type) {
491 if ((target == targ_ship) || (target == targ_sub)) {
492 if (fship.shp_effic > SHIP_MINEFF) {
493 shp_missdef(&fship, vict);
496 putship(fship.shp_uid, &fship);
503 free_flist(&defended);
505 odds = ((double)ndefending) / ((double)nfiring);
508 do_defdam(&fired, odds);
513 defend(struct emp_qelem *al, struct emp_qelem *dl,
514 struct empobj *attgp, natid vict, int *nd)
519 dam = quiet_bigdef(attgp->ef_type, dl, vict,
520 attgp->own, attgp->x, attgp->y, &nfiring);
524 add_to_flist(al, attgp, dam, vict);
531 do_defdam(struct emp_qelem *list, double odds)
539 struct emp_qelem *qp, *next;
541 for (qp = list->q_forw; qp != list; qp = next) {
543 fp = (struct flist *)qp;
544 if (fp->type == EF_SHIP) {
545 if (!getship(fp->uid, &ship) || !ship.shp_own)
550 pr("\nDefenders fire back!\n");
553 dam = odds * fp->defdam;
555 if (fp->type == EF_SHIP) {
557 pr("Return fire hit %s in %s for %d damage.\n",
559 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
562 "Return fire hit %s in %s for %d damage.\n",
563 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
564 shipdamage(&ship, dam);
565 putship(ship.shp_uid, &ship);
567 CANT_HAPPEN(fp->type != EF_SECTOR);
568 getsect(fp->x, fp->y, §);
570 pr("Return fire hit sector %s for %d damage.\n",
571 xyas(fp->x, fp->y, player->cnum), dam);
572 sectdamage(§, dam);
575 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
576 xyas(fp->x, fp->y, vict), dam);
578 emp_remque(&fp->queue);
584 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
585 coord ax, coord ay, int *nfiring)
592 struct sctstr firing;
599 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
600 while (nxtitem(&ni, &ship)) {
601 if (!feels_like_helping(ship.shp_own, own, aown))
604 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
607 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
608 erange = torprange(&ship);
609 if (roundrange(erange) < ni.curdist)
611 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
613 fp = search_flist(list, (struct empobj *)&ship);
617 dam2 = shp_torp(&ship, 0);
618 putship(ship.shp_uid, &ship);
622 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
625 erange = shp_fire_range(&ship);
626 if (roundrange(erange) < ni.curdist)
628 fp = search_flist(list, (struct empobj *)&ship);
632 dam2 = shp_fire(&ship);
633 putship(ship.shp_uid, &ship);
637 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
641 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
644 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
645 while (nxtitem(&ni, &land)) {
646 if (!feels_like_helping(land.lnd_own, own, aown))
649 erange = lnd_fire_range(&land);
650 if (roundrange(erange) < ni.curdist)
653 fp = search_flist(list, (struct empobj *)&land);
657 dam2 = lnd_fire(&land);
658 putland(land.lnd_uid, &land);
665 add_to_flist(list, (struct empobj *)&land, dam2, 0);
666 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
667 if (type == EF_SHIP) {
668 if (chance(lnd_acc(&land) / 100.0))
669 dam2 = ldround(dam2 / 2.0, 1);
675 * Determine if any nearby gun-equipped sectors are within
676 * range and able to fire at an attacker. Firing sectors
677 * need to have guns, shells, and military. Sector being
678 * attacked is x,y -- attacker is at ax,ay.
681 if (!opt_NO_FORT_FIRE) {
682 snxtsct_dist(&ns, ax, ay, 8);
683 while (nxtsct(&ns, &firing)) {
684 if (!feels_like_helping(firing.sct_own, own, aown))
687 erange = fortrange(&firing);
688 if (roundrange(erange) < ns.curdist)
691 fp = search_flist(list, (struct empobj *)&firing);
695 dam2 = fort_fire(&firing);
702 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
703 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
708 return *nfiring == 0 ? 0 : dam / *nfiring;
712 add_to_flist(struct emp_qelem *list,
713 struct empobj *gp, int dam, natid victim)
717 fp = malloc(sizeof(struct flist));
718 fp->type = gp->ef_type;
724 emp_insque(&fp->queue, list);
728 free_flist(struct emp_qelem *list)
730 struct emp_qelem *qp, *next;
733 for (qp = list->q_forw; qp != list; qp = next) {
735 fp = (struct flist *)qp;
736 emp_remque(&fp->queue);
742 uid_eq(struct emp_qelem *elem, void *key)
744 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
747 static struct flist *
748 search_flist(struct emp_qelem *list, struct empobj *gp)
750 return (struct flist *)emp_searchque(list, gp, uid_eq);