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 };
84 enum targ_type target;
87 struct nstr_item nbst;
93 union empobj_storage item;
94 struct emp_qelem fired, defended;
99 emp_initque(&defended);
100 p = getstarg(player->argp[1],
101 "Firing from ship(s), sect(s), or land unit(s)? ", buf);
104 type = ef_byname_from(p, ef_with_guns);
105 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
106 pr("Fort firing is disabled.\n");
110 pr("Ships, land units or sectors only!\n");
113 if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
116 while (nxtitem(&nbst, &item)) {
117 if (type == EF_LAND) {
118 if (!getland(item.land.lnd_uid, &fland))
120 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
122 if (item.land.lnd_own != player->cnum)
125 if (lchr[fland.lnd_type].l_dam == 0) {
126 pr("Unit %d cannot fire!\n", fland.lnd_uid);
129 if (fland.lnd_item[I_MILIT] < 1) {
130 pr("Unit %d cannot fire because it has no military!\n",
134 if (fland.lnd_ship >= 0) {
135 pr("Unit %d cannot fire because it is on a ship!\n",
139 if (fland.lnd_land >= 0) {
140 pr("Unit %d cannot fire because it is on a land unit!\n",
144 if (fland.lnd_effic < LAND_MINFIREEFF) {
145 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
146 fland.lnd_uid, LAND_MINFIREEFF);
149 if (fland.lnd_item[I_SHELL] == 0) {
150 pr("%s -- not enough shells\n", prland(&fland));
155 } else if (type == EF_SHIP) {
156 if (!getship(item.ship.shp_uid, &fship))
158 if (item.ship.shp_own != player->cnum)
160 if (item.ship.shp_item[I_MILIT] < 1) {
161 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
164 if (mchr[item.ship.shp_type].m_glim == 0) {
165 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
168 if (item.ship.shp_item[I_GUN] == 0) {
169 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
172 if (item.ship.shp_item[I_SHELL] == 0) {
173 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
176 if (item.ship.shp_effic < 60) {
177 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
183 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
185 if (item.sect.sct_own != player->cnum)
187 if (item.sect.sct_type != SCT_FORTR)
189 if (item.sect.sct_effic < FORTEFF) {
190 pr("Fort not efficient enough to fire!\n");
193 if (item.sect.sct_item[I_GUN] == 0) {
194 pr("Not enough guns in sector %s!\n",
195 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
198 if (item.sect.sct_item[I_SHELL] == 0) {
199 pr("Not enough shells in sector %s!\n",
200 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
203 if (item.sect.sct_item[I_MILIT] < 5) {
204 pr("Not enough military in sector %s!\n",
205 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
208 pr("\nSector %s firing\n",
209 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
214 ptr = getstarg(player->argp[3], "Firing at? ", buf);
219 if (!issector(ptr)) {
221 if (vshipno < 0 || !getship(vshipno, &vship) ||
223 pr("No such ship exists!\n");
226 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
227 targ_sub : targ_ship;
228 vict = vship.shp_own;
231 if (!getsect(x, y, &vsect)) {
232 pr("No such sector exists!\n");
236 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
237 pr("No such sector exists!\n");
240 /* We check the sector type, but we only use it for damage, not
241 reporting. That way, you don't get extra information you wouldn't
242 normally get. Besides, what if they want to slam water? :) */
243 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
247 vict = vsect.sct_own;
252 trange = mapdist(x, y, fx, fy);
254 if (type == EF_SHIP) {
255 if (!check_ship_ok(&fship))
257 if (fship.shp_own != player->cnum) {
258 pr("Not your ship!\n");
261 if (target == targ_sub || target == targ_ship) {
262 if (fship.shp_uid == vship.shp_uid) {
263 pr("You can't fire upon yourself!\n");
267 if (fship.shp_item[I_MILIT] < 1) {
268 pr("Not enough military for firing crew.\n");
271 if (fship.shp_effic < 60) {
272 pr("Ship #%d is crippled (%d%%)\n",
273 fship.shp_uid, fship.shp_effic);
276 range = shp_fire_range(&fship);
277 range2 = roundrange(range);
278 pr("range is %d.00 (%.2f)\n", range2, range);
279 if (target == targ_sub
281 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
282 dam = shp_dchrg(&fship);
284 if (target == targ_sub)
285 /* Don't tell it's a sub */
287 if (fship.shp_item[I_GUN] == 0) {
288 pr("Insufficient arms.\n");
291 dam = shp_fire(&fship);
293 putship(fship.shp_uid, &fship);
298 if (opt_NOMOBCOST == 0) {
299 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
300 putship(fship.shp_uid, &fship);
302 } else if (type == EF_LAND) {
303 if (!check_land_ok(&fland))
305 if (fland.lnd_own != player->cnum) {
306 pr("Not your unit!\n");
310 if (target == targ_land) {
311 if (fland.lnd_x == vsect.sct_x
312 && fland.lnd_y == vsect.sct_y) {
313 pr("You can't fire upon yourself!\n");
318 if (lchr[fland.lnd_type].l_dam == 0) {
319 pr("Unit %d cannot fire!\n", fland.lnd_uid);
322 if (fland.lnd_item[I_GUN] == 0) {
323 pr("%s -- not enough guns\n", prland(&fland));
327 range = lnd_fire_range(&fland);
328 range2 = roundrange(range);
329 pr("range is %d.00 (%.2f)\n", range2, range);
330 if (target == targ_sub) {
331 /* Don't tell it's a sub */
335 dam = lnd_fire(&fland);
336 putland(fland.lnd_uid, &fland);
341 if (target == targ_ship) {
342 if (chance(lnd_acc(&fland) / 100.0))
343 dam = ldround(dam / 2.0, 1);
346 if (!check_sect_ok(&fsect))
348 if (fsect.sct_own != player->cnum ||
349 fsect.sct_type != SCT_FORTR) {
350 pr("No fortress at %s\n",
351 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
354 if (target == targ_land) {
355 if (fsect.sct_x == vsect.sct_x
356 && fsect.sct_y == vsect.sct_y) {
357 pr("You can't fire upon yourself!\n");
361 if (fsect.sct_item[I_GUN] == 0) {
362 pr("Insufficient arms.\n");
365 if (fsect.sct_item[I_MILIT] < 5) {
366 pr("Not enough military for firing crew.\n");
369 dam = fort_fire(&fsect);
375 range = fortrange(&fsect);
376 range2 = roundrange(range);
377 pr("range is %d.00 (%.2f)\n", range2, range);
378 if (target == targ_sub) {
379 /* Don't tell it's a sub */
383 if (trange > range2) {
384 pr("Target out of range.\n");
390 fland.lnd_mission = 0;
391 putland(fland.lnd_uid, &fland);
394 fship.shp_mission = 0;
395 putship(fship.shp_uid, &fship);
404 if (!trechk(player->cnum, vict, SEAFIR))
408 if (!trechk(player->cnum, vict, SUBFIR))
412 if (!trechk(player->cnum, vict, LANFIR))
420 if (target == targ_land) {
421 natp = getnatp(player->cnum);
422 rel = getrel(natp, vict);
423 if ((rel != AT_WAR) && (player->cnum != vict) &&
424 (vict) && (vsect.sct_oldown != player->cnum)) {
425 pr("You're not at war with them!\n");
444 nreport(player->cnum, N_SCT_SHELL, vict, 1);
445 if (vict && vict != player->cnum)
447 "Country #%d shelled sector %s for %d damage.\n",
448 player->cnum, xyas(x, y, vict), dam);
449 pr("Shells hit sector %s for %d damage.\n",
450 xyas(x, y, player->cnum), dam);
453 nreport(player->cnum, N_SHP_SHELL, vict, 1);
458 "Country #%d shelled %s in %s for %d damage.\n",
459 player->cnum, prship(&vship),
460 xyas(vship.shp_x, vship.shp_y, vict), dam);
462 pr("Shells hit %s in %s for %d damage.\n",
464 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
467 /* Ok, now, check if we had a bogus target. If so,
468 just continue on, since there is no defender. */
469 if (target == targ_bogus)
472 if (type == EF_LAND) {
473 getsect(fland.lnd_x, fland.lnd_y, &fsect);
474 attgp = (struct empobj *)&fsect;
476 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
479 getsect(x, y, &vsect);
480 sectdamage(&vsect, dam);
484 getship(vshipno, &vship);
485 check_retreat_and_do_shipdamage(&vship, dam);
486 if (vship.shp_effic < SHIP_MINEFF)
487 pr("%s sunk!\n", prsub(&vship));
488 else if (target == targ_sub
489 && (vship.shp_rflags & RET_DCHRGED)
490 && !(vship.shp_rflags & RET_INJURED))
491 retreat_ship(&vship, 'd');
492 putship(vship.shp_uid, &vship);
495 if (totaldefdam == 0 && target == targ_ship
496 && (vship.shp_rflags & RET_HELPLESS)
497 && !(vship.shp_rflags & RET_INJURED))
498 retreat_ship(&vship, 'h');
499 switch (attgp->ef_type) {
504 if ((target == targ_ship) || (target == targ_sub)) {
505 if (fship.shp_effic > SHIP_MINEFF) {
506 shp_missdef(&fship, vict);
509 putship(fship.shp_uid, &fship);
516 free_flist(&defended);
518 odds = ((double)ndefending) / ((double)nfiring);
521 do_defdam(&fired, odds);
526 defend(struct emp_qelem *al, struct emp_qelem *dl,
527 struct empobj *attgp, natid vict, int *nd)
532 dam = quiet_bigdef(attgp->ef_type, dl, vict,
533 attgp->own, attgp->x, attgp->y, &nfiring);
537 add_to_flist(al, attgp, dam, vict);
544 do_defdam(struct emp_qelem *list, double odds)
552 struct emp_qelem *qp, *next;
554 for (qp = list->q_forw; qp != list; qp = next) {
556 fp = (struct flist *)qp;
557 if (fp->type == EF_SHIP) {
558 if (!getship(fp->uid, &ship) || !ship.shp_own)
563 pr("\nDefenders fire back!\n");
566 dam = odds * fp->defdam;
568 if (fp->type == EF_SHIP) {
570 pr("Return fire hit %s in %s for %d damage.\n",
572 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
575 "Return fire hit %s in %s for %d damage.\n",
576 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
577 shipdamage(&ship, dam);
578 putship(ship.shp_uid, &ship);
580 CANT_HAPPEN(fp->type != EF_SECTOR);
581 getsect(fp->x, fp->y, §);
583 pr("Return fire hit sector %s for %d damage.\n",
584 xyas(fp->x, fp->y, player->cnum), dam);
585 sectdamage(§, dam);
588 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
589 xyas(fp->x, fp->y, vict), dam);
591 emp_remque(&fp->queue);
597 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
598 coord ax, coord ay, int *nfiring)
604 int dam, dam2, rel, rel2;
605 struct sctstr firing;
612 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
613 while (nxtitem(&ni, &ship)) {
614 if (ship.shp_own == 0)
617 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
620 rel = getrel(getnatp(ship.shp_own), own);
621 rel2 = getrel(getnatp(ship.shp_own), aown);
622 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
624 /* Don't shoot yourself */
625 if (ship.shp_own == aown)
627 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
628 erange = torprange(&ship);
629 if (roundrange(erange) < ni.curdist)
631 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
633 fp = search_flist(list, (struct empobj *)&ship);
637 dam2 = shp_torp(&ship, 0);
638 putship(ship.shp_uid, &ship);
642 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
645 erange = shp_fire_range(&ship);
646 if (roundrange(erange) < ni.curdist)
648 fp = search_flist(list, (struct empobj *)&ship);
652 dam2 = shp_fire(&ship);
653 putship(ship.shp_uid, &ship);
657 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
661 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
664 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
665 while (nxtitem(&ni, &land)) {
666 if (land.lnd_own == 0)
668 /* Don't shoot yourself */
669 if (land.lnd_own == aown)
672 rel = getrel(getnatp(land.lnd_own), own);
673 rel2 = getrel(getnatp(land.lnd_own), aown);
675 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
678 erange = lnd_fire_range(&land);
679 if (roundrange(erange) < ni.curdist)
682 fp = search_flist(list, (struct empobj *)&land);
686 dam2 = lnd_fire(&land);
687 putland(land.lnd_uid, &land);
694 add_to_flist(list, (struct empobj *)&land, dam2, 0);
695 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
696 if (type == EF_SHIP) {
697 if (chance(lnd_acc(&land) / 100.0))
698 dam2 = ldround(dam2 / 2.0, 1);
704 * Determine if any nearby gun-equipped sectors are within
705 * range and able to fire at an attacker. Firing sectors
706 * need to have guns, shells, and military. Sector being
707 * attacked is x,y -- attacker is at ax,ay.
710 if (!opt_NO_FORT_FIRE) {
711 snxtsct_dist(&ns, ax, ay, 8);
712 while (nxtsct(&ns, &firing)) {
713 if (firing.sct_own == 0)
715 rel = getrel(getnatp(firing.sct_own), own);
716 rel2 = getrel(getnatp(firing.sct_own), aown);
718 if ((firing.sct_own != own) &&
719 ((rel != ALLIED) || (rel2 != AT_WAR)))
721 /* Don't shoot yourself */
722 if (firing.sct_own == aown)
724 erange = fortrange(&firing);
725 if (roundrange(erange) < ns.curdist)
728 fp = search_flist(list, (struct empobj *)&firing);
732 dam2 = fort_fire(&firing);
739 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
740 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
745 return *nfiring == 0 ? 0 : dam / *nfiring;
749 add_to_flist(struct emp_qelem *list,
750 struct empobj *gp, int dam, natid victim)
754 fp = malloc(sizeof(struct flist));
755 fp->type = gp->ef_type;
761 emp_insque(&fp->queue, list);
765 free_flist(struct emp_qelem *list)
767 struct emp_qelem *qp, *next;
770 for (qp = list->q_forw; qp != list; qp = next) {
772 fp = (struct flist *)qp;
773 emp_remque(&fp->queue);
779 uid_eq(struct emp_qelem *elem, void *key)
781 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
784 static struct flist *
785 search_flist(struct emp_qelem *list, struct empobj *gp)
787 return (struct flist *)emp_searchque(list, gp, uid_eq);