2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2013, 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-2011
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_SHELL] == 0) {
149 pr("%s -- not enough shells\n", prland(&fland));
154 } else if (type == EF_SHIP) {
155 if (!getship(item.ship.shp_uid, &fship))
157 if (item.ship.shp_own != player->cnum)
159 if (item.ship.shp_item[I_MILIT] < 1) {
160 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
163 if (mchr[item.ship.shp_type].m_glim == 0
164 && !(mchr[fship.shp_type].m_flags & M_DCH)) {
165 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
168 if (item.ship.shp_item[I_GUN] == 0) {
169 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
172 if (item.ship.shp_item[I_SHELL] == 0) {
173 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
176 if (item.ship.shp_effic < 60) {
177 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
183 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
185 if (item.sect.sct_own != player->cnum)
187 if (item.sect.sct_type != SCT_FORTR)
189 if (item.sect.sct_effic < FORTEFF) {
190 pr("Fort not efficient enough to fire!\n");
193 if (item.sect.sct_item[I_GUN] == 0) {
194 pr("Not enough guns in sector %s!\n",
195 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
198 if (item.sect.sct_item[I_SHELL] == 0) {
199 pr("Not enough shells in sector %s!\n",
200 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
203 if (item.sect.sct_item[I_MILIT] < 5) {
204 pr("Not enough military in sector %s!\n",
205 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
208 pr("\nSector %s firing\n",
209 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
214 ptr = getstarg(player->argp[3], "Firing at? ", buf);
219 if (!issector(ptr)) {
221 if (vshipno < 0 || !getship(vshipno, &vship) ||
223 pr("No such ship exists!\n");
226 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
227 targ_sub : targ_ship;
228 vict = vship.shp_own;
231 if (!getsect(x, y, &vsect)) {
232 pr("No such sector exists!\n");
236 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
237 pr("No such sector exists!\n");
240 /* We check the sector type, but we only use it for damage, not
241 reporting. That way, you don't get extra information you wouldn't
242 normally get. Besides, what if they want to slam water? :) */
243 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
247 vict = vsect.sct_own;
252 trange = mapdist(x, y, fx, fy);
254 if (type == EF_SHIP) {
255 if (!check_ship_ok(&fship))
257 if (fship.shp_own != player->cnum) {
258 pr("Not your ship!\n");
261 if (target == targ_sub || target == targ_ship) {
262 if (fship.shp_uid == vship.shp_uid) {
263 pr("You can't fire upon yourself!\n");
267 if (fship.shp_item[I_MILIT] < 1) {
268 pr("Not enough military for firing crew.\n");
271 if (fship.shp_effic < 60) {
272 pr("Ship #%d is crippled (%d%%)\n",
273 fship.shp_uid, fship.shp_effic);
276 range = shp_fire_range(&fship);
277 range2 = roundrange(range);
278 pr("range is %d.00 (%.2f)\n", range2, range);
279 if (target == targ_sub
281 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
282 dam = shp_dchrg(&fship);
284 if (target == targ_sub)
285 /* Don't tell it's a sub */
287 if (fship.shp_item[I_GUN] == 0) {
288 pr("Insufficient arms.\n");
291 dam = shp_fire(&fship);
293 putship(fship.shp_uid, &fship);
298 if (opt_NOMOBCOST == 0) {
299 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
300 putship(fship.shp_uid, &fship);
302 } else if (type == EF_LAND) {
303 if (!check_land_ok(&fland))
305 if (fland.lnd_own != player->cnum) {
306 pr("Not your unit!\n");
310 if (target == targ_land) {
311 if (fland.lnd_x == vsect.sct_x
312 && fland.lnd_y == vsect.sct_y) {
313 pr("You can't fire upon yourself!\n");
318 if (lchr[fland.lnd_type].l_dam == 0) {
319 pr("Unit %d cannot fire!\n", fland.lnd_uid);
322 if (fland.lnd_item[I_GUN] == 0) {
323 pr("%s -- not enough guns\n", prland(&fland));
327 range = lnd_fire_range(&fland);
328 range2 = roundrange(range);
329 pr("range is %d.00 (%.2f)\n", range2, range);
330 if (target == targ_sub) {
331 /* Don't tell it's a sub */
335 dam = lnd_fire(&fland);
336 putland(fland.lnd_uid, &fland);
341 if (target == targ_ship) {
342 if (chance(lnd_acc(&fland) / 100.0))
343 dam = ldround(dam / 2.0, 1);
346 if (!check_sect_ok(&fsect))
348 if (fsect.sct_own != player->cnum ||
349 fsect.sct_type != SCT_FORTR) {
350 pr("No fortress at %s\n",
351 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
354 if (target == targ_land) {
355 if (fsect.sct_x == vsect.sct_x
356 && fsect.sct_y == vsect.sct_y) {
357 pr("You can't fire upon yourself!\n");
361 if (fsect.sct_item[I_GUN] == 0) {
362 pr("Insufficient arms.\n");
365 if (fsect.sct_item[I_MILIT] < 5) {
366 pr("Not enough military for firing crew.\n");
369 dam = fort_fire(&fsect);
375 range = fortrange(&fsect);
376 range2 = roundrange(range);
377 pr("range is %d.00 (%.2f)\n", range2, range);
378 if (target == targ_sub) {
379 /* Don't tell it's a sub */
383 if (trange > range2) {
384 pr("Target out of range.\n");
390 fland.lnd_mission = 0;
391 putland(fland.lnd_uid, &fland);
394 fship.shp_mission = 0;
395 putship(fship.shp_uid, &fship);
404 if (!trechk(player->cnum, vict, SEAFIR))
408 if (!trechk(player->cnum, vict, SUBFIR))
412 if (!trechk(player->cnum, vict, LANFIR))
433 nreport(player->cnum, N_SCT_SHELL, vict, 1);
434 if (vict && vict != player->cnum)
436 "Country #%d shelled sector %s for %d damage.\n",
437 player->cnum, xyas(x, y, vict), dam);
438 pr("Shells hit sector %s for %d damage.\n",
439 xyas(x, y, player->cnum), dam);
442 nreport(player->cnum, N_SHP_SHELL, vict, 1);
447 "Country #%d shelled %s in %s for %d damage.\n",
448 player->cnum, prship(&vship),
449 xyas(vship.shp_x, vship.shp_y, vict), dam);
451 pr("Shells hit %s in %s for %d damage.\n",
453 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
456 /* Ok, now, check if we had a bogus target. If so,
457 just continue on, since there is no defender. */
458 if (target == targ_bogus)
461 if (type == EF_LAND) {
462 getsect(fland.lnd_x, fland.lnd_y, &fsect);
463 attgp = (struct empobj *)&fsect;
465 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
468 getsect(x, y, &vsect);
469 sectdamage(&vsect, dam);
473 getship(vshipno, &vship);
474 check_retreat_and_do_shipdamage(&vship, dam);
475 if (vship.shp_effic < SHIP_MINEFF)
476 pr("%s sunk!\n", prsub(&vship));
477 else if (target == targ_sub
478 && (vship.shp_rflags & RET_DCHRGED)
479 && !(vship.shp_rflags & RET_INJURED))
480 retreat_ship(&vship, 'd');
481 putship(vship.shp_uid, &vship);
484 if (totaldefdam == 0 && target == targ_ship
485 && (vship.shp_rflags & RET_HELPLESS)
486 && !(vship.shp_rflags & RET_INJURED))
487 retreat_ship(&vship, 'h');
488 switch (attgp->ef_type) {
493 if ((target == targ_ship) || (target == targ_sub)) {
494 if (fship.shp_effic > SHIP_MINEFF) {
495 shp_missdef(&fship, vict);
498 putship(fship.shp_uid, &fship);
505 free_flist(&defended);
507 odds = ((double)ndefending) / ((double)nfiring);
510 do_defdam(&fired, odds);
515 defend(struct emp_qelem *al, struct emp_qelem *dl,
516 struct empobj *attgp, natid vict, int *nd)
521 dam = quiet_bigdef(attgp->ef_type, dl, vict,
522 attgp->own, attgp->x, attgp->y, &nfiring);
526 add_to_flist(al, attgp, dam, vict);
533 do_defdam(struct emp_qelem *list, double odds)
541 struct emp_qelem *qp, *next;
543 for (qp = list->q_forw; qp != list; qp = next) {
545 fp = (struct flist *)qp;
546 if (fp->type == EF_SHIP) {
547 if (!getship(fp->uid, &ship) || !ship.shp_own)
552 pr("\nDefenders fire back!\n");
555 dam = odds * fp->defdam;
557 if (fp->type == EF_SHIP) {
559 pr("Return fire hit %s in %s for %d damage.\n",
561 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
564 "Return fire hit %s in %s for %d damage.\n",
565 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
566 shipdamage(&ship, dam);
567 putship(ship.shp_uid, &ship);
569 CANT_HAPPEN(fp->type != EF_SECTOR);
570 getsect(fp->x, fp->y, §);
572 pr("Return fire hit sector %s for %d damage.\n",
573 xyas(fp->x, fp->y, player->cnum), dam);
574 sectdamage(§, dam);
577 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
578 xyas(fp->x, fp->y, vict), dam);
580 emp_remque(&fp->queue);
586 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
587 coord ax, coord ay, int *nfiring)
594 struct sctstr firing;
601 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
602 while (nxtitem(&ni, &ship)) {
603 if (!feels_like_helping(ship.shp_own, own, aown))
606 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
609 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
610 erange = torprange(&ship);
611 if (roundrange(erange) < ni.curdist)
613 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
615 fp = search_flist(list, (struct empobj *)&ship);
619 dam2 = shp_torp(&ship, 0);
620 putship(ship.shp_uid, &ship);
624 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
627 erange = shp_fire_range(&ship);
628 if (roundrange(erange) < ni.curdist)
630 fp = search_flist(list, (struct empobj *)&ship);
634 dam2 = shp_fire(&ship);
635 putship(ship.shp_uid, &ship);
639 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
643 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
646 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
647 while (nxtitem(&ni, &land)) {
648 if (!feels_like_helping(land.lnd_own, own, aown))
651 erange = lnd_fire_range(&land);
652 if (roundrange(erange) < ni.curdist)
655 fp = search_flist(list, (struct empobj *)&land);
659 dam2 = lnd_fire(&land);
660 putland(land.lnd_uid, &land);
667 add_to_flist(list, (struct empobj *)&land, dam2, 0);
668 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
669 if (type == EF_SHIP) {
670 if (chance(lnd_acc(&land) / 100.0))
671 dam2 = ldround(dam2 / 2.0, 1);
677 * Determine if any nearby gun-equipped sectors are within
678 * range and able to fire at an attacker. Firing sectors
679 * need to have guns, shells, and military. Sector being
680 * attacked is x,y -- attacker is at ax,ay.
683 if (!opt_NO_FORT_FIRE) {
684 snxtsct_dist(&ns, ax, ay, 8);
685 while (nxtsct(&ns, &firing)) {
686 if (!feels_like_helping(firing.sct_own, own, aown))
689 erange = fortrange(&firing);
690 if (roundrange(erange) < ns.curdist)
693 fp = search_flist(list, (struct empobj *)&firing);
697 dam2 = fort_fire(&firing);
704 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
705 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
710 return *nfiring == 0 ? 0 : dam / *nfiring;
714 add_to_flist(struct emp_qelem *list,
715 struct empobj *gp, int dam, natid victim)
719 fp = malloc(sizeof(struct flist));
720 fp->type = gp->ef_type;
726 emp_insque(&fp->queue, list);
730 free_flist(struct emp_qelem *list)
732 struct emp_qelem *qp, *next;
735 for (qp = list->q_forw; qp != list; qp = next) {
737 fp = (struct flist *)qp;
738 emp_remque(&fp->queue);
744 uid_eq(struct emp_qelem *elem, void *key)
746 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
749 static struct flist *
750 search_flist(struct emp_qelem *list, struct empobj *gp)
752 return (struct flist *)emp_searchque(list, gp, uid_eq);