2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2011, 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-2009
42 targ_land, targ_ship, targ_sub, targ_bogus
46 struct emp_qelem queue; /* list of fired things */
47 short type; /* EF_SECTOR, EF_SHIP or EF_LAND */
50 int defdam; /* damage defenders did */
54 static int defend(struct emp_qelem *, struct emp_qelem *,
55 struct empobj *, natid, int *);
56 static void do_defdam(struct emp_qelem *, double);
57 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
59 static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
60 static void free_flist(struct emp_qelem *);
61 static struct flist *search_flist(struct emp_qelem *, struct empobj *);
66 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
83 enum targ_type target;
84 struct nstr_item nbst;
90 union empobj_storage item;
91 struct emp_qelem fired, defended;
96 emp_initque(&defended);
97 p = getstarg(player->argp[1],
98 "Firing from ship(s), sect(s), or land unit(s)? ", buf);
101 type = ef_byname_from(p, ef_with_guns);
102 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
103 pr("Fort firing is disabled.\n");
107 pr("Ships, land units or sectors only!\n");
110 if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
113 while (nxtitem(&nbst, &item)) {
114 if (type == EF_LAND) {
115 if (!getland(item.land.lnd_uid, &fland))
117 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
119 if (item.land.lnd_own != player->cnum)
122 if (lchr[fland.lnd_type].l_dam == 0) {
123 pr("Unit %d cannot fire!\n", fland.lnd_uid);
126 if (fland.lnd_item[I_MILIT] < 1) {
127 pr("Unit %d cannot fire because it has no military!\n",
131 if (fland.lnd_ship >= 0) {
132 pr("Unit %d cannot fire because it is on a ship!\n",
136 if (fland.lnd_land >= 0) {
137 pr("Unit %d cannot fire because it is on a land unit!\n",
141 if (fland.lnd_effic < LAND_MINFIREEFF) {
142 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
143 fland.lnd_uid, LAND_MINFIREEFF);
146 if (fland.lnd_item[I_SHELL] == 0) {
147 pr("%s -- not enough shells\n", prland(&fland));
152 } else if (type == EF_SHIP) {
153 if (!getship(item.ship.shp_uid, &fship))
155 if (item.ship.shp_own != player->cnum)
157 if (item.ship.shp_item[I_MILIT] < 1) {
158 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
161 if (mchr[item.ship.shp_type].m_glim == 0) {
162 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
165 if (item.ship.shp_item[I_GUN] == 0) {
166 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
169 if (item.ship.shp_item[I_SHELL] == 0) {
170 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
173 if (item.ship.shp_effic < 60) {
174 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
180 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
182 if (item.sect.sct_own != player->cnum)
184 if (item.sect.sct_type != SCT_FORTR)
186 if (item.sect.sct_effic < FORTEFF) {
187 pr("Fort not efficient enough to fire!\n");
190 if (item.sect.sct_item[I_GUN] == 0) {
191 pr("Not enough guns in sector %s!\n",
192 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
195 if (item.sect.sct_item[I_SHELL] == 0) {
196 pr("Not enough shells in sector %s!\n",
197 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
200 if (item.sect.sct_item[I_MILIT] < 5) {
201 pr("Not enough military in sector %s!\n",
202 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
205 pr("\nSector %s firing\n",
206 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
211 ptr = getstarg(player->argp[3], "Firing at? ", buf);
216 if (!issector(ptr)) {
218 if (vshipno < 0 || !getship(vshipno, &vship) ||
220 pr("No such ship exists!\n");
223 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
224 targ_sub : targ_ship;
225 vict = vship.shp_own;
228 if (!getsect(x, y, &vsect)) {
229 pr("No such sector exists!\n");
233 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
234 pr("No such sector exists!\n");
237 /* We check the sector type, but we only use it for damage, not
238 reporting. That way, you don't get extra information you wouldn't
239 normally get. Besides, what if they want to slam water? :) */
240 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
244 vict = vsect.sct_own;
249 trange = mapdist(x, y, fx, fy);
251 if (type == EF_SHIP) {
252 if (!check_ship_ok(&fship))
254 if (fship.shp_own != player->cnum) {
255 pr("Not your ship!\n");
258 if (target == targ_sub || target == targ_ship) {
259 if (fship.shp_uid == vship.shp_uid) {
260 pr("You can't fire upon yourself!\n");
264 if (fship.shp_item[I_MILIT] < 1) {
265 pr("Not enough military for firing crew.\n");
268 if (fship.shp_effic < 60) {
269 pr("Ship #%d is crippled (%d%%)\n",
270 fship.shp_uid, fship.shp_effic);
273 range = shp_fire_range(&fship);
274 range2 = roundrange(range);
275 pr("range is %d.00 (%.2f)\n", range2, range);
276 if (target == targ_sub
278 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
279 dam = shp_dchrg(&fship);
281 if (target == targ_sub)
282 /* Don't tell it's a sub */
284 if (fship.shp_item[I_GUN] == 0) {
285 pr("Insufficient arms.\n");
288 dam = shp_fire(&fship);
290 putship(fship.shp_uid, &fship);
295 if (opt_NOMOBCOST == 0) {
296 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
297 putship(fship.shp_uid, &fship);
299 } else if (type == EF_LAND) {
300 if (!check_land_ok(&fland))
302 if (fland.lnd_own != player->cnum) {
303 pr("Not your unit!\n");
307 if (target == targ_land) {
308 if (fland.lnd_x == vsect.sct_x
309 && fland.lnd_y == vsect.sct_y) {
310 pr("You can't fire upon yourself!\n");
315 if (lchr[fland.lnd_type].l_dam == 0) {
316 pr("Unit %d cannot fire!\n", fland.lnd_uid);
319 if (fland.lnd_item[I_GUN] == 0) {
320 pr("%s -- not enough guns\n", prland(&fland));
324 range = lnd_fire_range(&fland);
325 range2 = roundrange(range);
326 pr("range is %d.00 (%.2f)\n", range2, range);
327 if (target == targ_sub) {
328 /* Don't tell it's a sub */
332 dam = lnd_fire(&fland);
333 putland(fland.lnd_uid, &fland);
338 if (target == targ_ship) {
339 if (chance(lnd_acc(&fland) / 100.0))
340 dam = ldround(dam / 2.0, 1);
343 if (!check_sect_ok(&fsect))
345 if (fsect.sct_own != player->cnum ||
346 fsect.sct_type != SCT_FORTR) {
347 pr("No fortress at %s\n",
348 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
351 if (target == targ_land) {
352 if (fsect.sct_x == vsect.sct_x
353 && fsect.sct_y == vsect.sct_y) {
354 pr("You can't fire upon yourself!\n");
358 if (fsect.sct_item[I_GUN] == 0) {
359 pr("Insufficient arms.\n");
362 if (fsect.sct_item[I_MILIT] < 5) {
363 pr("Not enough military for firing crew.\n");
366 dam = fort_fire(&fsect);
372 range = fortrange(&fsect);
373 range2 = roundrange(range);
374 pr("range is %d.00 (%.2f)\n", range2, range);
375 if (target == targ_sub) {
376 /* Don't tell it's a sub */
380 if (trange > range2) {
381 pr("Target out of range.\n");
387 fland.lnd_mission = 0;
388 putland(fland.lnd_uid, &fland);
391 fship.shp_mission = 0;
392 putship(fship.shp_uid, &fship);
401 if (!trechk(player->cnum, vict, SEAFIR))
405 if (!trechk(player->cnum, vict, SUBFIR))
409 if (!trechk(player->cnum, vict, LANFIR))
430 nreport(player->cnum, N_SCT_SHELL, vict, 1);
431 if (vict && vict != player->cnum)
433 "Country #%d shelled sector %s for %d damage.\n",
434 player->cnum, xyas(x, y, vict), dam);
435 pr("Shells hit sector %s for %d damage.\n",
436 xyas(x, y, player->cnum), dam);
439 nreport(player->cnum, N_SHP_SHELL, vict, 1);
444 "Country #%d shelled %s in %s for %d damage.\n",
445 player->cnum, prship(&vship),
446 xyas(vship.shp_x, vship.shp_y, vict), dam);
448 pr("Shells hit %s in %s for %d damage.\n",
450 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
453 /* Ok, now, check if we had a bogus target. If so,
454 just continue on, since there is no defender. */
455 if (target == targ_bogus)
458 if (type == EF_LAND) {
459 getsect(fland.lnd_x, fland.lnd_y, &fsect);
460 attgp = (struct empobj *)&fsect;
462 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
465 getsect(x, y, &vsect);
466 sectdamage(&vsect, dam);
470 getship(vshipno, &vship);
471 check_retreat_and_do_shipdamage(&vship, dam);
472 if (vship.shp_effic < SHIP_MINEFF)
473 pr("%s sunk!\n", prsub(&vship));
474 else if (target == targ_sub
475 && (vship.shp_rflags & RET_DCHRGED)
476 && !(vship.shp_rflags & RET_INJURED))
477 retreat_ship(&vship, 'd');
478 putship(vship.shp_uid, &vship);
481 if (totaldefdam == 0 && target == targ_ship
482 && (vship.shp_rflags & RET_HELPLESS)
483 && !(vship.shp_rflags & RET_INJURED))
484 retreat_ship(&vship, 'h');
485 switch (attgp->ef_type) {
490 if ((target == targ_ship) || (target == targ_sub)) {
491 if (fship.shp_effic > SHIP_MINEFF) {
492 shp_missdef(&fship, vict);
495 putship(fship.shp_uid, &fship);
502 free_flist(&defended);
504 odds = ((double)ndefending) / ((double)nfiring);
507 do_defdam(&fired, odds);
512 defend(struct emp_qelem *al, struct emp_qelem *dl,
513 struct empobj *attgp, natid vict, int *nd)
518 dam = quiet_bigdef(attgp->ef_type, dl, vict,
519 attgp->own, attgp->x, attgp->y, &nfiring);
523 add_to_flist(al, attgp, dam, vict);
530 do_defdam(struct emp_qelem *list, double odds)
538 struct emp_qelem *qp, *next;
540 for (qp = list->q_forw; qp != list; qp = next) {
542 fp = (struct flist *)qp;
543 if (fp->type == EF_SHIP) {
544 if (!getship(fp->uid, &ship) || !ship.shp_own)
549 pr("\nDefenders fire back!\n");
552 dam = odds * fp->defdam;
554 if (fp->type == EF_SHIP) {
556 pr("Return fire hit %s in %s for %d damage.\n",
558 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
561 "Return fire hit %s in %s for %d damage.\n",
562 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
563 shipdamage(&ship, dam);
564 putship(ship.shp_uid, &ship);
566 CANT_HAPPEN(fp->type != EF_SECTOR);
567 getsect(fp->x, fp->y, §);
569 pr("Return fire hit sector %s for %d damage.\n",
570 xyas(fp->x, fp->y, player->cnum), dam);
571 sectdamage(§, dam);
574 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
575 xyas(fp->x, fp->y, vict), dam);
577 emp_remque(&fp->queue);
583 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
584 coord ax, coord ay, int *nfiring)
591 struct sctstr firing;
598 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
599 while (nxtitem(&ni, &ship)) {
600 if (!feels_like_helping(ship.shp_own, own, aown))
603 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
606 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
607 erange = torprange(&ship);
608 if (roundrange(erange) < ni.curdist)
610 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
612 fp = search_flist(list, (struct empobj *)&ship);
616 dam2 = shp_torp(&ship, 0);
617 putship(ship.shp_uid, &ship);
621 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
624 erange = shp_fire_range(&ship);
625 if (roundrange(erange) < ni.curdist)
627 fp = search_flist(list, (struct empobj *)&ship);
631 dam2 = shp_fire(&ship);
632 putship(ship.shp_uid, &ship);
636 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
640 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
643 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
644 while (nxtitem(&ni, &land)) {
645 if (!feels_like_helping(land.lnd_own, own, aown))
648 erange = lnd_fire_range(&land);
649 if (roundrange(erange) < ni.curdist)
652 fp = search_flist(list, (struct empobj *)&land);
656 dam2 = lnd_fire(&land);
657 putland(land.lnd_uid, &land);
664 add_to_flist(list, (struct empobj *)&land, dam2, 0);
665 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
666 if (type == EF_SHIP) {
667 if (chance(lnd_acc(&land) / 100.0))
668 dam2 = ldround(dam2 / 2.0, 1);
674 * Determine if any nearby gun-equipped sectors are within
675 * range and able to fire at an attacker. Firing sectors
676 * need to have guns, shells, and military. Sector being
677 * attacked is x,y -- attacker is at ax,ay.
680 if (!opt_NO_FORT_FIRE) {
681 snxtsct_dist(&ns, ax, ay, 8);
682 while (nxtsct(&ns, &firing)) {
683 if (!feels_like_helping(firing.sct_own, own, aown))
686 erange = fortrange(&firing);
687 if (roundrange(erange) < ns.curdist)
690 fp = search_flist(list, (struct empobj *)&firing);
694 dam2 = fort_fire(&firing);
701 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
702 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
707 return *nfiring == 0 ? 0 : dam / *nfiring;
711 add_to_flist(struct emp_qelem *list,
712 struct empobj *gp, int dam, natid victim)
716 fp = malloc(sizeof(struct flist));
717 fp->type = gp->ef_type;
723 emp_insque(&fp->queue, list);
727 free_flist(struct emp_qelem *list)
729 struct emp_qelem *qp, *next;
732 for (qp = list->q_forw; qp != list; qp = next) {
734 fp = (struct flist *)qp;
735 emp_remque(&fp->queue);
741 uid_eq(struct emp_qelem *elem, void *key)
743 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
746 static struct flist *
747 search_flist(struct emp_qelem *list, struct empobj *gp)
749 return (struct flist *)emp_searchque(list, gp, uid_eq);