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 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
177 if (item.ship.shp_item[I_GUN] == 0) {
178 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
181 if (item.ship.shp_item[I_SHELL] == 0) {
182 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
185 if (item.ship.shp_effic < 60) {
186 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
189 pr("%s%s ready to fire\n", sep, prship(&fship));
193 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
195 if (item.sect.sct_own != player->cnum)
197 if (item.sect.sct_type != SCT_FORTR)
199 if (item.sect.sct_effic < FORTEFF) {
200 pr("Fort not efficient enough to fire!\n");
203 if (item.sect.sct_item[I_GUN] == 0) {
204 pr("Not enough guns in sector %s!\n",
205 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
208 if (item.sect.sct_item[I_SHELL] == 0) {
209 pr("Not enough shells in sector %s!\n",
210 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
213 if (item.sect.sct_item[I_MILIT] < 5) {
214 pr("Not enough military in sector %s!\n",
215 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
218 pr("%sSector %s ready to fire\n", sep,
219 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
225 ptr = getstarg(player->argp[3], "Firing at? ", buf);
230 if (!issector(ptr)) {
232 if (vshipno < 0 || !getship(vshipno, &vship) ||
234 pr("No such ship exists!\n");
237 target = targ_ship; /* targ_ship vs. targ_sub decided below */
238 vict = vship.shp_own;
241 if (!getsect(x, y, &vsect)) {
242 pr("No such sector exists!\n");
246 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
247 pr("No such sector exists!\n");
250 /* We check the sector type, but we only use it for damage, not
251 reporting. That way, you don't get extra information you wouldn't
252 normally get. Besides, what if they want to slam water? :) */
253 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
257 vict = vsect.sct_own;
262 trange = mapdist(x, y, fx, fy);
264 if (type == EF_SHIP) {
265 if (!check_ship_ok(&fship))
267 if (target == targ_ship) {
268 if (fship.shp_uid == vship.shp_uid) {
269 pr("You can't fire upon yourself!\n");
273 range = shp_fire_range(&fship);
274 range2 = roundrange(range);
275 pr("range is %d.00 (%.2f)\n", range2, range);
276 /* Use depth charges against subs, but only when in range */
277 if (target == targ_ship && trange <= range2
278 && (mchr[vship.shp_type].m_flags & M_SUB)
279 && (mchr[fship.shp_type].m_flags & M_DCH))
281 if (target == targ_sub)
282 dam = shp_dchrg(&fship);
284 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");
305 range = lnd_fire_range(&fland);
306 range2 = roundrange(range);
307 pr("range is %d.00 (%.2f)\n", range2, range);
308 dam = lnd_fire(&fland);
309 fland.lnd_mission = 0;
310 putland(fland.lnd_uid, &fland);
311 if (CANT_HAPPEN(dam < 0)) {
315 if (target == targ_ship) {
316 if (chance(lnd_acc(&fland) / 100.0))
317 dam = ldround(dam / 2.0, 1);
320 if (!check_sect_ok(&fsect))
322 if (target == targ_land) {
323 if (fsect.sct_x == vsect.sct_x
324 && fsect.sct_y == vsect.sct_y) {
325 pr("You can't fire upon yourself!\n");
329 dam = fort_fire(&fsect);
331 if (CANT_HAPPEN(dam < 0)) {
335 range = fortrange(&fsect);
336 range2 = roundrange(range);
337 pr("range is %d.00 (%.2f)\n", range2, range);
353 * If the player fires guns at a submarine, take care not to
354 * disclose it's a submarine: pretend the target is out of range.
356 if (target == targ_ship && (mchr[vship.shp_type].m_flags & M_SUB))
358 if (trange > range2) {
359 pr("Target out of range.\n");
366 nreport(player->cnum, N_SCT_SHELL, vict, 1);
367 if (vict && vict != player->cnum)
369 "Country #%d shelled sector %s for %d damage.\n",
370 player->cnum, xyas(x, y, vict), dam);
371 pr("Shells hit sector %s for %d damage.\n",
372 xyas(x, y, player->cnum), dam);
375 nreport(player->cnum, N_SHP_SHELL, vict, 1);
378 if (vict && vict != player->cnum) {
380 "Country #%d shelled %s in %s for %d damage.\n",
381 player->cnum, prship(&vship),
382 xyas(vship.shp_x, vship.shp_y, vict), dam);
384 pr("Shells hit %s in %s for %d damage.\n",
386 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
389 /* Ok, now, check if we had a bogus target. If so,
390 just continue on, since there is no defender. */
391 if (target == targ_bogus)
394 if (type == EF_LAND) {
395 getsect(fland.lnd_x, fland.lnd_y, &fsect);
396 attgp = (struct empobj *)&fsect;
398 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
401 getsect(x, y, &vsect);
402 sectdamage(&vsect, dam);
406 getship(vshipno, &vship);
407 shipdamage(&vship, dam);
408 if (vship.shp_effic < SHIP_MINEFF)
409 pr("%s sunk!\n", prsub(&vship));
410 putship(vship.shp_uid, &vship);
411 if (dam && (vship.shp_rflags & RET_INJURED))
412 retreat_ship(&vship, vict, 'i');
413 else if (target == targ_sub && (vship.shp_rflags & RET_DCHRGED))
414 retreat_ship(&vship, vict, 'd');
415 else if (totaldefdam == 0 && (vship.shp_rflags & RET_HELPLESS))
416 retreat_ship(&vship, vict, 'h');
419 switch (attgp->ef_type) {
423 if ((target == targ_ship) || (target == targ_sub)) {
424 if (fship.shp_effic > SHIP_MINEFF) {
425 shp_missdef(&fship, vict);
434 free_flist(&defended);
436 odds = ((double)ndefending) / ((double)nfiring);
439 do_defdam(&fired, odds);
444 defend(struct emp_qelem *al, struct emp_qelem *dl,
445 struct empobj *attgp, natid vict, int *nd)
450 dam = quiet_bigdef(attgp->ef_type, dl, vict,
451 attgp->own, attgp->x, attgp->y, &nfiring);
455 add_to_flist(al, attgp, dam, vict);
462 do_defdam(struct emp_qelem *list, double odds)
470 struct emp_qelem *qp, *next;
472 for (qp = list->q_forw; qp != list; qp = next) {
474 fp = (struct flist *)qp;
475 if (fp->type == EF_SHIP) {
476 if (!getship(fp->uid, &ship) || !ship.shp_own)
481 pr("\nDefenders fire back!\n");
484 dam = odds * fp->defdam;
486 if (fp->type == EF_SHIP) {
488 pr("Return fire hit %s in %s for %d damage.\n",
490 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
493 "Return fire hit %s in %s for %d damage.\n",
494 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
495 shipdamage(&ship, dam);
496 putship(ship.shp_uid, &ship);
498 CANT_HAPPEN(fp->type != EF_SECTOR);
499 getsect(fp->x, fp->y, §);
501 pr("Return fire hit sector %s for %d damage.\n",
502 xyas(fp->x, fp->y, player->cnum), dam);
503 sectdamage(§, dam);
506 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
507 xyas(fp->x, fp->y, vict), dam);
509 emp_remque(&fp->queue);
515 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
516 coord ax, coord ay, int *nfiring)
523 struct sctstr firing;
530 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
531 while (nxtitem(&ni, &ship)) {
532 if (!feels_like_helping(ship.shp_own, own, aown))
535 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
538 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
539 erange = torprange(&ship);
540 if (roundrange(erange) < ni.curdist)
542 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
544 fp = search_flist(list, (struct empobj *)&ship);
548 dam2 = shp_torp(&ship, 0);
549 putship(ship.shp_uid, &ship);
553 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
556 erange = shp_fire_range(&ship);
557 if (roundrange(erange) < ni.curdist)
559 fp = search_flist(list, (struct empobj *)&ship);
563 dam2 = shp_fire(&ship);
564 putship(ship.shp_uid, &ship);
568 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
572 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
575 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
576 while (nxtitem(&ni, &land)) {
577 if (!feels_like_helping(land.lnd_own, own, aown))
580 erange = lnd_fire_range(&land);
581 if (roundrange(erange) < ni.curdist)
584 fp = search_flist(list, (struct empobj *)&land);
588 dam2 = lnd_fire(&land);
589 putland(land.lnd_uid, &land);
596 add_to_flist(list, (struct empobj *)&land, dam2, 0);
597 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
598 if (type == EF_SHIP) {
599 if (chance(lnd_acc(&land) / 100.0))
600 dam2 = ldround(dam2 / 2.0, 1);
606 * Determine if any nearby gun-equipped sectors are within
607 * range and able to fire at an attacker. Firing sectors
608 * need to have guns, shells, and military. Sector being
609 * attacked is x,y -- attacker is at ax,ay.
612 if (!opt_NO_FORT_FIRE) {
613 snxtsct_dist(&ns, ax, ay, 8);
614 while (nxtsct(&ns, &firing)) {
615 if (!feels_like_helping(firing.sct_own, own, aown))
618 erange = fortrange(&firing);
619 if (roundrange(erange) < ns.curdist)
622 fp = search_flist(list, (struct empobj *)&firing);
626 dam2 = fort_fire(&firing);
633 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
634 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
639 return *nfiring == 0 ? 0 : dam / *nfiring;
643 add_to_flist(struct emp_qelem *list,
644 struct empobj *gp, int dam, natid victim)
648 fp = malloc(sizeof(struct flist));
649 fp->type = gp->ef_type;
655 emp_insque(&fp->queue, list);
659 free_flist(struct emp_qelem *list)
661 struct emp_qelem *qp, *next;
664 for (qp = list->q_forw; qp != list; qp = next) {
666 fp = (struct flist *)qp;
667 emp_remque(&fp->queue);
673 uid_eq(struct emp_qelem *elem, void *key)
675 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
678 static struct flist *
679 search_flist(struct emp_qelem *list, struct empobj *gp)
681 return (struct flist *)emp_searchque(list, gp, uid_eq);