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));
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 && !(mchr[fship.shp_type].m_flags & M_DCH)) {
171 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
174 if (item.ship.shp_item[I_GUN] == 0) {
175 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
178 if (item.ship.shp_item[I_SHELL] == 0) {
179 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
182 if (item.ship.shp_effic < 60) {
183 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
189 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
191 if (item.sect.sct_own != player->cnum)
193 if (item.sect.sct_type != SCT_FORTR)
195 if (item.sect.sct_effic < FORTEFF) {
196 pr("Fort not efficient enough to fire!\n");
199 if (item.sect.sct_item[I_GUN] == 0) {
200 pr("Not enough guns in sector %s!\n",
201 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
204 if (item.sect.sct_item[I_SHELL] == 0) {
205 pr("Not enough shells in sector %s!\n",
206 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
209 if (item.sect.sct_item[I_MILIT] < 5) {
210 pr("Not enough military in sector %s!\n",
211 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
214 pr("%sSector %s ready to fire\n", sep,
215 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
221 ptr = getstarg(player->argp[3], "Firing at? ", buf);
226 if (!issector(ptr)) {
228 if (vshipno < 0 || !getship(vshipno, &vship) ||
230 pr("No such ship exists!\n");
233 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
234 targ_sub : targ_ship;
235 vict = vship.shp_own;
238 if (!getsect(x, y, &vsect)) {
239 pr("No such sector exists!\n");
243 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
244 pr("No such sector exists!\n");
247 /* We check the sector type, but we only use it for damage, not
248 reporting. That way, you don't get extra information you wouldn't
249 normally get. Besides, what if they want to slam water? :) */
250 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
254 vict = vsect.sct_own;
259 trange = mapdist(x, y, fx, fy);
261 if (type == EF_SHIP) {
262 if (!check_ship_ok(&fship))
264 if (target == targ_sub || target == targ_ship) {
265 if (fship.shp_uid == vship.shp_uid) {
266 pr("You can't fire upon yourself!\n");
270 range = shp_fire_range(&fship);
271 range2 = roundrange(range);
272 pr("range is %d.00 (%.2f)\n", range2, range);
273 if (target == targ_sub
275 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
276 dam = shp_dchrg(&fship);
278 if (target == targ_sub)
279 /* Don't tell it's a sub */
281 if (fship.shp_item[I_GUN] == 0) {
282 pr("Insufficient arms.\n");
285 dam = shp_fire(&fship);
287 putship(fship.shp_uid, &fship);
288 if (CANT_HAPPEN(dam < 0)) {
292 if (opt_NOMOBCOST == 0) {
293 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
294 putship(fship.shp_uid, &fship);
296 } else if (type == EF_LAND) {
297 if (!check_land_ok(&fland))
299 if (target == targ_land) {
300 if (fland.lnd_x == vsect.sct_x
301 && fland.lnd_y == vsect.sct_y) {
302 pr("You can't fire upon yourself!\n");
307 range = lnd_fire_range(&fland);
308 range2 = roundrange(range);
309 pr("range is %d.00 (%.2f)\n", range2, range);
310 if (target == targ_sub) {
311 /* Don't tell it's a sub */
315 dam = lnd_fire(&fland);
316 putland(fland.lnd_uid, &fland);
317 if (CANT_HAPPEN(dam < 0)) {
321 if (target == targ_ship) {
322 if (chance(lnd_acc(&fland) / 100.0))
323 dam = ldround(dam / 2.0, 1);
326 if (!check_sect_ok(&fsect))
328 if (target == targ_land) {
329 if (fsect.sct_x == vsect.sct_x
330 && fsect.sct_y == vsect.sct_y) {
331 pr("You can't fire upon yourself!\n");
335 dam = fort_fire(&fsect);
337 if (CANT_HAPPEN(dam < 0)) {
341 range = fortrange(&fsect);
342 range2 = roundrange(range);
343 pr("range is %d.00 (%.2f)\n", range2, range);
344 if (target == targ_sub) {
345 /* Don't tell it's a sub */
349 if (trange > range2) {
350 pr("Target out of range.\n");
356 fland.lnd_mission = 0;
357 putland(fland.lnd_uid, &fland);
360 fship.shp_mission = 0;
361 putship(fship.shp_uid, &fship);
370 if (!trechk(player->cnum, vict, SEAFIR))
374 if (!trechk(player->cnum, vict, SUBFIR))
378 if (!trechk(player->cnum, vict, LANFIR))
399 nreport(player->cnum, N_SCT_SHELL, vict, 1);
400 if (vict && vict != player->cnum)
402 "Country #%d shelled sector %s for %d damage.\n",
403 player->cnum, xyas(x, y, vict), dam);
404 pr("Shells hit sector %s for %d damage.\n",
405 xyas(x, y, player->cnum), dam);
408 nreport(player->cnum, N_SHP_SHELL, vict, 1);
411 if (vict && vict != player->cnum) {
413 "Country #%d shelled %s in %s for %d damage.\n",
414 player->cnum, prship(&vship),
415 xyas(vship.shp_x, vship.shp_y, vict), dam);
417 pr("Shells hit %s in %s for %d damage.\n",
419 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
422 /* Ok, now, check if we had a bogus target. If so,
423 just continue on, since there is no defender. */
424 if (target == targ_bogus)
427 if (type == EF_LAND) {
428 getsect(fland.lnd_x, fland.lnd_y, &fsect);
429 attgp = (struct empobj *)&fsect;
431 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
434 getsect(x, y, &vsect);
435 sectdamage(&vsect, dam);
439 getship(vshipno, &vship);
440 check_retreat_and_do_shipdamage(&vship, dam);
441 if (vship.shp_effic < SHIP_MINEFF)
442 pr("%s sunk!\n", prsub(&vship));
443 else if (target == targ_sub
444 && (vship.shp_rflags & RET_DCHRGED)
445 && !(vship.shp_rflags & RET_INJURED))
446 retreat_ship(&vship, 'd');
447 putship(vship.shp_uid, &vship);
450 if (totaldefdam == 0 && target == targ_ship
451 && (vship.shp_rflags & RET_HELPLESS)
452 && !(vship.shp_rflags & RET_INJURED))
453 retreat_ship(&vship, 'h');
454 switch (attgp->ef_type) {
459 if ((target == targ_ship) || (target == targ_sub)) {
460 if (fship.shp_effic > SHIP_MINEFF) {
461 shp_missdef(&fship, vict);
464 putship(fship.shp_uid, &fship);
471 free_flist(&defended);
473 odds = ((double)ndefending) / ((double)nfiring);
476 do_defdam(&fired, odds);
481 defend(struct emp_qelem *al, struct emp_qelem *dl,
482 struct empobj *attgp, natid vict, int *nd)
487 dam = quiet_bigdef(attgp->ef_type, dl, vict,
488 attgp->own, attgp->x, attgp->y, &nfiring);
492 add_to_flist(al, attgp, dam, vict);
499 do_defdam(struct emp_qelem *list, double odds)
507 struct emp_qelem *qp, *next;
509 for (qp = list->q_forw; qp != list; qp = next) {
511 fp = (struct flist *)qp;
512 if (fp->type == EF_SHIP) {
513 if (!getship(fp->uid, &ship) || !ship.shp_own)
518 pr("\nDefenders fire back!\n");
521 dam = odds * fp->defdam;
523 if (fp->type == EF_SHIP) {
525 pr("Return fire hit %s in %s for %d damage.\n",
527 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
530 "Return fire hit %s in %s for %d damage.\n",
531 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
532 shipdamage(&ship, dam);
533 putship(ship.shp_uid, &ship);
535 CANT_HAPPEN(fp->type != EF_SECTOR);
536 getsect(fp->x, fp->y, §);
538 pr("Return fire hit sector %s for %d damage.\n",
539 xyas(fp->x, fp->y, player->cnum), dam);
540 sectdamage(§, dam);
543 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
544 xyas(fp->x, fp->y, vict), dam);
546 emp_remque(&fp->queue);
552 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
553 coord ax, coord ay, int *nfiring)
560 struct sctstr firing;
567 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
568 while (nxtitem(&ni, &ship)) {
569 if (!feels_like_helping(ship.shp_own, own, aown))
572 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
575 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
576 erange = torprange(&ship);
577 if (roundrange(erange) < ni.curdist)
579 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
581 fp = search_flist(list, (struct empobj *)&ship);
585 dam2 = shp_torp(&ship, 0);
586 putship(ship.shp_uid, &ship);
590 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
593 erange = shp_fire_range(&ship);
594 if (roundrange(erange) < ni.curdist)
596 fp = search_flist(list, (struct empobj *)&ship);
600 dam2 = shp_fire(&ship);
601 putship(ship.shp_uid, &ship);
605 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
609 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
612 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
613 while (nxtitem(&ni, &land)) {
614 if (!feels_like_helping(land.lnd_own, own, aown))
617 erange = lnd_fire_range(&land);
618 if (roundrange(erange) < ni.curdist)
621 fp = search_flist(list, (struct empobj *)&land);
625 dam2 = lnd_fire(&land);
626 putland(land.lnd_uid, &land);
633 add_to_flist(list, (struct empobj *)&land, dam2, 0);
634 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
635 if (type == EF_SHIP) {
636 if (chance(lnd_acc(&land) / 100.0))
637 dam2 = ldround(dam2 / 2.0, 1);
643 * Determine if any nearby gun-equipped sectors are within
644 * range and able to fire at an attacker. Firing sectors
645 * need to have guns, shells, and military. Sector being
646 * attacked is x,y -- attacker is at ax,ay.
649 if (!opt_NO_FORT_FIRE) {
650 snxtsct_dist(&ns, ax, ay, 8);
651 while (nxtsct(&ns, &firing)) {
652 if (!feels_like_helping(firing.sct_own, own, aown))
655 erange = fortrange(&firing);
656 if (roundrange(erange) < ns.curdist)
659 fp = search_flist(list, (struct empobj *)&firing);
663 dam2 = fort_fire(&firing);
670 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
671 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
676 return *nfiring == 0 ? 0 : dam / *nfiring;
680 add_to_flist(struct emp_qelem *list,
681 struct empobj *gp, int dam, natid victim)
685 fp = malloc(sizeof(struct flist));
686 fp->type = gp->ef_type;
692 emp_insque(&fp->queue, list);
696 free_flist(struct emp_qelem *list)
698 struct emp_qelem *qp, *next;
701 for (qp = list->q_forw; qp != list; qp = next) {
703 fp = (struct flist *)qp;
704 emp_remque(&fp->queue);
710 uid_eq(struct emp_qelem *elem, void *key)
712 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
715 static struct flist *
716 search_flist(struct emp_qelem *list, struct empobj *gp)
718 return (struct flist *)emp_searchque(list, gp, uid_eq);