2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2015, 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-2015
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);
385 nreport(player->cnum, N_SCT_SHELL, vict, 1);
386 if (vict && vict != player->cnum)
388 "Country #%d shelled sector %s for %d damage.\n",
389 player->cnum, xyas(x, y, vict), dam);
390 pr("Shells hit sector %s for %d damage.\n",
391 xyas(x, y, player->cnum), dam);
394 nreport(player->cnum, N_SHP_SHELL, vict, 1);
397 if (vict && vict != player->cnum) {
399 "Country #%d shelled %s in %s for %d damage.\n",
400 player->cnum, prship(&vship),
401 xyas(vship.shp_x, vship.shp_y, vict), dam);
403 pr("Shells hit %s in %s for %d damage.\n",
405 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
408 /* Ok, now, check if we had a bogus target. If so,
409 just continue on, since there is no defender. */
410 if (target == targ_bogus)
413 if (type == EF_LAND) {
414 getsect(fland.lnd_x, fland.lnd_y, &fsect);
415 attgp = (struct empobj *)&fsect;
417 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
420 getsect(x, y, &vsect);
421 sectdamage(&vsect, dam);
425 getship(vshipno, &vship);
426 shipdamage(&vship, dam);
427 if (vship.shp_effic < SHIP_MINEFF)
428 pr("%s sunk!\n", prsub(&vship));
429 if (dam && (vship.shp_rflags & RET_INJURED))
430 retreat_ship(&vship, 'i');
431 else if (target == targ_sub && (vship.shp_rflags & RET_DCHRGED))
432 retreat_ship(&vship, 'd');
433 else if (totaldefdam == 0 && (vship.shp_rflags & RET_HELPLESS))
434 retreat_ship(&vship, 'h');
435 putship(vship.shp_uid, &vship);
438 switch (attgp->ef_type) {
443 if ((target == targ_ship) || (target == targ_sub)) {
444 if (fship.shp_effic > SHIP_MINEFF) {
445 shp_missdef(&fship, vict);
448 putship(fship.shp_uid, &fship);
455 free_flist(&defended);
457 odds = ((double)ndefending) / ((double)nfiring);
460 do_defdam(&fired, odds);
465 defend(struct emp_qelem *al, struct emp_qelem *dl,
466 struct empobj *attgp, natid vict, int *nd)
471 dam = quiet_bigdef(attgp->ef_type, dl, vict,
472 attgp->own, attgp->x, attgp->y, &nfiring);
476 add_to_flist(al, attgp, dam, vict);
483 do_defdam(struct emp_qelem *list, double odds)
491 struct emp_qelem *qp, *next;
493 for (qp = list->q_forw; qp != list; qp = next) {
495 fp = (struct flist *)qp;
496 if (fp->type == EF_SHIP) {
497 if (!getship(fp->uid, &ship) || !ship.shp_own)
502 pr("\nDefenders fire back!\n");
505 dam = odds * fp->defdam;
507 if (fp->type == EF_SHIP) {
509 pr("Return fire hit %s in %s for %d damage.\n",
511 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
514 "Return fire hit %s in %s for %d damage.\n",
515 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
516 shipdamage(&ship, dam);
517 putship(ship.shp_uid, &ship);
519 CANT_HAPPEN(fp->type != EF_SECTOR);
520 getsect(fp->x, fp->y, §);
522 pr("Return fire hit sector %s for %d damage.\n",
523 xyas(fp->x, fp->y, player->cnum), dam);
524 sectdamage(§, dam);
527 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
528 xyas(fp->x, fp->y, vict), dam);
530 emp_remque(&fp->queue);
536 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
537 coord ax, coord ay, int *nfiring)
544 struct sctstr firing;
551 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
552 while (nxtitem(&ni, &ship)) {
553 if (!feels_like_helping(ship.shp_own, own, aown))
556 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
559 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
560 erange = torprange(&ship);
561 if (roundrange(erange) < ni.curdist)
563 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
565 fp = search_flist(list, (struct empobj *)&ship);
569 dam2 = shp_torp(&ship, 0);
570 putship(ship.shp_uid, &ship);
574 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
577 erange = shp_fire_range(&ship);
578 if (roundrange(erange) < ni.curdist)
580 fp = search_flist(list, (struct empobj *)&ship);
584 dam2 = shp_fire(&ship);
585 putship(ship.shp_uid, &ship);
589 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
593 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
596 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
597 while (nxtitem(&ni, &land)) {
598 if (!feels_like_helping(land.lnd_own, own, aown))
601 erange = lnd_fire_range(&land);
602 if (roundrange(erange) < ni.curdist)
605 fp = search_flist(list, (struct empobj *)&land);
609 dam2 = lnd_fire(&land);
610 putland(land.lnd_uid, &land);
617 add_to_flist(list, (struct empobj *)&land, dam2, 0);
618 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
619 if (type == EF_SHIP) {
620 if (chance(lnd_acc(&land) / 100.0))
621 dam2 = ldround(dam2 / 2.0, 1);
627 * Determine if any nearby gun-equipped sectors are within
628 * range and able to fire at an attacker. Firing sectors
629 * need to have guns, shells, and military. Sector being
630 * attacked is x,y -- attacker is at ax,ay.
633 if (!opt_NO_FORT_FIRE) {
634 snxtsct_dist(&ns, ax, ay, 8);
635 while (nxtsct(&ns, &firing)) {
636 if (!feels_like_helping(firing.sct_own, own, aown))
639 erange = fortrange(&firing);
640 if (roundrange(erange) < ns.curdist)
643 fp = search_flist(list, (struct empobj *)&firing);
647 dam2 = fort_fire(&firing);
654 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
655 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
660 return *nfiring == 0 ? 0 : dam / *nfiring;
664 add_to_flist(struct emp_qelem *list,
665 struct empobj *gp, int dam, natid victim)
669 fp = malloc(sizeof(struct flist));
670 fp->type = gp->ef_type;
676 emp_insque(&fp->queue, list);
680 free_flist(struct emp_qelem *list)
682 struct emp_qelem *qp, *next;
685 for (qp = list->q_forw; qp != list; qp = next) {
687 fp = (struct flist *)qp;
688 emp_remque(&fp->queue);
694 uid_eq(struct emp_qelem *elem, void *key)
696 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
699 static struct flist *
700 search_flist(struct emp_qelem *list, struct empobj *gp)
702 return (struct flist *)emp_searchque(list, gp, uid_eq);