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
43 enum targ_type { /* Targeting... */
44 targ_land, /* a sector with guns */
45 targ_ship, /* a ship with guns */
46 targ_sub, /* a submarine with depth charges */
47 targ_bogus /* a bogus sector with guns */
51 struct emp_qelem queue; /* list of fired things */
52 short type; /* EF_SECTOR, EF_SHIP or EF_LAND */
55 int defdam; /* damage defenders did */
59 static int defend(struct emp_qelem *, struct emp_qelem *,
60 struct empobj *, natid, int *);
61 static void do_defdam(struct emp_qelem *, double);
62 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
64 static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
65 static void free_flist(struct emp_qelem *);
66 static struct flist *search_flist(struct emp_qelem *, struct empobj *);
71 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
89 enum targ_type target;
90 struct nstr_item nbst;
96 union empobj_storage item;
97 struct emp_qelem fired, defended;
102 emp_initque(&defended);
103 p = getstarg(player->argp[1],
104 "Firing from ship(s), sect(s), or land unit(s)? ", buf);
107 type = ef_byname_from(p, ef_with_guns);
108 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
109 pr("Fort firing is disabled.\n");
113 pr("Ships, land units or sectors only!\n");
116 if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
119 while (nxtitem(&nbst, &item)) {
120 if (type == EF_LAND) {
121 if (!getland(item.land.lnd_uid, &fland))
123 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
125 if (item.land.lnd_own != player->cnum)
128 if (lchr[fland.lnd_type].l_dam == 0) {
129 pr("Unit %d cannot fire!\n", fland.lnd_uid);
132 if (fland.lnd_item[I_MILIT] < 1) {
133 pr("Unit %d cannot fire because it has no military!\n",
137 if (fland.lnd_ship >= 0) {
138 pr("Unit %d cannot fire because it is on a ship!\n",
142 if (fland.lnd_land >= 0) {
143 pr("Unit %d cannot fire because it is on a land unit!\n",
147 if (fland.lnd_effic < LAND_MINFIREEFF) {
148 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
149 fland.lnd_uid, LAND_MINFIREEFF);
152 if (fland.lnd_item[I_GUN] == 0) {
153 pr("%s -- not enough guns\n", prland(&fland));
157 if (fland.lnd_item[I_SHELL] == 0) {
158 pr("%s -- not enough shells\n", prland(&fland));
161 pr("%s%s ready to fire\n", sep, prland(&fland));
164 } else if (type == EF_SHIP) {
165 if (!getship(item.ship.shp_uid, &fship))
167 if (item.ship.shp_own != player->cnum)
169 if (item.ship.shp_item[I_MILIT] < 1) {
170 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
173 if (mchr[item.ship.shp_type].m_glim == 0
174 && !(mchr[fship.shp_type].m_flags & M_DCH)) {
175 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
178 if (item.ship.shp_item[I_GUN] == 0) {
179 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
182 if (item.ship.shp_item[I_SHELL] == 0) {
183 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
186 if (item.ship.shp_effic < 60) {
187 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
190 pr("%s%s ready to fire\n", sep, prship(&fship));
194 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
196 if (item.sect.sct_own != player->cnum)
198 if (item.sect.sct_type != SCT_FORTR)
200 if (item.sect.sct_effic < FORTEFF) {
201 pr("Fort not efficient enough to fire!\n");
204 if (item.sect.sct_item[I_GUN] == 0) {
205 pr("Not enough guns in sector %s!\n",
206 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
209 if (item.sect.sct_item[I_SHELL] == 0) {
210 pr("Not enough shells in sector %s!\n",
211 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
214 if (item.sect.sct_item[I_MILIT] < 5) {
215 pr("Not enough military in sector %s!\n",
216 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
219 pr("%sSector %s ready to fire\n", sep,
220 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
226 ptr = getstarg(player->argp[3], "Firing at? ", buf);
231 if (!issector(ptr)) {
233 if (vshipno < 0 || !getship(vshipno, &vship) ||
235 pr("No such ship exists!\n");
238 target = targ_ship; /* targ_ship vs. targ_sub decided below */
239 vict = vship.shp_own;
242 if (!getsect(x, y, &vsect)) {
243 pr("No such sector exists!\n");
247 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
248 pr("No such sector exists!\n");
251 /* We check the sector type, but we only use it for damage, not
252 reporting. That way, you don't get extra information you wouldn't
253 normally get. Besides, what if they want to slam water? :) */
254 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
258 vict = vsect.sct_own;
263 trange = mapdist(x, y, fx, fy);
265 if (type == EF_SHIP) {
266 if (!check_ship_ok(&fship))
268 if (target == targ_ship) {
269 if (fship.shp_uid == vship.shp_uid) {
270 pr("You can't fire upon yourself!\n");
274 range = shp_fire_range(&fship);
275 range2 = roundrange(range);
276 pr("range is %d.00 (%.2f)\n", range2, range);
277 /* Use depth charges against subs, but only when in range */
278 if (target == targ_ship && trange <= range2
279 && (mchr[vship.shp_type].m_flags & M_SUB)
280 && (mchr[fship.shp_type].m_flags & M_DCH))
282 if (target == targ_sub)
283 dam = shp_dchrg(&fship);
285 dam = shp_fire(&fship);
286 fship.shp_mission = 0;
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");
306 range = lnd_fire_range(&fland);
307 range2 = roundrange(range);
308 pr("range is %d.00 (%.2f)\n", range2, range);
309 dam = lnd_fire(&fland);
310 fland.lnd_mission = 0;
311 putland(fland.lnd_uid, &fland);
312 if (CANT_HAPPEN(dam < 0)) {
316 if (target == targ_ship) {
317 if (chance(lnd_acc(&fland) / 100.0))
318 dam = ldround(dam / 2.0, 1);
321 if (!check_sect_ok(&fsect))
323 if (target == targ_land) {
324 if (fsect.sct_x == vsect.sct_x
325 && fsect.sct_y == vsect.sct_y) {
326 pr("You can't fire upon yourself!\n");
330 dam = fort_fire(&fsect);
332 if (CANT_HAPPEN(dam < 0)) {
336 range = fortrange(&fsect);
337 range2 = roundrange(range);
338 pr("range is %d.00 (%.2f)\n", range2, range);
342 * If the player fires guns at a submarine, take care not to
343 * disclose it's a submarine: pretend the target is out of range.
345 if (target == targ_ship && (mchr[vship.shp_type].m_flags & M_SUB))
347 if (trange > range2) {
348 pr("Target out of range.\n");
367 nreport(player->cnum, N_SCT_SHELL, vict, 1);
368 if (vict && vict != player->cnum)
370 "Country #%d shelled sector %s for %d damage.\n",
371 player->cnum, xyas(x, y, vict), dam);
372 pr("Shells hit sector %s for %d damage.\n",
373 xyas(x, y, player->cnum), dam);
376 nreport(player->cnum, N_SHP_SHELL, vict, 1);
379 if (vict && vict != player->cnum) {
381 "Country #%d shelled %s in %s for %d damage.\n",
382 player->cnum, prship(&vship),
383 xyas(vship.shp_x, vship.shp_y, vict), dam);
385 pr("Shells hit %s in %s for %d damage.\n",
387 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
390 /* Ok, now, check if we had a bogus target. If so,
391 just continue on, since there is no defender. */
392 if (target == targ_bogus)
395 if (type == EF_LAND) {
396 getsect(fland.lnd_x, fland.lnd_y, &fsect);
397 attgp = (struct empobj *)&fsect;
399 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
402 getsect(x, y, &vsect);
403 sectdamage(&vsect, dam);
407 getship(vshipno, &vship);
408 shipdamage(&vship, dam);
409 if (vship.shp_effic < SHIP_MINEFF)
410 pr("%s sunk!\n", prsub(&vship));
411 putship(vship.shp_uid, &vship);
412 if (dam && (vship.shp_rflags & RET_INJURED))
413 retreat_ship(&vship, vict, 'i');
414 else if (target == targ_sub && (vship.shp_rflags & RET_DCHRGED))
415 retreat_ship(&vship, vict, 'd');
416 else if (totaldefdam == 0 && (vship.shp_rflags & RET_HELPLESS))
417 retreat_ship(&vship, vict, 'h');
420 switch (attgp->ef_type) {
424 if ((target == targ_ship) || (target == targ_sub)) {
425 if (fship.shp_effic > SHIP_MINEFF) {
426 shp_missdef(&fship, vict);
435 free_flist(&defended);
437 odds = ((double)ndefending) / ((double)nfiring);
440 do_defdam(&fired, odds);
445 defend(struct emp_qelem *al, struct emp_qelem *dl,
446 struct empobj *attgp, natid vict, int *nd)
451 dam = quiet_bigdef(attgp->ef_type, dl, vict,
452 attgp->own, attgp->x, attgp->y, &nfiring);
456 add_to_flist(al, attgp, dam, vict);
463 do_defdam(struct emp_qelem *list, double odds)
471 struct emp_qelem *qp, *next;
473 for (qp = list->q_forw; qp != list; qp = next) {
475 fp = (struct flist *)qp;
476 if (fp->type == EF_SHIP) {
477 if (!getship(fp->uid, &ship) || !ship.shp_own)
482 pr("\nDefenders fire back!\n");
485 dam = odds * fp->defdam;
487 if (fp->type == EF_SHIP) {
489 pr("Return fire hit %s in %s for %d damage.\n",
491 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
494 "Return fire hit %s in %s for %d damage.\n",
495 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
496 shipdamage(&ship, dam);
497 putship(ship.shp_uid, &ship);
499 CANT_HAPPEN(fp->type != EF_SECTOR);
500 getsect(fp->x, fp->y, §);
502 pr("Return fire hit sector %s for %d damage.\n",
503 xyas(fp->x, fp->y, player->cnum), dam);
504 sectdamage(§, dam);
507 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
508 xyas(fp->x, fp->y, vict), dam);
510 emp_remque(&fp->queue);
516 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
517 coord ax, coord ay, int *nfiring)
524 struct sctstr firing;
531 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
532 while (nxtitem(&ni, &ship)) {
533 if (!feels_like_helping(ship.shp_own, own, aown))
536 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
539 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
540 erange = torprange(&ship);
541 if (roundrange(erange) < ni.curdist)
543 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
545 fp = search_flist(list, (struct empobj *)&ship);
549 dam2 = shp_torp(&ship, 0);
550 putship(ship.shp_uid, &ship);
554 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
557 erange = shp_fire_range(&ship);
558 if (roundrange(erange) < ni.curdist)
560 fp = search_flist(list, (struct empobj *)&ship);
564 dam2 = shp_fire(&ship);
565 putship(ship.shp_uid, &ship);
569 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
573 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
576 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
577 while (nxtitem(&ni, &land)) {
578 if (!feels_like_helping(land.lnd_own, own, aown))
581 erange = lnd_fire_range(&land);
582 if (roundrange(erange) < ni.curdist)
585 fp = search_flist(list, (struct empobj *)&land);
589 dam2 = lnd_fire(&land);
590 putland(land.lnd_uid, &land);
597 add_to_flist(list, (struct empobj *)&land, dam2, 0);
598 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
599 if (type == EF_SHIP) {
600 if (chance(lnd_acc(&land) / 100.0))
601 dam2 = ldround(dam2 / 2.0, 1);
607 * Determine if any nearby gun-equipped sectors are within
608 * range and able to fire at an attacker. Firing sectors
609 * need to have guns, shells, and military. Sector being
610 * attacked is x,y -- attacker is at ax,ay.
613 if (!opt_NO_FORT_FIRE) {
614 snxtsct_dist(&ns, ax, ay, 8);
615 while (nxtsct(&ns, &firing)) {
616 if (!feels_like_helping(firing.sct_own, own, aown))
619 erange = fortrange(&firing);
620 if (roundrange(erange) < ns.curdist)
623 fp = search_flist(list, (struct empobj *)&firing);
627 dam2 = fort_fire(&firing);
634 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
635 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
640 return *nfiring == 0 ? 0 : dam / *nfiring;
644 add_to_flist(struct emp_qelem *list,
645 struct empobj *gp, int dam, natid victim)
649 fp = malloc(sizeof(struct flist));
650 fp->type = gp->ef_type;
656 emp_insque(&fp->queue, list);
660 free_flist(struct emp_qelem *list)
662 struct emp_qelem *qp, *next;
665 for (qp = list->q_forw; qp != list; qp = next) {
667 fp = (struct flist *)qp;
668 emp_remque(&fp->queue);
674 uid_eq(struct emp_qelem *elem, void *key)
676 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
679 static struct flist *
680 search_flist(struct emp_qelem *list, struct empobj *gp)
682 return (struct flist *)emp_searchque(list, gp, uid_eq);