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 dam = shp_fire(&fship);
285 fship.shp_mission = 0;
286 putship(fship.shp_uid, &fship);
287 if (CANT_HAPPEN(dam < 0)) {
291 if (opt_NOMOBCOST == 0) {
292 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
293 putship(fship.shp_uid, &fship);
295 } else if (type == EF_LAND) {
296 if (!check_land_ok(&fland))
298 if (target == targ_land) {
299 if (fland.lnd_x == vsect.sct_x
300 && fland.lnd_y == vsect.sct_y) {
301 pr("You can't fire upon yourself!\n");
306 range = lnd_fire_range(&fland);
307 range2 = roundrange(range);
308 pr("range is %d.00 (%.2f)\n", range2, range);
309 if (target == targ_sub) {
310 /* Don't tell it's a sub */
314 dam = lnd_fire(&fland);
315 fland.lnd_mission = 0;
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");
368 nreport(player->cnum, N_SCT_SHELL, vict, 1);
369 if (vict && vict != player->cnum)
371 "Country #%d shelled sector %s for %d damage.\n",
372 player->cnum, xyas(x, y, vict), dam);
373 pr("Shells hit sector %s for %d damage.\n",
374 xyas(x, y, player->cnum), dam);
377 nreport(player->cnum, N_SHP_SHELL, vict, 1);
380 if (vict && vict != player->cnum) {
382 "Country #%d shelled %s in %s for %d damage.\n",
383 player->cnum, prship(&vship),
384 xyas(vship.shp_x, vship.shp_y, vict), dam);
386 pr("Shells hit %s in %s for %d damage.\n",
388 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
391 /* Ok, now, check if we had a bogus target. If so,
392 just continue on, since there is no defender. */
393 if (target == targ_bogus)
396 if (type == EF_LAND) {
397 getsect(fland.lnd_x, fland.lnd_y, &fsect);
398 attgp = (struct empobj *)&fsect;
400 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
403 getsect(x, y, &vsect);
404 sectdamage(&vsect, dam);
408 getship(vshipno, &vship);
409 shipdamage(&vship, dam);
410 if (vship.shp_effic < SHIP_MINEFF)
411 pr("%s sunk!\n", prsub(&vship));
412 putship(vship.shp_uid, &vship);
413 if (dam && (vship.shp_rflags & RET_INJURED))
414 retreat_ship(&vship, vict, 'i');
415 else if (target == targ_sub && (vship.shp_rflags & RET_DCHRGED))
416 retreat_ship(&vship, vict, 'd');
417 else if (totaldefdam == 0 && (vship.shp_rflags & RET_HELPLESS))
418 retreat_ship(&vship, vict, 'h');
421 switch (attgp->ef_type) {
425 if ((target == targ_ship) || (target == targ_sub)) {
426 if (fship.shp_effic > SHIP_MINEFF) {
427 shp_missdef(&fship, vict);
436 free_flist(&defended);
438 odds = ((double)ndefending) / ((double)nfiring);
441 do_defdam(&fired, odds);
446 defend(struct emp_qelem *al, struct emp_qelem *dl,
447 struct empobj *attgp, natid vict, int *nd)
452 dam = quiet_bigdef(attgp->ef_type, dl, vict,
453 attgp->own, attgp->x, attgp->y, &nfiring);
457 add_to_flist(al, attgp, dam, vict);
464 do_defdam(struct emp_qelem *list, double odds)
472 struct emp_qelem *qp, *next;
474 for (qp = list->q_forw; qp != list; qp = next) {
476 fp = (struct flist *)qp;
477 if (fp->type == EF_SHIP) {
478 if (!getship(fp->uid, &ship) || !ship.shp_own)
483 pr("\nDefenders fire back!\n");
486 dam = odds * fp->defdam;
488 if (fp->type == EF_SHIP) {
490 pr("Return fire hit %s in %s for %d damage.\n",
492 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
495 "Return fire hit %s in %s for %d damage.\n",
496 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
497 shipdamage(&ship, dam);
498 putship(ship.shp_uid, &ship);
500 CANT_HAPPEN(fp->type != EF_SECTOR);
501 getsect(fp->x, fp->y, §);
503 pr("Return fire hit sector %s for %d damage.\n",
504 xyas(fp->x, fp->y, player->cnum), dam);
505 sectdamage(§, dam);
508 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
509 xyas(fp->x, fp->y, vict), dam);
511 emp_remque(&fp->queue);
517 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
518 coord ax, coord ay, int *nfiring)
525 struct sctstr firing;
532 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
533 while (nxtitem(&ni, &ship)) {
534 if (!feels_like_helping(ship.shp_own, own, aown))
537 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
540 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
541 erange = torprange(&ship);
542 if (roundrange(erange) < ni.curdist)
544 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
546 fp = search_flist(list, (struct empobj *)&ship);
550 dam2 = shp_torp(&ship, 0);
551 putship(ship.shp_uid, &ship);
555 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
558 erange = shp_fire_range(&ship);
559 if (roundrange(erange) < ni.curdist)
561 fp = search_flist(list, (struct empobj *)&ship);
565 dam2 = shp_fire(&ship);
566 putship(ship.shp_uid, &ship);
570 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
574 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
577 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
578 while (nxtitem(&ni, &land)) {
579 if (!feels_like_helping(land.lnd_own, own, aown))
582 erange = lnd_fire_range(&land);
583 if (roundrange(erange) < ni.curdist)
586 fp = search_flist(list, (struct empobj *)&land);
590 dam2 = lnd_fire(&land);
591 putland(land.lnd_uid, &land);
598 add_to_flist(list, (struct empobj *)&land, dam2, 0);
599 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
600 if (type == EF_SHIP) {
601 if (chance(lnd_acc(&land) / 100.0))
602 dam2 = ldround(dam2 / 2.0, 1);
608 * Determine if any nearby gun-equipped sectors are within
609 * range and able to fire at an attacker. Firing sectors
610 * need to have guns, shells, and military. Sector being
611 * attacked is x,y -- attacker is at ax,ay.
614 if (!opt_NO_FORT_FIRE) {
615 snxtsct_dist(&ns, ax, ay, 8);
616 while (nxtsct(&ns, &firing)) {
617 if (!feels_like_helping(firing.sct_own, own, aown))
620 erange = fortrange(&firing);
621 if (roundrange(erange) < ns.curdist)
624 fp = search_flist(list, (struct empobj *)&firing);
628 dam2 = fort_fire(&firing);
635 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
636 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
641 return *nfiring == 0 ? 0 : dam / *nfiring;
645 add_to_flist(struct emp_qelem *list,
646 struct empobj *gp, int dam, natid victim)
650 fp = malloc(sizeof(struct flist));
651 fp->type = gp->ef_type;
657 emp_insque(&fp->queue, list);
661 free_flist(struct emp_qelem *list)
663 struct emp_qelem *qp, *next;
666 for (qp = list->q_forw; qp != list; qp = next) {
668 fp = (struct flist *)qp;
669 emp_remque(&fp->queue);
675 uid_eq(struct emp_qelem *elem, void *key)
677 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
680 static struct flist *
681 search_flist(struct emp_qelem *list, struct empobj *gp)
683 return (struct flist *)emp_searchque(list, gp, uid_eq);