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
44 targ_land, targ_ship, targ_sub, targ_bogus
48 struct emp_qelem queue; /* list of fired things */
49 short type; /* EF_SECTOR, EF_SHIP or EF_LAND */
52 int defdam; /* damage defenders did */
56 static int defend(struct emp_qelem *, struct emp_qelem *,
57 struct empobj *, natid, int *);
58 static void do_defdam(struct emp_qelem *, double);
59 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
61 static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
62 static void free_flist(struct emp_qelem *);
63 static struct flist *search_flist(struct emp_qelem *, struct empobj *);
68 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
85 enum targ_type target;
86 struct nstr_item nbst;
92 union empobj_storage item;
93 struct emp_qelem fired, defended;
98 emp_initque(&defended);
99 p = getstarg(player->argp[1],
100 "Firing from ship(s), sect(s), or land unit(s)? ", buf);
103 type = ef_byname_from(p, ef_with_guns);
104 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
105 pr("Fort firing is disabled.\n");
109 pr("Ships, land units or sectors only!\n");
112 if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
115 while (nxtitem(&nbst, &item)) {
116 if (type == EF_LAND) {
117 if (!getland(item.land.lnd_uid, &fland))
119 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
121 if (item.land.lnd_own != player->cnum)
124 if (lchr[fland.lnd_type].l_dam == 0) {
125 pr("Unit %d cannot fire!\n", fland.lnd_uid);
128 if (fland.lnd_item[I_MILIT] < 1) {
129 pr("Unit %d cannot fire because it has no military!\n",
133 if (fland.lnd_ship >= 0) {
134 pr("Unit %d cannot fire because it is on a ship!\n",
138 if (fland.lnd_land >= 0) {
139 pr("Unit %d cannot fire because it is on a land unit!\n",
143 if (fland.lnd_effic < LAND_MINFIREEFF) {
144 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
145 fland.lnd_uid, LAND_MINFIREEFF);
148 if (fland.lnd_item[I_SHELL] == 0) {
149 pr("%s -- not enough shells\n", prland(&fland));
154 } else if (type == EF_SHIP) {
155 if (!getship(item.ship.shp_uid, &fship))
157 if (item.ship.shp_own != player->cnum)
159 if (item.ship.shp_item[I_MILIT] < 1) {
160 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
163 if (mchr[item.ship.shp_type].m_glim == 0) {
164 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
167 if (item.ship.shp_item[I_GUN] == 0) {
168 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
171 if (item.ship.shp_item[I_SHELL] == 0) {
172 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
175 if (item.ship.shp_effic < 60) {
176 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
182 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
184 if (item.sect.sct_own != player->cnum)
186 if (item.sect.sct_type != SCT_FORTR)
188 if (item.sect.sct_effic < FORTEFF) {
189 pr("Fort not efficient enough to fire!\n");
192 if (item.sect.sct_item[I_GUN] == 0) {
193 pr("Not enough guns in sector %s!\n",
194 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
197 if (item.sect.sct_item[I_SHELL] == 0) {
198 pr("Not enough shells in sector %s!\n",
199 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
202 if (item.sect.sct_item[I_MILIT] < 5) {
203 pr("Not enough military in sector %s!\n",
204 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
207 pr("\nSector %s firing\n",
208 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
213 ptr = getstarg(player->argp[3], "Firing at? ", buf);
218 if (!issector(ptr)) {
220 if (vshipno < 0 || !getship(vshipno, &vship) ||
222 pr("No such ship exists!\n");
225 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
226 targ_sub : targ_ship;
227 vict = vship.shp_own;
230 if (!getsect(x, y, &vsect)) {
231 pr("No such sector exists!\n");
235 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
236 pr("No such sector exists!\n");
239 /* We check the sector type, but we only use it for damage, not
240 reporting. That way, you don't get extra information you wouldn't
241 normally get. Besides, what if they want to slam water? :) */
242 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
246 vict = vsect.sct_own;
251 trange = mapdist(x, y, fx, fy);
253 if (type == EF_SHIP) {
254 if (!check_ship_ok(&fship))
256 if (fship.shp_own != player->cnum) {
257 pr("Not your ship!\n");
260 if (target == targ_sub || target == targ_ship) {
261 if (fship.shp_uid == vship.shp_uid) {
262 pr("You can't fire upon yourself!\n");
266 if (fship.shp_item[I_MILIT] < 1) {
267 pr("Not enough military for firing crew.\n");
270 if (fship.shp_effic < 60) {
271 pr("Ship #%d is crippled (%d%%)\n",
272 fship.shp_uid, fship.shp_effic);
275 range = shp_fire_range(&fship);
276 range2 = roundrange(range);
277 pr("range is %d.00 (%.2f)\n", range2, range);
278 if (target == targ_sub
280 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
281 dam = shp_dchrg(&fship);
283 if (target == targ_sub)
284 /* Don't tell it's a sub */
286 if (fship.shp_item[I_GUN] == 0) {
287 pr("Insufficient arms.\n");
290 dam = shp_fire(&fship);
292 putship(fship.shp_uid, &fship);
297 if (opt_NOMOBCOST == 0) {
298 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
299 putship(fship.shp_uid, &fship);
301 } else if (type == EF_LAND) {
302 if (!check_land_ok(&fland))
304 if (fland.lnd_own != player->cnum) {
305 pr("Not your unit!\n");
309 if (target == targ_land) {
310 if (fland.lnd_x == vsect.sct_x
311 && fland.lnd_y == vsect.sct_y) {
312 pr("You can't fire upon yourself!\n");
317 if (lchr[fland.lnd_type].l_dam == 0) {
318 pr("Unit %d cannot fire!\n", fland.lnd_uid);
321 if (fland.lnd_item[I_GUN] == 0) {
322 pr("%s -- not enough guns\n", prland(&fland));
326 range = lnd_fire_range(&fland);
327 range2 = roundrange(range);
328 pr("range is %d.00 (%.2f)\n", range2, range);
329 if (target == targ_sub) {
330 /* Don't tell it's a sub */
334 dam = lnd_fire(&fland);
335 putland(fland.lnd_uid, &fland);
340 if (target == targ_ship) {
341 if (chance(lnd_acc(&fland) / 100.0))
342 dam = ldround(dam / 2.0, 1);
345 if (!check_sect_ok(&fsect))
347 if (fsect.sct_own != player->cnum ||
348 fsect.sct_type != SCT_FORTR) {
349 pr("No fortress at %s\n",
350 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
353 if (target == targ_land) {
354 if (fsect.sct_x == vsect.sct_x
355 && fsect.sct_y == vsect.sct_y) {
356 pr("You can't fire upon yourself!\n");
360 if (fsect.sct_item[I_GUN] == 0) {
361 pr("Insufficient arms.\n");
364 if (fsect.sct_item[I_MILIT] < 5) {
365 pr("Not enough military for firing crew.\n");
368 dam = fort_fire(&fsect);
374 range = fortrange(&fsect);
375 range2 = roundrange(range);
376 pr("range is %d.00 (%.2f)\n", range2, range);
377 if (target == targ_sub) {
378 /* Don't tell it's a sub */
382 if (trange > range2) {
383 pr("Target out of range.\n");
389 fland.lnd_mission = 0;
390 putland(fland.lnd_uid, &fland);
393 fship.shp_mission = 0;
394 putship(fship.shp_uid, &fship);
403 if (!trechk(player->cnum, vict, SEAFIR))
407 if (!trechk(player->cnum, vict, SUBFIR))
411 if (!trechk(player->cnum, vict, LANFIR))
432 nreport(player->cnum, N_SCT_SHELL, vict, 1);
433 if (vict && vict != player->cnum)
435 "Country #%d shelled sector %s for %d damage.\n",
436 player->cnum, xyas(x, y, vict), dam);
437 pr("Shells hit sector %s for %d damage.\n",
438 xyas(x, y, player->cnum), dam);
441 nreport(player->cnum, N_SHP_SHELL, vict, 1);
446 "Country #%d shelled %s in %s for %d damage.\n",
447 player->cnum, prship(&vship),
448 xyas(vship.shp_x, vship.shp_y, vict), dam);
450 pr("Shells hit %s in %s for %d damage.\n",
452 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
455 /* Ok, now, check if we had a bogus target. If so,
456 just continue on, since there is no defender. */
457 if (target == targ_bogus)
460 if (type == EF_LAND) {
461 getsect(fland.lnd_x, fland.lnd_y, &fsect);
462 attgp = (struct empobj *)&fsect;
464 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
467 getsect(x, y, &vsect);
468 sectdamage(&vsect, dam);
472 getship(vshipno, &vship);
473 check_retreat_and_do_shipdamage(&vship, dam);
474 if (vship.shp_effic < SHIP_MINEFF)
475 pr("%s sunk!\n", prsub(&vship));
476 else if (target == targ_sub
477 && (vship.shp_rflags & RET_DCHRGED)
478 && !(vship.shp_rflags & RET_INJURED))
479 retreat_ship(&vship, 'd');
480 putship(vship.shp_uid, &vship);
483 if (totaldefdam == 0 && target == targ_ship
484 && (vship.shp_rflags & RET_HELPLESS)
485 && !(vship.shp_rflags & RET_INJURED))
486 retreat_ship(&vship, 'h');
487 switch (attgp->ef_type) {
492 if ((target == targ_ship) || (target == targ_sub)) {
493 if (fship.shp_effic > SHIP_MINEFF) {
494 shp_missdef(&fship, vict);
497 putship(fship.shp_uid, &fship);
504 free_flist(&defended);
506 odds = ((double)ndefending) / ((double)nfiring);
509 do_defdam(&fired, odds);
514 defend(struct emp_qelem *al, struct emp_qelem *dl,
515 struct empobj *attgp, natid vict, int *nd)
520 dam = quiet_bigdef(attgp->ef_type, dl, vict,
521 attgp->own, attgp->x, attgp->y, &nfiring);
525 add_to_flist(al, attgp, dam, vict);
532 do_defdam(struct emp_qelem *list, double odds)
540 struct emp_qelem *qp, *next;
542 for (qp = list->q_forw; qp != list; qp = next) {
544 fp = (struct flist *)qp;
545 if (fp->type == EF_SHIP) {
546 if (!getship(fp->uid, &ship) || !ship.shp_own)
551 pr("\nDefenders fire back!\n");
554 dam = odds * fp->defdam;
556 if (fp->type == EF_SHIP) {
558 pr("Return fire hit %s in %s for %d damage.\n",
560 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
563 "Return fire hit %s in %s for %d damage.\n",
564 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
565 shipdamage(&ship, dam);
566 putship(ship.shp_uid, &ship);
568 CANT_HAPPEN(fp->type != EF_SECTOR);
569 getsect(fp->x, fp->y, §);
571 pr("Return fire hit sector %s for %d damage.\n",
572 xyas(fp->x, fp->y, player->cnum), dam);
573 sectdamage(§, dam);
576 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
577 xyas(fp->x, fp->y, vict), dam);
579 emp_remque(&fp->queue);
585 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
586 coord ax, coord ay, int *nfiring)
593 struct sctstr firing;
600 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
601 while (nxtitem(&ni, &ship)) {
602 if (!feels_like_helping(ship.shp_own, own, aown))
605 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
608 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
609 erange = torprange(&ship);
610 if (roundrange(erange) < ni.curdist)
612 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
614 fp = search_flist(list, (struct empobj *)&ship);
618 dam2 = shp_torp(&ship, 0);
619 putship(ship.shp_uid, &ship);
623 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
626 erange = shp_fire_range(&ship);
627 if (roundrange(erange) < ni.curdist)
629 fp = search_flist(list, (struct empobj *)&ship);
633 dam2 = shp_fire(&ship);
634 putship(ship.shp_uid, &ship);
638 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
642 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
645 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
646 while (nxtitem(&ni, &land)) {
647 if (!feels_like_helping(land.lnd_own, own, aown))
650 erange = lnd_fire_range(&land);
651 if (roundrange(erange) < ni.curdist)
654 fp = search_flist(list, (struct empobj *)&land);
658 dam2 = lnd_fire(&land);
659 putland(land.lnd_uid, &land);
666 add_to_flist(list, (struct empobj *)&land, dam2, 0);
667 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
668 if (type == EF_SHIP) {
669 if (chance(lnd_acc(&land) / 100.0))
670 dam2 = ldround(dam2 / 2.0, 1);
676 * Determine if any nearby gun-equipped sectors are within
677 * range and able to fire at an attacker. Firing sectors
678 * need to have guns, shells, and military. Sector being
679 * attacked is x,y -- attacker is at ax,ay.
682 if (!opt_NO_FORT_FIRE) {
683 snxtsct_dist(&ns, ax, ay, 8);
684 while (nxtsct(&ns, &firing)) {
685 if (!feels_like_helping(firing.sct_own, own, aown))
688 erange = fortrange(&firing);
689 if (roundrange(erange) < ns.curdist)
692 fp = search_flist(list, (struct empobj *)&firing);
696 dam2 = fort_fire(&firing);
703 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
704 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
709 return *nfiring == 0 ? 0 : dam / *nfiring;
713 add_to_flist(struct emp_qelem *list,
714 struct empobj *gp, int dam, natid victim)
718 fp = malloc(sizeof(struct flist));
719 fp->type = gp->ef_type;
725 emp_insque(&fp->queue, list);
729 free_flist(struct emp_qelem *list)
731 struct emp_qelem *qp, *next;
734 for (qp = list->q_forw; qp != list; qp = next) {
736 fp = (struct flist *)qp;
737 emp_remque(&fp->queue);
743 uid_eq(struct emp_qelem *elem, void *key)
745 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
748 static struct flist *
749 search_flist(struct emp_qelem *list, struct empobj *gp)
751 return (struct flist *)emp_searchque(list, gp, uid_eq);