2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2008, 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-2008
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 if (!(p = getstarg(player->argp[1],
102 "Firing from ship(s), sect(s), or land unit(s)? ",
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 if (vship.shp_rflags & RET_HELPLESS)
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);