2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2014, 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-2014
44 targ_land, targ_ship, targ_sub, targ_bogus
48 struct emp_qelem queue; /* list of fired things */
49 short type; /* EF_SECTOR, EF_SHIP or EF_LAND */
52 int defdam; /* damage defenders did */
56 static int defend(struct emp_qelem *, struct emp_qelem *,
57 struct empobj *, natid, int *);
58 static void do_defdam(struct emp_qelem *, double);
59 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
61 static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
62 static void free_flist(struct emp_qelem *);
63 static struct flist *search_flist(struct emp_qelem *, struct empobj *);
68 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
86 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_GUN] == 0) {
150 pr("%s -- not enough guns\n", prland(&fland));
154 if (fland.lnd_item[I_SHELL] == 0) {
155 pr("%s -- not enough shells\n", prland(&fland));
158 pr("%s%s ready to fire\n", sep, prland(&fland));
161 } else if (type == EF_SHIP) {
162 if (!getship(item.ship.shp_uid, &fship))
164 if (item.ship.shp_own != player->cnum)
166 if (item.ship.shp_item[I_MILIT] < 1) {
167 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
170 if (mchr[item.ship.shp_type].m_glim == 0
171 && !(mchr[fship.shp_type].m_flags & M_DCH)) {
172 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
175 if (item.ship.shp_item[I_GUN] == 0) {
176 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
179 if (item.ship.shp_item[I_SHELL] == 0) {
180 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
183 if (item.ship.shp_effic < 60) {
184 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
187 pr("%s%s ready to fire\n", sep, prship(&fship));
191 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
193 if (item.sect.sct_own != player->cnum)
195 if (item.sect.sct_type != SCT_FORTR)
197 if (item.sect.sct_effic < FORTEFF) {
198 pr("Fort not efficient enough to fire!\n");
201 if (item.sect.sct_item[I_GUN] == 0) {
202 pr("Not enough guns in sector %s!\n",
203 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
206 if (item.sect.sct_item[I_SHELL] == 0) {
207 pr("Not enough shells in sector %s!\n",
208 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
211 if (item.sect.sct_item[I_MILIT] < 5) {
212 pr("Not enough military in sector %s!\n",
213 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
216 pr("%sSector %s ready to fire\n", sep,
217 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
223 ptr = getstarg(player->argp[3], "Firing at? ", buf);
228 if (!issector(ptr)) {
230 if (vshipno < 0 || !getship(vshipno, &vship) ||
232 pr("No such ship exists!\n");
235 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
236 targ_sub : targ_ship;
237 vict = vship.shp_own;
240 if (!getsect(x, y, &vsect)) {
241 pr("No such sector exists!\n");
245 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
246 pr("No such sector exists!\n");
249 /* We check the sector type, but we only use it for damage, not
250 reporting. That way, you don't get extra information you wouldn't
251 normally get. Besides, what if they want to slam water? :) */
252 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
256 vict = vsect.sct_own;
261 trange = mapdist(x, y, fx, fy);
263 if (type == EF_SHIP) {
264 if (!check_ship_ok(&fship))
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 range = shp_fire_range(&fship);
273 range2 = roundrange(range);
274 pr("range is %d.00 (%.2f)\n", range2, range);
275 if (target == targ_sub
277 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
278 dam = shp_dchrg(&fship);
280 if (target == targ_sub)
281 /* Don't tell it's a sub */
283 if (fship.shp_item[I_GUN] == 0) {
284 pr("Insufficient arms.\n");
287 dam = shp_fire(&fship);
289 putship(fship.shp_uid, &fship);
290 if (CANT_HAPPEN(dam < 0)) {
294 if (opt_NOMOBCOST == 0) {
295 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
296 putship(fship.shp_uid, &fship);
298 } else if (type == EF_LAND) {
299 if (!check_land_ok(&fland))
301 if (target == targ_land) {
302 if (fland.lnd_x == vsect.sct_x
303 && fland.lnd_y == vsect.sct_y) {
304 pr("You can't fire upon yourself!\n");
309 range = lnd_fire_range(&fland);
310 range2 = roundrange(range);
311 pr("range is %d.00 (%.2f)\n", range2, range);
312 if (target == targ_sub) {
313 /* Don't tell it's a sub */
317 dam = lnd_fire(&fland);
318 putland(fland.lnd_uid, &fland);
319 if (CANT_HAPPEN(dam < 0)) {
323 if (target == targ_ship) {
324 if (chance(lnd_acc(&fland) / 100.0))
325 dam = ldround(dam / 2.0, 1);
328 if (!check_sect_ok(&fsect))
330 if (target == targ_land) {
331 if (fsect.sct_x == vsect.sct_x
332 && fsect.sct_y == vsect.sct_y) {
333 pr("You can't fire upon yourself!\n");
337 dam = fort_fire(&fsect);
339 if (CANT_HAPPEN(dam < 0)) {
343 range = fortrange(&fsect);
344 range2 = roundrange(range);
345 pr("range is %d.00 (%.2f)\n", range2, range);
346 if (target == targ_sub) {
347 /* Don't tell it's a sub */
351 if (trange > range2) {
352 pr("Target out of range.\n");
358 fland.lnd_mission = 0;
359 putland(fland.lnd_uid, &fland);
362 fship.shp_mission = 0;
363 putship(fship.shp_uid, &fship);
372 if (!trechk(player->cnum, vict, SEAFIR))
376 if (!trechk(player->cnum, vict, SUBFIR))
380 if (!trechk(player->cnum, vict, LANFIR))
401 nreport(player->cnum, N_SCT_SHELL, vict, 1);
402 if (vict && vict != player->cnum)
404 "Country #%d shelled sector %s for %d damage.\n",
405 player->cnum, xyas(x, y, vict), dam);
406 pr("Shells hit sector %s for %d damage.\n",
407 xyas(x, y, player->cnum), dam);
410 nreport(player->cnum, N_SHP_SHELL, vict, 1);
413 if (vict && vict != player->cnum) {
415 "Country #%d shelled %s in %s for %d damage.\n",
416 player->cnum, prship(&vship),
417 xyas(vship.shp_x, vship.shp_y, vict), dam);
419 pr("Shells hit %s in %s for %d damage.\n",
421 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
424 /* Ok, now, check if we had a bogus target. If so,
425 just continue on, since there is no defender. */
426 if (target == targ_bogus)
429 if (type == EF_LAND) {
430 getsect(fland.lnd_x, fland.lnd_y, &fsect);
431 attgp = (struct empobj *)&fsect;
433 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
436 getsect(x, y, &vsect);
437 sectdamage(&vsect, dam);
441 getship(vshipno, &vship);
442 check_retreat_and_do_shipdamage(&vship, dam);
443 if (vship.shp_effic < SHIP_MINEFF)
444 pr("%s sunk!\n", prsub(&vship));
445 else if (target == targ_sub
446 && (vship.shp_rflags & RET_DCHRGED)
447 && !(vship.shp_rflags & RET_INJURED))
448 retreat_ship(&vship, 'd');
449 putship(vship.shp_uid, &vship);
452 if (totaldefdam == 0 && target == targ_ship
453 && (vship.shp_rflags & RET_HELPLESS)
454 && !(vship.shp_rflags & RET_INJURED))
455 retreat_ship(&vship, 'h');
456 switch (attgp->ef_type) {
461 if ((target == targ_ship) || (target == targ_sub)) {
462 if (fship.shp_effic > SHIP_MINEFF) {
463 shp_missdef(&fship, vict);
466 putship(fship.shp_uid, &fship);
473 free_flist(&defended);
475 odds = ((double)ndefending) / ((double)nfiring);
478 do_defdam(&fired, odds);
483 defend(struct emp_qelem *al, struct emp_qelem *dl,
484 struct empobj *attgp, natid vict, int *nd)
489 dam = quiet_bigdef(attgp->ef_type, dl, vict,
490 attgp->own, attgp->x, attgp->y, &nfiring);
494 add_to_flist(al, attgp, dam, vict);
501 do_defdam(struct emp_qelem *list, double odds)
509 struct emp_qelem *qp, *next;
511 for (qp = list->q_forw; qp != list; qp = next) {
513 fp = (struct flist *)qp;
514 if (fp->type == EF_SHIP) {
515 if (!getship(fp->uid, &ship) || !ship.shp_own)
520 pr("\nDefenders fire back!\n");
523 dam = odds * fp->defdam;
525 if (fp->type == EF_SHIP) {
527 pr("Return fire hit %s in %s for %d damage.\n",
529 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
532 "Return fire hit %s in %s for %d damage.\n",
533 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
534 shipdamage(&ship, dam);
535 putship(ship.shp_uid, &ship);
537 CANT_HAPPEN(fp->type != EF_SECTOR);
538 getsect(fp->x, fp->y, §);
540 pr("Return fire hit sector %s for %d damage.\n",
541 xyas(fp->x, fp->y, player->cnum), dam);
542 sectdamage(§, dam);
545 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
546 xyas(fp->x, fp->y, vict), dam);
548 emp_remque(&fp->queue);
554 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
555 coord ax, coord ay, int *nfiring)
562 struct sctstr firing;
569 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
570 while (nxtitem(&ni, &ship)) {
571 if (!feels_like_helping(ship.shp_own, own, aown))
574 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
577 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
578 erange = torprange(&ship);
579 if (roundrange(erange) < ni.curdist)
581 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
583 fp = search_flist(list, (struct empobj *)&ship);
587 dam2 = shp_torp(&ship, 0);
588 putship(ship.shp_uid, &ship);
592 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
595 erange = shp_fire_range(&ship);
596 if (roundrange(erange) < ni.curdist)
598 fp = search_flist(list, (struct empobj *)&ship);
602 dam2 = shp_fire(&ship);
603 putship(ship.shp_uid, &ship);
607 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
611 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
614 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
615 while (nxtitem(&ni, &land)) {
616 if (!feels_like_helping(land.lnd_own, own, aown))
619 erange = lnd_fire_range(&land);
620 if (roundrange(erange) < ni.curdist)
623 fp = search_flist(list, (struct empobj *)&land);
627 dam2 = lnd_fire(&land);
628 putland(land.lnd_uid, &land);
635 add_to_flist(list, (struct empobj *)&land, dam2, 0);
636 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
637 if (type == EF_SHIP) {
638 if (chance(lnd_acc(&land) / 100.0))
639 dam2 = ldround(dam2 / 2.0, 1);
645 * Determine if any nearby gun-equipped sectors are within
646 * range and able to fire at an attacker. Firing sectors
647 * need to have guns, shells, and military. Sector being
648 * attacked is x,y -- attacker is at ax,ay.
651 if (!opt_NO_FORT_FIRE) {
652 snxtsct_dist(&ns, ax, ay, 8);
653 while (nxtsct(&ns, &firing)) {
654 if (!feels_like_helping(firing.sct_own, own, aown))
657 erange = fortrange(&firing);
658 if (roundrange(erange) < ns.curdist)
661 fp = search_flist(list, (struct empobj *)&firing);
665 dam2 = fort_fire(&firing);
672 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
673 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
678 return *nfiring == 0 ? 0 : dam / *nfiring;
682 add_to_flist(struct emp_qelem *list,
683 struct empobj *gp, int dam, natid victim)
687 fp = malloc(sizeof(struct flist));
688 fp->type = gp->ef_type;
694 emp_insque(&fp->queue, list);
698 free_flist(struct emp_qelem *list)
700 struct emp_qelem *qp, *next;
703 for (qp = list->q_forw; qp != list; qp = next) {
705 fp = (struct flist *)qp;
706 emp_remque(&fp->queue);
712 uid_eq(struct emp_qelem *elem, void *key)
714 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
717 static struct flist *
718 search_flist(struct emp_qelem *list, struct empobj *gp)
720 return (struct flist *)emp_searchque(list, gp, uid_eq);