2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2014, 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-2014
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 };
85 enum targ_type target;
86 struct nstr_item nbst;
92 union empobj_storage item;
93 struct emp_qelem fired, defended;
98 emp_initque(&defended);
99 p = getstarg(player->argp[1],
100 "Firing from ship(s), sect(s), or land unit(s)? ", buf);
103 type = ef_byname_from(p, ef_with_guns);
104 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
105 pr("Fort firing is disabled.\n");
109 pr("Ships, land units or sectors only!\n");
112 if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
115 while (nxtitem(&nbst, &item)) {
116 if (type == EF_LAND) {
117 if (!getland(item.land.lnd_uid, &fland))
119 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
121 if (item.land.lnd_own != player->cnum)
124 if (lchr[fland.lnd_type].l_dam == 0) {
125 pr("Unit %d cannot fire!\n", fland.lnd_uid);
128 if (fland.lnd_item[I_MILIT] < 1) {
129 pr("Unit %d cannot fire because it has no military!\n",
133 if (fland.lnd_ship >= 0) {
134 pr("Unit %d cannot fire because it is on a ship!\n",
138 if (fland.lnd_land >= 0) {
139 pr("Unit %d cannot fire because it is on a land unit!\n",
143 if (fland.lnd_effic < LAND_MINFIREEFF) {
144 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
145 fland.lnd_uid, LAND_MINFIREEFF);
148 if (fland.lnd_item[I_GUN] == 0) {
149 pr("%s -- not enough guns\n", prland(&fland));
153 if (fland.lnd_item[I_SHELL] == 0) {
154 pr("%s -- not enough shells\n", prland(&fland));
159 } else if (type == EF_SHIP) {
160 if (!getship(item.ship.shp_uid, &fship))
162 if (item.ship.shp_own != player->cnum)
164 if (item.ship.shp_item[I_MILIT] < 1) {
165 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
168 if (mchr[item.ship.shp_type].m_glim == 0
169 && !(mchr[fship.shp_type].m_flags & M_DCH)) {
170 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
173 if (item.ship.shp_item[I_GUN] == 0) {
174 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
177 if (item.ship.shp_item[I_SHELL] == 0) {
178 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
181 if (item.ship.shp_effic < 60) {
182 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
188 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
190 if (item.sect.sct_own != player->cnum)
192 if (item.sect.sct_type != SCT_FORTR)
194 if (item.sect.sct_effic < FORTEFF) {
195 pr("Fort not efficient enough to fire!\n");
198 if (item.sect.sct_item[I_GUN] == 0) {
199 pr("Not enough guns in sector %s!\n",
200 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
203 if (item.sect.sct_item[I_SHELL] == 0) {
204 pr("Not enough shells in sector %s!\n",
205 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
208 if (item.sect.sct_item[I_MILIT] < 5) {
209 pr("Not enough military in sector %s!\n",
210 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
213 pr("\nSector %s firing\n",
214 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
219 ptr = getstarg(player->argp[3], "Firing at? ", buf);
224 if (!issector(ptr)) {
226 if (vshipno < 0 || !getship(vshipno, &vship) ||
228 pr("No such ship exists!\n");
231 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
232 targ_sub : targ_ship;
233 vict = vship.shp_own;
236 if (!getsect(x, y, &vsect)) {
237 pr("No such sector exists!\n");
241 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
242 pr("No such sector exists!\n");
245 /* We check the sector type, but we only use it for damage, not
246 reporting. That way, you don't get extra information you wouldn't
247 normally get. Besides, what if they want to slam water? :) */
248 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
252 vict = vsect.sct_own;
257 trange = mapdist(x, y, fx, fy);
259 if (type == EF_SHIP) {
260 if (!check_ship_ok(&fship))
262 if (target == targ_sub || target == targ_ship) {
263 if (fship.shp_uid == vship.shp_uid) {
264 pr("You can't fire upon yourself!\n");
268 range = shp_fire_range(&fship);
269 range2 = roundrange(range);
270 pr("range is %d.00 (%.2f)\n", range2, range);
271 if (target == targ_sub
273 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
274 dam = shp_dchrg(&fship);
276 if (target == targ_sub)
277 /* Don't tell it's a sub */
279 if (fship.shp_item[I_GUN] == 0) {
280 pr("Insufficient arms.\n");
283 dam = shp_fire(&fship);
285 putship(fship.shp_uid, &fship);
286 if (dam == 0 || CANT_HAPPEN(dam < 0)) {
290 if (opt_NOMOBCOST == 0) {
291 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
292 putship(fship.shp_uid, &fship);
294 } else if (type == EF_LAND) {
295 if (!check_land_ok(&fland))
297 if (target == targ_land) {
298 if (fland.lnd_x == vsect.sct_x
299 && fland.lnd_y == vsect.sct_y) {
300 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 if (target == targ_sub) {
309 /* Don't tell it's a sub */
313 dam = lnd_fire(&fland);
314 putland(fland.lnd_uid, &fland);
315 if (CANT_HAPPEN(dam < 0)) {
319 if (target == targ_ship) {
320 if (chance(lnd_acc(&fland) / 100.0))
321 dam = ldround(dam / 2.0, 1);
324 if (!check_sect_ok(&fsect))
326 if (target == targ_land) {
327 if (fsect.sct_x == vsect.sct_x
328 && fsect.sct_y == vsect.sct_y) {
329 pr("You can't fire upon yourself!\n");
333 dam = fort_fire(&fsect);
335 if (CANT_HAPPEN(dam < 0)) {
339 range = fortrange(&fsect);
340 range2 = roundrange(range);
341 pr("range is %d.00 (%.2f)\n", range2, range);
342 if (target == targ_sub) {
343 /* Don't tell it's a sub */
347 if (trange > range2) {
348 pr("Target out of range.\n");
354 fland.lnd_mission = 0;
355 putland(fland.lnd_uid, &fland);
358 fship.shp_mission = 0;
359 putship(fship.shp_uid, &fship);
368 if (!trechk(player->cnum, vict, SEAFIR))
372 if (!trechk(player->cnum, vict, SUBFIR))
376 if (!trechk(player->cnum, vict, LANFIR))
397 nreport(player->cnum, N_SCT_SHELL, vict, 1);
398 if (vict && vict != player->cnum)
400 "Country #%d shelled sector %s for %d damage.\n",
401 player->cnum, xyas(x, y, vict), dam);
402 pr("Shells hit sector %s for %d damage.\n",
403 xyas(x, y, player->cnum), dam);
406 nreport(player->cnum, N_SHP_SHELL, vict, 1);
409 if (vict && vict != player->cnum) {
411 "Country #%d shelled %s in %s for %d damage.\n",
412 player->cnum, prship(&vship),
413 xyas(vship.shp_x, vship.shp_y, vict), dam);
415 pr("Shells hit %s in %s for %d damage.\n",
417 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
420 /* Ok, now, check if we had a bogus target. If so,
421 just continue on, since there is no defender. */
422 if (target == targ_bogus)
425 if (type == EF_LAND) {
426 getsect(fland.lnd_x, fland.lnd_y, &fsect);
427 attgp = (struct empobj *)&fsect;
429 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
432 getsect(x, y, &vsect);
433 sectdamage(&vsect, dam);
437 getship(vshipno, &vship);
438 check_retreat_and_do_shipdamage(&vship, dam);
439 if (vship.shp_effic < SHIP_MINEFF)
440 pr("%s sunk!\n", prsub(&vship));
441 else if (target == targ_sub
442 && (vship.shp_rflags & RET_DCHRGED)
443 && !(vship.shp_rflags & RET_INJURED))
444 retreat_ship(&vship, 'd');
445 putship(vship.shp_uid, &vship);
448 if (totaldefdam == 0 && target == targ_ship
449 && (vship.shp_rflags & RET_HELPLESS)
450 && !(vship.shp_rflags & RET_INJURED))
451 retreat_ship(&vship, 'h');
452 switch (attgp->ef_type) {
457 if ((target == targ_ship) || (target == targ_sub)) {
458 if (fship.shp_effic > SHIP_MINEFF) {
459 shp_missdef(&fship, vict);
462 putship(fship.shp_uid, &fship);
469 free_flist(&defended);
471 odds = ((double)ndefending) / ((double)nfiring);
474 do_defdam(&fired, odds);
479 defend(struct emp_qelem *al, struct emp_qelem *dl,
480 struct empobj *attgp, natid vict, int *nd)
485 dam = quiet_bigdef(attgp->ef_type, dl, vict,
486 attgp->own, attgp->x, attgp->y, &nfiring);
490 add_to_flist(al, attgp, dam, vict);
497 do_defdam(struct emp_qelem *list, double odds)
505 struct emp_qelem *qp, *next;
507 for (qp = list->q_forw; qp != list; qp = next) {
509 fp = (struct flist *)qp;
510 if (fp->type == EF_SHIP) {
511 if (!getship(fp->uid, &ship) || !ship.shp_own)
516 pr("\nDefenders fire back!\n");
519 dam = odds * fp->defdam;
521 if (fp->type == EF_SHIP) {
523 pr("Return fire hit %s in %s for %d damage.\n",
525 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
528 "Return fire hit %s in %s for %d damage.\n",
529 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
530 shipdamage(&ship, dam);
531 putship(ship.shp_uid, &ship);
533 CANT_HAPPEN(fp->type != EF_SECTOR);
534 getsect(fp->x, fp->y, §);
536 pr("Return fire hit sector %s for %d damage.\n",
537 xyas(fp->x, fp->y, player->cnum), dam);
538 sectdamage(§, dam);
541 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
542 xyas(fp->x, fp->y, vict), dam);
544 emp_remque(&fp->queue);
550 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
551 coord ax, coord ay, int *nfiring)
558 struct sctstr firing;
565 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
566 while (nxtitem(&ni, &ship)) {
567 if (!feels_like_helping(ship.shp_own, own, aown))
570 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
573 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
574 erange = torprange(&ship);
575 if (roundrange(erange) < ni.curdist)
577 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
579 fp = search_flist(list, (struct empobj *)&ship);
583 dam2 = shp_torp(&ship, 0);
584 putship(ship.shp_uid, &ship);
588 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
591 erange = shp_fire_range(&ship);
592 if (roundrange(erange) < ni.curdist)
594 fp = search_flist(list, (struct empobj *)&ship);
598 dam2 = shp_fire(&ship);
599 putship(ship.shp_uid, &ship);
603 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
607 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
610 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
611 while (nxtitem(&ni, &land)) {
612 if (!feels_like_helping(land.lnd_own, own, aown))
615 erange = lnd_fire_range(&land);
616 if (roundrange(erange) < ni.curdist)
619 fp = search_flist(list, (struct empobj *)&land);
623 dam2 = lnd_fire(&land);
624 putland(land.lnd_uid, &land);
631 add_to_flist(list, (struct empobj *)&land, dam2, 0);
632 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
633 if (type == EF_SHIP) {
634 if (chance(lnd_acc(&land) / 100.0))
635 dam2 = ldround(dam2 / 2.0, 1);
641 * Determine if any nearby gun-equipped sectors are within
642 * range and able to fire at an attacker. Firing sectors
643 * need to have guns, shells, and military. Sector being
644 * attacked is x,y -- attacker is at ax,ay.
647 if (!opt_NO_FORT_FIRE) {
648 snxtsct_dist(&ns, ax, ay, 8);
649 while (nxtsct(&ns, &firing)) {
650 if (!feels_like_helping(firing.sct_own, own, aown))
653 erange = fortrange(&firing);
654 if (roundrange(erange) < ns.curdist)
657 fp = search_flist(list, (struct empobj *)&firing);
661 dam2 = fort_fire(&firing);
668 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
669 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
674 return *nfiring == 0 ? 0 : dam / *nfiring;
678 add_to_flist(struct emp_qelem *list,
679 struct empobj *gp, int dam, natid victim)
683 fp = malloc(sizeof(struct flist));
684 fp->type = gp->ef_type;
690 emp_insque(&fp->queue, list);
694 free_flist(struct emp_qelem *list)
696 struct emp_qelem *qp, *next;
699 for (qp = list->q_forw; qp != list; qp = next) {
701 fp = (struct flist *)qp;
702 emp_remque(&fp->queue);
708 uid_eq(struct emp_qelem *elem, void *key)
710 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
713 static struct flist *
714 search_flist(struct emp_qelem *list, struct empobj *gp)
716 return (struct flist *)emp_searchque(list, gp, uid_eq);