2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2010, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure
6 * This program 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 2 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, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * See files README, COPYING and CREDITS in the root of the source
23 * tree for related information and legal notices. It is expected
24 * that future projects/authors will amend these files as needed.
28 * multifire.c: Fire at other sectors/ships
30 * Known contributors to this file:
32 * Markus Armbruster, 2004-2009
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 };
85 enum targ_type target;
88 struct nstr_item nbst;
94 union empobj_storage item;
95 struct emp_qelem fired, defended;
100 emp_initque(&defended);
101 p = getstarg(player->argp[1],
102 "Firing from ship(s), sect(s), or land unit(s)? ", buf);
105 type = ef_byname_from(p, ef_with_guns);
106 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
107 pr("Fort firing is disabled.\n");
111 pr("Ships, land units or sectors only!\n");
114 if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
117 while (nxtitem(&nbst, &item)) {
118 if (type == EF_LAND) {
119 if (!getland(item.land.lnd_uid, &fland))
121 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
123 if (item.land.lnd_own != player->cnum)
126 if (lchr[fland.lnd_type].l_dam == 0) {
127 pr("Unit %d cannot fire!\n", fland.lnd_uid);
130 if (fland.lnd_item[I_MILIT] < 1) {
131 pr("Unit %d cannot fire because it has no military!\n",
135 if (fland.lnd_ship >= 0) {
136 pr("Unit %d cannot fire because it is on a ship!\n",
140 if (fland.lnd_land >= 0) {
141 pr("Unit %d cannot fire because it is on a land unit!\n",
145 if (fland.lnd_effic < LAND_MINFIREEFF) {
146 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
147 fland.lnd_uid, LAND_MINFIREEFF);
150 if (fland.lnd_item[I_SHELL] == 0) {
151 pr("%s -- not enough shells\n", prland(&fland));
156 } else if (type == EF_SHIP) {
157 if (!getship(item.ship.shp_uid, &fship))
159 if (item.ship.shp_own != player->cnum)
161 if (item.ship.shp_item[I_MILIT] < 1) {
162 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
165 if (mchr[item.ship.shp_type].m_glim == 0) {
166 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
169 if (item.ship.shp_item[I_GUN] == 0) {
170 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
173 if (item.ship.shp_item[I_SHELL] == 0) {
174 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
177 if (item.ship.shp_effic < 60) {
178 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
184 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
186 if (item.sect.sct_own != player->cnum)
188 if (item.sect.sct_type != SCT_FORTR)
190 if (item.sect.sct_effic < FORTEFF) {
191 pr("Fort not efficient enough to fire!\n");
194 if (item.sect.sct_item[I_GUN] == 0) {
195 pr("Not enough guns in sector %s!\n",
196 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
199 if (item.sect.sct_item[I_SHELL] == 0) {
200 pr("Not enough shells in sector %s!\n",
201 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
204 if (item.sect.sct_item[I_MILIT] < 5) {
205 pr("Not enough military in sector %s!\n",
206 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
209 pr("\nSector %s firing\n",
210 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
215 ptr = getstarg(player->argp[3], "Firing at? ", buf);
220 if (!issector(ptr)) {
222 if (vshipno < 0 || !getship(vshipno, &vship) ||
224 pr("No such ship exists!\n");
227 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
228 targ_sub : targ_ship;
229 vict = vship.shp_own;
232 if (!getsect(x, y, &vsect)) {
233 pr("No such sector exists!\n");
237 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
238 pr("No such sector exists!\n");
241 /* We check the sector type, but we only use it for damage, not
242 reporting. That way, you don't get extra information you wouldn't
243 normally get. Besides, what if they want to slam water? :) */
244 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
248 vict = vsect.sct_own;
253 trange = mapdist(x, y, fx, fy);
255 if (type == EF_SHIP) {
256 if (!check_ship_ok(&fship))
258 if (fship.shp_own != player->cnum) {
259 pr("Not your ship!\n");
262 if (target == targ_sub || target == targ_ship) {
263 if (fship.shp_uid == vship.shp_uid) {
264 pr("You can't fire upon yourself!\n");
268 if ((mil = fship.shp_item[I_MILIT]) < 1) {
269 pr("Not enough military for firing crew.\n");
272 if (fship.shp_effic < 60) {
273 pr("Ship #%d is crippled (%d%%)\n",
274 fship.shp_uid, fship.shp_effic);
277 range = shp_fire_range(&fship);
278 range2 = roundrange(range);
279 pr("range is %d.00 (%.2f)\n", range2, range);
280 if (target == targ_sub
282 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
283 dam = shp_dchrg(&fship);
285 if (target == targ_sub)
286 /* Don't tell it's a sub */
288 if (fship.shp_item[I_GUN] == 0) {
289 pr("Insufficient arms.\n");
292 dam = shp_fire(&fship);
294 putship(fship.shp_uid, &fship);
299 if (opt_NOMOBCOST == 0) {
300 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
301 putship(fship.shp_uid, &fship);
303 } else if (type == EF_LAND) {
304 if (!check_land_ok(&fland))
306 if (fland.lnd_own != player->cnum) {
307 pr("Not your unit!\n");
311 if (target == targ_land) {
312 if (fland.lnd_x == vsect.sct_x
313 && fland.lnd_y == vsect.sct_y) {
314 pr("You can't fire upon yourself!\n");
319 if (lchr[fland.lnd_type].l_dam == 0) {
320 pr("Unit %d cannot fire!\n", fland.lnd_uid);
323 if (fland.lnd_item[I_GUN] == 0) {
324 pr("%s -- not enough guns\n", prland(&fland));
328 range = lnd_fire_range(&fland);
329 range2 = roundrange(range);
330 pr("range is %d.00 (%.2f)\n", range2, range);
331 if (target == targ_sub) {
332 /* Don't tell it's a sub */
336 dam = lnd_fire(&fland);
337 putland(fland.lnd_uid, &fland);
342 if (target == targ_ship) {
343 if (chance(lnd_acc(&fland) / 100.0))
344 dam = ldround(dam / 2.0, 1);
347 if (!check_sect_ok(&fsect))
349 if (fsect.sct_own != player->cnum ||
350 fsect.sct_type != SCT_FORTR) {
351 pr("No fortress at %s\n",
352 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
355 if (target == targ_land) {
356 if (fsect.sct_x == vsect.sct_x
357 && fsect.sct_y == vsect.sct_y) {
358 pr("You can't fire upon yourself!\n");
362 if (fsect.sct_item[I_GUN] == 0) {
363 pr("Insufficient arms.\n");
366 if (fsect.sct_item[I_MILIT] < 5) {
367 pr("Not enough military for firing crew.\n");
370 dam = fort_fire(&fsect);
376 range = fortrange(&fsect);
377 range2 = roundrange(range);
378 pr("range is %d.00 (%.2f)\n", range2, range);
379 if (target == targ_sub) {
380 /* Don't tell it's a sub */
384 if (trange > range2) {
385 pr("Target out of range.\n");
391 fland.lnd_mission = 0;
392 putland(fland.lnd_uid, &fland);
395 fship.shp_mission = 0;
396 putship(fship.shp_uid, &fship);
405 if (!trechk(player->cnum, vict, SEAFIR))
409 if (!trechk(player->cnum, vict, SUBFIR))
413 if (!trechk(player->cnum, vict, LANFIR))
421 if (target == targ_land) {
422 natp = getnatp(player->cnum);
423 rel = getrel(natp, vict);
424 if ((rel != AT_WAR) && (player->cnum != vict) &&
425 (vict) && (vsect.sct_oldown != player->cnum)) {
426 pr("You're not at war with them!\n");
445 nreport(player->cnum, N_SCT_SHELL, vict, 1);
446 if (vict && vict != player->cnum)
448 "Country #%d shelled sector %s for %d damage.\n",
449 player->cnum, xyas(x, y, vict), dam);
450 pr("Shells hit sector %s for %d damage.\n",
451 xyas(x, y, player->cnum), dam);
454 nreport(player->cnum, N_SHP_SHELL, vict, 1);
459 "Country #%d shelled %s in %s for %d damage.\n",
460 player->cnum, prship(&vship),
461 xyas(vship.shp_x, vship.shp_y, vict), dam);
463 pr("Shells hit %s in %s for %d damage.\n",
465 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
468 /* Ok, now, check if we had a bogus target. If so,
469 just continue on, since there is no defender. */
470 if (target == targ_bogus)
473 if (type == EF_LAND) {
474 getsect(fland.lnd_x, fland.lnd_y, &fsect);
475 attgp = (struct empobj *)&fsect;
477 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
480 getsect(x, y, &vsect);
481 sectdamage(&vsect, dam);
485 getship(vshipno, &vship);
486 check_retreat_and_do_shipdamage(&vship, dam);
487 if (vship.shp_effic < SHIP_MINEFF)
488 pr("%s sunk!\n", prsub(&vship));
489 else if (target == targ_sub
490 && (vship.shp_rflags & RET_DCHRGED)
491 && !(vship.shp_rflags & RET_INJURED))
492 retreat_ship(&vship, 'd');
493 putship(vship.shp_uid, &vship);
496 if (totaldefdam == 0 && target == targ_ship
497 && (vship.shp_rflags & RET_HELPLESS)
498 && !(vship.shp_rflags & RET_INJURED))
499 retreat_ship(&vship, 'h');
500 switch (attgp->ef_type) {
505 if ((target == targ_ship) || (target == targ_sub)) {
506 if (fship.shp_effic > SHIP_MINEFF) {
507 shp_missdef(&fship, vict);
510 putship(fship.shp_uid, &fship);
517 free_flist(&defended);
519 odds = ((double)ndefending) / ((double)nfiring);
522 do_defdam(&fired, odds);
527 defend(struct emp_qelem *al, struct emp_qelem *dl,
528 struct empobj *attgp, natid vict, int *nd)
533 dam = quiet_bigdef(attgp->ef_type, dl, vict,
534 attgp->own, attgp->x, attgp->y, &nfiring);
538 add_to_flist(al, attgp, dam, vict);
545 do_defdam(struct emp_qelem *list, double odds)
553 struct emp_qelem *qp, *next;
555 for (qp = list->q_forw; qp != list; qp = next) {
557 fp = (struct flist *)qp;
558 if (fp->type == EF_SHIP) {
559 if (!getship(fp->uid, &ship) || !ship.shp_own)
564 pr("\nDefenders fire back!\n");
567 dam = odds * fp->defdam;
569 if (fp->type == EF_SHIP) {
571 pr("Return fire hit %s in %s for %d damage.\n",
573 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
576 "Return fire hit %s in %s for %d damage.\n",
577 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
578 shipdamage(&ship, dam);
579 putship(ship.shp_uid, &ship);
581 CANT_HAPPEN(fp->type != EF_SECTOR);
582 getsect(fp->x, fp->y, §);
584 pr("Return fire hit sector %s for %d damage.\n",
585 xyas(fp->x, fp->y, player->cnum), dam);
586 sectdamage(§, dam);
589 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
590 xyas(fp->x, fp->y, vict), dam);
592 emp_remque(&fp->queue);
598 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
599 coord ax, coord ay, int *nfiring)
605 int dam, dam2, rel, rel2;
606 struct sctstr firing;
613 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
614 while (nxtitem(&ni, &ship)) {
615 if (ship.shp_own == 0)
618 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
621 rel = getrel(getnatp(ship.shp_own), own);
622 rel2 = getrel(getnatp(ship.shp_own), aown);
623 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
625 /* Don't shoot yourself */
626 if (ship.shp_own == aown)
628 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
629 erange = torprange(&ship);
630 if (roundrange(erange) < ni.curdist)
632 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
634 fp = search_flist(list, (struct empobj *)&ship);
638 dam2 = shp_torp(&ship, 0);
639 putship(ship.shp_uid, &ship);
643 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
646 erange = shp_fire_range(&ship);
647 if (roundrange(erange) < ni.curdist)
649 fp = search_flist(list, (struct empobj *)&ship);
653 dam2 = shp_fire(&ship);
654 putship(ship.shp_uid, &ship);
658 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
662 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
665 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
666 while (nxtitem(&ni, &land)) {
667 if (land.lnd_own == 0)
669 /* Don't shoot yourself */
670 if (land.lnd_own == aown)
673 rel = getrel(getnatp(land.lnd_own), own);
674 rel2 = getrel(getnatp(land.lnd_own), aown);
676 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
679 erange = lnd_fire_range(&land);
680 if (roundrange(erange) < ni.curdist)
683 fp = search_flist(list, (struct empobj *)&land);
687 dam2 = lnd_fire(&land);
688 putland(land.lnd_uid, &land);
695 add_to_flist(list, (struct empobj *)&land, dam2, 0);
696 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
697 if (type == EF_SHIP) {
698 if (chance(lnd_acc(&land) / 100.0))
699 dam2 = ldround(dam2 / 2.0, 1);
705 * Determine if any nearby gun-equipped sectors are within
706 * range and able to fire at an attacker. Firing sectors
707 * need to have guns, shells, and military. Sector being
708 * attacked is x,y -- attacker is at ax,ay.
711 if (!opt_NO_FORT_FIRE) {
712 snxtsct_dist(&ns, ax, ay, 8);
713 while (nxtsct(&ns, &firing)) {
714 if (firing.sct_own == 0)
716 rel = getrel(getnatp(firing.sct_own), own);
717 rel2 = getrel(getnatp(firing.sct_own), aown);
719 if ((firing.sct_own != own) &&
720 ((rel != ALLIED) || (rel2 != AT_WAR)))
722 /* Don't shoot yourself */
723 if (firing.sct_own == aown)
725 erange = fortrange(&firing);
726 if (roundrange(erange) < ns.curdist)
729 fp = search_flist(list, (struct empobj *)&firing);
733 dam2 = fort_fire(&firing);
740 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
741 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
746 return *nfiring == 0 ? 0 : dam / *nfiring;
750 add_to_flist(struct emp_qelem *list,
751 struct empobj *gp, int dam, natid victim)
755 fp = malloc(sizeof(struct flist));
756 fp->type = gp->ef_type;
762 emp_insque(&fp->queue, list);
766 free_flist(struct emp_qelem *list)
768 struct emp_qelem *qp, *next;
771 for (qp = list->q_forw; qp != list; qp = next) {
773 fp = (struct flist *)qp;
774 emp_remque(&fp->queue);
780 uid_eq(struct emp_qelem *elem, void *key)
782 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
785 static struct flist *
786 search_flist(struct emp_qelem *list, struct empobj *gp)
788 return (struct flist *)emp_searchque(list, gp, uid_eq);