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 fship.shp_mission = 0;
290 putship(fship.shp_uid, &fship);
291 if (CANT_HAPPEN(dam < 0)) {
295 if (opt_NOMOBCOST == 0) {
296 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
297 putship(fship.shp_uid, &fship);
299 } else if (type == EF_LAND) {
300 if (!check_land_ok(&fland))
302 if (target == targ_land) {
303 if (fland.lnd_x == vsect.sct_x
304 && fland.lnd_y == vsect.sct_y) {
305 pr("You can't fire upon yourself!\n");
310 range = lnd_fire_range(&fland);
311 range2 = roundrange(range);
312 pr("range is %d.00 (%.2f)\n", range2, range);
313 if (target == targ_sub) {
314 /* Don't tell it's a sub */
318 dam = lnd_fire(&fland);
319 fland.lnd_mission = 0;
320 putland(fland.lnd_uid, &fland);
321 if (CANT_HAPPEN(dam < 0)) {
325 if (target == targ_ship) {
326 if (chance(lnd_acc(&fland) / 100.0))
327 dam = ldround(dam / 2.0, 1);
330 if (!check_sect_ok(&fsect))
332 if (target == targ_land) {
333 if (fsect.sct_x == vsect.sct_x
334 && fsect.sct_y == vsect.sct_y) {
335 pr("You can't fire upon yourself!\n");
339 dam = fort_fire(&fsect);
341 if (CANT_HAPPEN(dam < 0)) {
345 range = fortrange(&fsect);
346 range2 = roundrange(range);
347 pr("range is %d.00 (%.2f)\n", range2, range);
348 if (target == targ_sub) {
349 /* Don't tell it's a sub */
353 if (trange > range2) {
354 pr("Target out of range.\n");
372 nreport(player->cnum, N_SCT_SHELL, vict, 1);
373 if (vict && vict != player->cnum)
375 "Country #%d shelled sector %s for %d damage.\n",
376 player->cnum, xyas(x, y, vict), dam);
377 pr("Shells hit sector %s for %d damage.\n",
378 xyas(x, y, player->cnum), dam);
381 nreport(player->cnum, N_SHP_SHELL, vict, 1);
384 if (vict && vict != player->cnum) {
386 "Country #%d shelled %s in %s for %d damage.\n",
387 player->cnum, prship(&vship),
388 xyas(vship.shp_x, vship.shp_y, vict), dam);
390 pr("Shells hit %s in %s for %d damage.\n",
392 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
395 /* Ok, now, check if we had a bogus target. If so,
396 just continue on, since there is no defender. */
397 if (target == targ_bogus)
400 if (type == EF_LAND) {
401 getsect(fland.lnd_x, fland.lnd_y, &fsect);
402 attgp = (struct empobj *)&fsect;
404 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
407 getsect(x, y, &vsect);
408 sectdamage(&vsect, dam);
412 getship(vshipno, &vship);
413 shipdamage(&vship, dam);
414 if (vship.shp_effic < SHIP_MINEFF)
415 pr("%s sunk!\n", prsub(&vship));
416 putship(vship.shp_uid, &vship);
417 if (dam && (vship.shp_rflags & RET_INJURED))
418 retreat_ship(&vship, vict, 'i');
419 else if (target == targ_sub && (vship.shp_rflags & RET_DCHRGED))
420 retreat_ship(&vship, vict, 'd');
421 else if (totaldefdam == 0 && (vship.shp_rflags & RET_HELPLESS))
422 retreat_ship(&vship, vict, 'h');
425 switch (attgp->ef_type) {
429 if ((target == targ_ship) || (target == targ_sub)) {
430 if (fship.shp_effic > SHIP_MINEFF) {
431 shp_missdef(&fship, vict);
440 free_flist(&defended);
442 odds = ((double)ndefending) / ((double)nfiring);
445 do_defdam(&fired, odds);
450 defend(struct emp_qelem *al, struct emp_qelem *dl,
451 struct empobj *attgp, natid vict, int *nd)
456 dam = quiet_bigdef(attgp->ef_type, dl, vict,
457 attgp->own, attgp->x, attgp->y, &nfiring);
461 add_to_flist(al, attgp, dam, vict);
468 do_defdam(struct emp_qelem *list, double odds)
476 struct emp_qelem *qp, *next;
478 for (qp = list->q_forw; qp != list; qp = next) {
480 fp = (struct flist *)qp;
481 if (fp->type == EF_SHIP) {
482 if (!getship(fp->uid, &ship) || !ship.shp_own)
487 pr("\nDefenders fire back!\n");
490 dam = odds * fp->defdam;
492 if (fp->type == EF_SHIP) {
494 pr("Return fire hit %s in %s for %d damage.\n",
496 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
499 "Return fire hit %s in %s for %d damage.\n",
500 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
501 shipdamage(&ship, dam);
502 putship(ship.shp_uid, &ship);
504 CANT_HAPPEN(fp->type != EF_SECTOR);
505 getsect(fp->x, fp->y, §);
507 pr("Return fire hit sector %s for %d damage.\n",
508 xyas(fp->x, fp->y, player->cnum), dam);
509 sectdamage(§, dam);
512 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
513 xyas(fp->x, fp->y, vict), dam);
515 emp_remque(&fp->queue);
521 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
522 coord ax, coord ay, int *nfiring)
529 struct sctstr firing;
536 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
537 while (nxtitem(&ni, &ship)) {
538 if (!feels_like_helping(ship.shp_own, own, aown))
541 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
544 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
545 erange = torprange(&ship);
546 if (roundrange(erange) < ni.curdist)
548 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
550 fp = search_flist(list, (struct empobj *)&ship);
554 dam2 = shp_torp(&ship, 0);
555 putship(ship.shp_uid, &ship);
559 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
562 erange = shp_fire_range(&ship);
563 if (roundrange(erange) < ni.curdist)
565 fp = search_flist(list, (struct empobj *)&ship);
569 dam2 = shp_fire(&ship);
570 putship(ship.shp_uid, &ship);
574 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
578 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
581 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
582 while (nxtitem(&ni, &land)) {
583 if (!feels_like_helping(land.lnd_own, own, aown))
586 erange = lnd_fire_range(&land);
587 if (roundrange(erange) < ni.curdist)
590 fp = search_flist(list, (struct empobj *)&land);
594 dam2 = lnd_fire(&land);
595 putland(land.lnd_uid, &land);
602 add_to_flist(list, (struct empobj *)&land, dam2, 0);
603 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
604 if (type == EF_SHIP) {
605 if (chance(lnd_acc(&land) / 100.0))
606 dam2 = ldround(dam2 / 2.0, 1);
612 * Determine if any nearby gun-equipped sectors are within
613 * range and able to fire at an attacker. Firing sectors
614 * need to have guns, shells, and military. Sector being
615 * attacked is x,y -- attacker is at ax,ay.
618 if (!opt_NO_FORT_FIRE) {
619 snxtsct_dist(&ns, ax, ay, 8);
620 while (nxtsct(&ns, &firing)) {
621 if (!feels_like_helping(firing.sct_own, own, aown))
624 erange = fortrange(&firing);
625 if (roundrange(erange) < ns.curdist)
628 fp = search_flist(list, (struct empobj *)&firing);
632 dam2 = fort_fire(&firing);
639 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
640 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
645 return *nfiring == 0 ? 0 : dam / *nfiring;
649 add_to_flist(struct emp_qelem *list,
650 struct empobj *gp, int dam, natid victim)
654 fp = malloc(sizeof(struct flist));
655 fp->type = gp->ef_type;
661 emp_insque(&fp->queue, list);
665 free_flist(struct emp_qelem *list)
667 struct emp_qelem *qp, *next;
670 for (qp = list->q_forw; qp != list; qp = next) {
672 fp = (struct flist *)qp;
673 emp_remque(&fp->queue);
679 uid_eq(struct emp_qelem *elem, void *key)
681 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
684 static struct flist *
685 search_flist(struct emp_qelem *list, struct empobj *gp)
687 return (struct flist *)emp_searchque(list, gp, uid_eq);