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 ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
118 if (!snxtitem(&nbst, type, ptr))
121 while (nxtitem(&nbst, &item)) {
122 if (type == EF_LAND) {
123 if (!getland(item.land.lnd_uid, &fland))
125 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
127 if (item.land.lnd_own != player->cnum)
130 if (lchr[fland.lnd_type].l_dam == 0) {
131 pr("Unit %d cannot fire!\n", fland.lnd_uid);
134 if (fland.lnd_item[I_MILIT] < 1) {
135 pr("Unit %d cannot fire because it has no military!\n",
139 if (fland.lnd_ship >= 0) {
140 pr("Unit %d cannot fire because it is on a ship!\n",
144 if (fland.lnd_land >= 0) {
145 pr("Unit %d cannot fire because it is on a land unit!\n",
149 if (fland.lnd_effic < LAND_MINFIREEFF) {
150 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
151 fland.lnd_uid, LAND_MINFIREEFF);
154 if (fland.lnd_item[I_SHELL] == 0) {
155 pr("%s -- not enough shells\n", prland(&fland));
160 } else if (type == EF_SHIP) {
161 if (!getship(item.ship.shp_uid, &fship))
163 if (item.ship.shp_own != player->cnum)
165 if (item.ship.shp_item[I_MILIT] < 1) {
166 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
169 if (mchr[item.ship.shp_type].m_glim == 0) {
170 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
173 if (item.ship.shp_item[I_GUN] == 0) {
174 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
177 if (item.ship.shp_item[I_SHELL] == 0) {
178 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
181 if (item.ship.shp_effic < 60) {
182 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
188 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
190 if (item.sect.sct_own != player->cnum)
192 if (item.sect.sct_type != SCT_FORTR)
194 if (item.sect.sct_effic < FORTEFF) {
195 pr("Fort not efficient enough to fire!\n");
198 if (item.sect.sct_item[I_GUN] == 0) {
199 pr("Not enough guns in sector %s!\n",
200 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
203 if (item.sect.sct_item[I_SHELL] == 0) {
204 pr("Not enough shells in sector %s!\n",
205 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
208 if (item.sect.sct_item[I_MILIT] < 5) {
209 pr("Not enough military in sector %s!\n",
210 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
213 pr("\nSector %s firing\n",
214 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
219 ptr = getstarg(player->argp[3], "Firing at? ", buf);
224 if (!issector(ptr)) {
226 if (vshipno < 0 || !getship(vshipno, &vship) ||
228 pr("No such ship exists!\n");
231 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
232 targ_sub : targ_ship;
233 vict = vship.shp_own;
236 if (!getsect(x, y, &vsect)) {
237 pr("No such sector exists!\n");
241 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
242 pr("No such sector exists!\n");
245 /* We check the sector type, but we only use it for damage, not
246 reporting. That way, you don't get extra information you wouldn't
247 normally get. Besides, what if they want to slam water? :) */
248 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
252 vict = vsect.sct_own;
257 trange = mapdist(x, y, fx, fy);
259 if (type == EF_SHIP) {
260 if (!check_ship_ok(&fship))
262 if (fship.shp_own != player->cnum) {
263 pr("Not your ship!\n");
266 if (target == targ_sub || target == targ_ship) {
267 if (fship.shp_uid == vship.shp_uid) {
268 pr("You can't fire upon yourself!\n");
272 if ((mil = fship.shp_item[I_MILIT]) < 1) {
273 pr("Not enough military for firing crew.\n");
276 if (fship.shp_effic < 60) {
277 pr("Ship #%d is crippled (%d%%)\n",
278 fship.shp_uid, fship.shp_effic);
281 range = shp_fire_range(&fship);
282 range2 = roundrange(range);
283 pr("range is %d.00 (%.2f)\n", range2, range);
284 if (target == targ_sub
286 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
287 dam = shp_dchrg(&fship);
289 if (target == targ_sub)
290 /* Don't tell it's a sub */
292 if (fship.shp_item[I_GUN] == 0) {
293 pr("Insufficient arms.\n");
296 dam = shp_fire(&fship);
298 putship(fship.shp_uid, &fship);
303 if (opt_NOMOBCOST == 0) {
304 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
305 putship(fship.shp_uid, &fship);
307 } else if (type == EF_LAND) {
308 if (!check_land_ok(&fland))
310 if (fland.lnd_own != player->cnum) {
311 pr("Not your unit!\n");
315 if (target == targ_land) {
316 if (fland.lnd_x == vsect.sct_x
317 && fland.lnd_y == vsect.sct_y) {
318 pr("You can't fire upon yourself!\n");
323 if (lchr[fland.lnd_type].l_dam == 0) {
324 pr("Unit %d cannot fire!\n", fland.lnd_uid);
327 if (fland.lnd_item[I_GUN] == 0) {
328 pr("%s -- not enough guns\n", prland(&fland));
332 range = lnd_fire_range(&fland);
333 range2 = roundrange(range);
334 pr("range is %d.00 (%.2f)\n", range2, range);
335 if (target == targ_sub) {
336 /* Don't tell it's a sub */
340 dam = lnd_fire(&fland);
341 putland(fland.lnd_uid, &fland);
346 if (target == targ_ship) {
347 if (chance(lnd_acc(&fland) / 100.0))
348 dam = ldround(dam / 2.0, 1);
351 if (!check_sect_ok(&fsect))
353 if (fsect.sct_own != player->cnum ||
354 fsect.sct_type != SCT_FORTR) {
355 pr("No fortress at %s\n",
356 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
359 if (target == targ_land) {
360 if (fsect.sct_x == vsect.sct_x
361 && fsect.sct_y == vsect.sct_y) {
362 pr("You can't fire upon yourself!\n");
366 if (fsect.sct_item[I_GUN] == 0) {
367 pr("Insufficient arms.\n");
370 if (fsect.sct_item[I_MILIT] < 5) {
371 pr("Not enough military for firing crew.\n");
374 dam = fort_fire(&fsect);
380 range = fortrange(&fsect);
381 range2 = roundrange(range);
382 pr("range is %d.00 (%.2f)\n", range2, range);
383 if (target == targ_sub) {
384 /* Don't tell it's a sub */
388 if (trange > range2) {
389 pr("Target out of range.\n");
395 fland.lnd_mission = 0;
396 putland(fland.lnd_uid, &fland);
399 fship.shp_mission = 0;
400 putship(fship.shp_uid, &fship);
409 if (!trechk(player->cnum, vict, SEAFIR))
413 if (!trechk(player->cnum, vict, SUBFIR))
417 if (!trechk(player->cnum, vict, LANFIR))
425 if (target == targ_land) {
426 natp = getnatp(player->cnum);
427 rel = getrel(natp, vict);
428 if ((rel != AT_WAR) && (player->cnum != vict) &&
429 (vict) && (vsect.sct_oldown != player->cnum)) {
430 pr("You're not at war with them!\n");
449 nreport(player->cnum, N_SCT_SHELL, vict, 1);
450 if (vict && vict != player->cnum)
452 "Country #%d shelled sector %s for %d damage.\n",
453 player->cnum, xyas(x, y, vict), dam);
454 pr("Shells hit sector %s for %d damage.\n",
455 xyas(x, y, player->cnum), dam);
458 nreport(player->cnum, N_SHP_SHELL, vict, 1);
463 "Country #%d shelled %s in %s for %d damage.\n",
464 player->cnum, prship(&vship),
465 xyas(vship.shp_x, vship.shp_y, vict), dam);
467 pr("Shells hit %s in %s for %d damage.\n",
469 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
472 /* Ok, now, check if we had a bogus target. If so,
473 just continue on, since there is no defender. */
474 if (target == targ_bogus)
477 if (type == EF_LAND) {
478 getsect(fland.lnd_x, fland.lnd_y, &fsect);
479 attgp = (struct empobj *)&fsect;
481 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
484 getsect(x, y, &vsect);
485 sectdamage(&vsect, dam);
489 getship(vshipno, &vship);
490 check_retreat_and_do_shipdamage(&vship, dam);
491 if (vship.shp_effic < SHIP_MINEFF)
492 pr("%s sunk!\n", prsub(&vship));
493 else if (target == targ_sub
494 && (vship.shp_rflags & RET_DCHRGED)
495 && !(vship.shp_rflags & RET_INJURED))
496 retreat_ship(&vship, 'd');
497 putship(vship.shp_uid, &vship);
500 if ((totaldefdam == 0) && (target == targ_ship))
501 if (vship.shp_rflags & RET_HELPLESS)
502 retreat_ship(&vship, 'h');
503 switch (attgp->ef_type) {
508 if ((target == targ_ship) || (target == targ_sub)) {
509 if (fship.shp_effic > SHIP_MINEFF) {
510 shp_missdef(&fship, vict);
513 putship(fship.shp_uid, &fship);
520 free_flist(&defended);
522 odds = ((double)ndefending) / ((double)nfiring);
525 do_defdam(&fired, odds);
530 defend(struct emp_qelem *al, struct emp_qelem *dl,
531 struct empobj *attgp, natid vict, int *nd)
536 dam = quiet_bigdef(attgp->ef_type, dl, vict,
537 attgp->own, attgp->x, attgp->y, &nfiring);
541 add_to_flist(al, attgp, dam, vict);
548 do_defdam(struct emp_qelem *list, double odds)
556 struct emp_qelem *qp, *next;
558 for (qp = list->q_forw; qp != list; qp = next) {
560 fp = (struct flist *)qp;
561 if (fp->type == EF_SHIP) {
562 if (!getship(fp->uid, &ship) || !ship.shp_own)
567 pr("\nDefenders fire back!\n");
570 dam = odds * fp->defdam;
572 if (fp->type == EF_SHIP) {
574 pr("Return fire hit %s in %s for %d damage.\n",
576 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
579 "Return fire hit %s in %s for %d damage.\n",
580 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
581 shipdamage(&ship, dam);
582 putship(ship.shp_uid, &ship);
584 CANT_HAPPEN(fp->type != EF_SECTOR);
585 getsect(fp->x, fp->y, §);
587 pr("Return fire hit sector %s for %d damage.\n",
588 xyas(fp->x, fp->y, player->cnum), dam);
589 sectdamage(§, dam);
592 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
593 xyas(fp->x, fp->y, vict), dam);
595 emp_remque(&fp->queue);
601 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
602 coord ax, coord ay, int *nfiring)
608 int dam, dam2, rel, rel2;
609 struct sctstr firing;
616 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
617 while (nxtitem(&ni, &ship)) {
618 if (ship.shp_own == 0)
621 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
624 rel = getrel(getnatp(ship.shp_own), own);
625 rel2 = getrel(getnatp(ship.shp_own), aown);
626 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
628 /* Don't shoot yourself */
629 if (ship.shp_own == aown)
631 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
632 erange = torprange(&ship);
633 if (roundrange(erange) < ni.curdist)
635 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
637 fp = search_flist(list, (struct empobj *)&ship);
641 dam2 = shp_torp(&ship, 0);
642 putship(ship.shp_uid, &ship);
646 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
649 erange = shp_fire_range(&ship);
650 if (roundrange(erange) < ni.curdist)
652 fp = search_flist(list, (struct empobj *)&ship);
656 dam2 = shp_fire(&ship);
657 putship(ship.shp_uid, &ship);
661 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
665 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
668 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
669 while (nxtitem(&ni, &land)) {
670 if (land.lnd_own == 0)
672 /* Don't shoot yourself */
673 if (land.lnd_own == aown)
676 rel = getrel(getnatp(land.lnd_own), own);
677 rel2 = getrel(getnatp(land.lnd_own), aown);
679 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
682 erange = lnd_fire_range(&land);
683 if (roundrange(erange) < ni.curdist)
686 fp = search_flist(list, (struct empobj *)&land);
690 dam2 = lnd_fire(&land);
691 putland(land.lnd_uid, &land);
698 add_to_flist(list, (struct empobj *)&land, dam2, 0);
699 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
700 if (type == EF_SHIP) {
701 if (chance(lnd_acc(&land) / 100.0))
702 dam2 = ldround(dam2 / 2.0, 1);
708 * Determine if any nearby gun-equipped sectors are within
709 * range and able to fire at an attacker. Firing sectors
710 * need to have guns, shells, and military. Sector being
711 * attacked is x,y -- attacker is at ax,ay.
714 if (!opt_NO_FORT_FIRE) {
715 snxtsct_dist(&ns, ax, ay, 8);
716 while (nxtsct(&ns, &firing)) {
717 if (firing.sct_own == 0)
719 rel = getrel(getnatp(firing.sct_own), own);
720 rel2 = getrel(getnatp(firing.sct_own), aown);
722 if ((firing.sct_own != own) &&
723 ((rel != ALLIED) || (rel2 != AT_WAR)))
725 /* Don't shoot yourself */
726 if (firing.sct_own == aown)
728 erange = fortrange(&firing);
729 if (roundrange(erange) < ns.curdist)
732 fp = search_flist(list, (struct empobj *)&firing);
736 dam2 = fort_fire(&firing);
743 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
744 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
749 return *nfiring == 0 ? 0 : dam / *nfiring;
753 add_to_flist(struct emp_qelem *list,
754 struct empobj *gp, int dam, natid victim)
758 fp = malloc(sizeof(struct flist));
759 fp->type = gp->ef_type;
765 emp_insque(&fp->queue, list);
769 free_flist(struct emp_qelem *list)
771 struct emp_qelem *qp, *next;
774 for (qp = list->q_forw; qp != list; qp = next) {
776 fp = (struct flist *)qp;
777 emp_remque(&fp->queue);
783 uid_eq(struct emp_qelem *elem, void *key)
785 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
788 static struct flist *
789 search_flist(struct emp_qelem *list, struct empobj *gp)
791 return (struct flist *)emp_searchque(list, gp, uid_eq);