2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure
6 * This program 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 2 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, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * See files README, COPYING and CREDITS in the root of the source
23 * tree for related information and legal notices. It is expected
24 * that future projects/authors will amend these files as needed.
28 * multifire.c: Fire at other sectors/ships
30 * Known contributors to this file:
42 targ_land, targ_ship, targ_sub, targ_unit, targ_bogus
46 struct emp_qelem queue; /* list of fired things */
47 short type; /* EF_SECTOR, EF_SHIP or EF_LAND */
50 int defdam; /* damage defenders did */
54 static int defend(struct emp_qelem *, struct emp_qelem *,
55 struct empobj *, natid, int *);
56 static void do_defdam(struct emp_qelem *, double);
57 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
59 static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
60 static void free_flist(struct emp_qelem *);
61 static struct flist *search_flist(struct emp_qelem *, struct empobj *);
66 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
86 enum targ_type target;
89 struct nstr_item nbst;
95 union empobj_storage item;
96 struct emp_qelem fired, defended;
101 emp_initque(&defended);
102 if (!(p = getstarg(player->argp[1],
103 "Firing from ship(s), sect(s), or land unit(s)? ",
106 type = ef_byname_from(p, ef_with_guns);
107 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
108 pr("Fort firing is disabled.\n");
112 pr("Ships, land units or sectors only!\n");
115 if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
119 if (!snxtitem(&nbst, type, ptr))
122 if (player->aborted) {
123 pr("Fire aborted.\n");
126 while (nxtitem(&nbst, &item)) {
127 if (type == EF_LAND) {
128 if (!getland(item.land.lnd_uid, &fland))
130 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
132 if (item.land.lnd_own != player->cnum)
135 if (lchr[fland.lnd_type].l_dam == 0) {
136 pr("Unit %d cannot fire!\n", fland.lnd_uid);
139 if (fland.lnd_item[I_MILIT] < 1) {
140 pr("Unit %d cannot fire because it has no military!\n",
144 if (fland.lnd_ship >= 0) {
145 pr("Unit %d cannot fire because it is on a ship!\n",
149 if (fland.lnd_land >= 0) {
150 pr("Unit %d cannot fire because it is on a land unit!\n",
154 if (fland.lnd_effic < LAND_MINFIREEFF) {
155 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
156 fland.lnd_uid, LAND_MINFIREEFF);
159 if (fland.lnd_item[I_SHELL] == 0) {
160 pr("%s -- not enough shells\n", prland(&fland));
163 } else if (type == EF_SHIP) {
164 if (!getship(item.ship.shp_uid, &fship))
166 if (item.ship.shp_own != player->cnum)
168 if (item.ship.shp_item[I_MILIT] < 1) {
169 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
172 if (mchr[item.ship.shp_type].m_glim == 0) {
173 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
176 if (item.ship.shp_item[I_GUN] == 0) {
177 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
180 if (item.ship.shp_item[I_SHELL] == 0) {
181 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
184 if (item.ship.shp_effic < 60) {
185 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
188 } else if (type == EF_SECTOR) {
189 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
191 if (item.sect.sct_own != player->cnum)
193 if (item.sect.sct_type != SCT_FORTR)
195 if (item.sect.sct_effic < FORTEFF) {
196 pr("Fort not efficient enough to fire!\n");
199 if (item.sect.sct_item[I_GUN] == 0) {
200 pr("Not enough guns in sector %s!\n",
201 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
204 if (item.sect.sct_item[I_SHELL] == 0) {
205 pr("Not enough shells in sector %s!\n",
206 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
209 if (item.sect.sct_item[I_MILIT] < 5) {
210 pr("Not enough military in sector %s!\n",
211 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
214 pr("\nSector %s firing\n",
215 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
217 if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
220 if (player->aborted) {
221 pr("Fire aborted.\n");
225 (void)strcpy(vbuf, ptr);
230 if (target == targ_ship) {
231 vshipno = atoi(vbuf);
232 if (vshipno < 0 || !getship(vshipno, &vship) ||
234 pr("No such ship exists!\n");
237 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
238 targ_sub : targ_ship;
239 vict = vship.shp_own;
242 if (!getsect(x, y, &vsect)) {
243 pr("No such sector exists!\n");
247 if (!sarg_xy(vbuf, &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;
262 if (type == EF_SHIP) {
263 if (fship.shp_own != player->cnum) {
264 pr("Not your ship!\n");
267 if (target == targ_sub || target == targ_ship) {
268 if (fship.shp_uid == vship.shp_uid) {
269 pr("You can't fire upon yourself!\n");
275 if ((mil = fship.shp_item[I_MILIT]) < 1) {
276 pr("Not enough military for firing crew.\n");
279 if (fship.shp_effic < 60) {
280 pr("Ship #%d is crippled (%d%%)\n",
281 fship.shp_uid, fship.shp_effic);
284 range = shp_fire_range(&fship);
285 range2 = roundrange(range);
286 pr("range is %d.00 (%.2f)\n", range2, range);
287 if (target == targ_sub
288 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
289 dam = shp_dchrg(&fship);
290 putship(fship.shp_uid, &fship);
292 pr("Not enough shells for depth charge!\n");
296 if (target == targ_sub)
297 /* Don't tell it's a sub */
299 if (fship.shp_item[I_GUN] == 0) {
300 pr("Insufficient arms.\n");
303 dam = shp_fire(&fship);
304 putship(fship.shp_uid, &fship);
310 if (opt_NOMOBCOST == 0) {
311 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
312 putship(fship.shp_uid, &fship);
314 } else if (type == EF_LAND) {
315 if (fland.lnd_own != player->cnum) {
316 pr("Not your unit!\n");
320 if (target == targ_land) {
321 if (fland.lnd_x == vsect.sct_x
322 && fland.lnd_y == vsect.sct_y) {
323 pr("You can't fire upon yourself!\n");
331 if (lchr[fland.lnd_type].l_dam == 0) {
332 pr("Unit %d cannot fire!\n", fland.lnd_uid);
335 if (fland.lnd_item[I_GUN] == 0) {
336 pr("%s -- not enough guns\n", prland(&fland));
340 range = lnd_fire_range(&fland);
341 range2 = roundrange(range);
342 pr("range is %d.00 (%.2f)\n", range2, range);
343 if (target == targ_sub) {
344 /* Don't tell it's a sub */
348 dam = lnd_fire(&fland);
349 putland(fland.lnd_uid, &fland);
354 if (target == targ_ship) {
355 if (chance(lnd_acc(&fland) / 100.0))
356 dam = ldround(dam / 2.0, 1);
361 if (fsect.sct_own != player->cnum ||
362 fsect.sct_type != SCT_FORTR) {
363 pr("No fortress at %s\n",
364 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
367 if (target == targ_land) {
368 if (fsect.sct_x == vsect.sct_x
369 && fsect.sct_y == vsect.sct_y) {
370 pr("You can't fire upon yourself!\n");
374 if (fsect.sct_item[I_GUN] == 0) {
375 pr("Insufficient arms.\n");
378 if (fsect.sct_item[I_MILIT] < 5) {
379 pr("Not enough military for firing crew.\n");
382 dam = fort_fire(&fsect);
388 range = fortrange(&fsect);
389 range2 = roundrange(range);
390 pr("range is %d.00 (%.2f)\n", range2, range);
391 if (target == targ_sub) {
392 /* Don't tell it's a sub */
396 trange = mapdist(x, y, fx, fy);
397 if (trange > range2) {
398 pr("Target out of range.\n");
404 fland.lnd_mission = 0;
405 putland(fland.lnd_uid, &fland);
408 fship.shp_mission = 0;
409 putship(fship.shp_uid, &fship);
418 if (!trechk(player->cnum, vict, SEAFIR))
422 if (!trechk(player->cnum, vict, SUBFIR))
427 if (!trechk(player->cnum, vict, LANFIR))
435 if (target == targ_land) {
436 natp = getnatp(player->cnum);
437 rel = getrel(natp, vict);
438 if ((rel != AT_WAR) && (player->cnum != vict) &&
439 (vict) && (vsect.sct_oldown != player->cnum)) {
440 pr("You're not at war with them!\n");
450 if (vship.shp_rflags & RET_DCHRGED)
451 retreat_ship(&vship, 'd');
456 prb = range2 ? (double)trange / range2 : 1.0;
459 pr("Wind deflects shells.\n");
460 /* dam = (int)(dam / 2.0);*/
461 dam *= (90 - (random() % 11)) / 100.0;
470 nreport(player->cnum, N_SCT_SHELL, vict, 1);
471 if (vict && vict != player->cnum)
473 "Country #%d shelled sector %s for %d damage.\n",
474 player->cnum, xyas(x, y, vict), dam);
475 pr("Shells hit sector %s for %d damage.\n",
476 xyas(x, y, player->cnum), dam);
477 /* Ok, it wasn't a bogus target, so do damage. */
478 if (target != targ_bogus)
479 sectdamage(&vsect, dam, 0);
482 nreport(player->cnum, N_SHP_SHELL, vict, 1);
485 if ((target != targ_sub) ||
486 ((vship.shp_rflags & RET_DCHRGED) == 0))
487 check_retreat_and_do_shipdamage(&vship, dam);
489 shipdamage(&vship, dam);
492 "Country #%d shelled %s in %s for %d damage.\n",
493 player->cnum, prship(&vship),
494 xyas(vship.shp_x, vship.shp_y, vict), dam);
496 pr("Shells hit %s in %s for %d damage.\n",
498 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
500 if (vship.shp_effic < SHIP_MINEFF)
501 pr("%s sunk!\n", prsub(&vship));
505 /* Ok, now, check if we had a bogus target. If so,
506 just continue on, since there is no defender. */
507 if (target == targ_bogus)
510 if (type == EF_LAND) {
511 getsect(fland.lnd_x, fland.lnd_y, &fsect);
512 attgp = (struct empobj *)&fsect;
514 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
520 putship(vship.shp_uid, &vship);
523 if ((totaldefdam == 0) && (target == targ_ship))
524 if (vship.shp_rflags & RET_HELPLESS)
525 retreat_ship(&vship, 'h');
526 switch (attgp->ef_type) {
531 if ((target == targ_ship) || (target == targ_sub)) {
532 if (fship.shp_effic > SHIP_MINEFF) {
533 shp_missdef(&fship, vict);
536 putship(fship.shp_uid, &fship);
543 free_flist(&defended);
545 odds = ((double)ndefending) / ((double)nfiring);
548 do_defdam(&fired, odds);
553 defend(struct emp_qelem *al, struct emp_qelem *dl,
554 struct empobj *attgp, natid vict, int *nd)
559 dam = quiet_bigdef(attgp->ef_type, dl, vict,
560 attgp->own, attgp->x, attgp->y, &nfiring);
564 add_to_flist(al, attgp, dam, vict);
571 do_defdam(struct emp_qelem *list, double odds)
579 struct emp_qelem *qp, *next;
581 for (qp = list->q_forw; qp != list; qp = next) {
583 fp = (struct flist *)qp;
584 if (fp->type == EF_SHIP) {
585 if (!getship(fp->uid, &ship) || !ship.shp_own)
590 pr("\nDefenders fire back!\n");
593 dam = odds * fp->defdam;
595 if (fp->type == EF_SHIP) {
597 pr("Return fire hit %s in %s for %d damage.\n",
599 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
602 "Return fire hit %s in %s for %d damage.\n",
603 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
604 shipdamage(&ship, dam);
605 putship(ship.shp_uid, &ship);
607 CANT_HAPPEN(fp->type != EF_SECTOR);
608 getsect(fp->x, fp->y, §);
610 pr("Return fire hit sector %s for %d damage.\n",
611 xyas(fp->x, fp->y, player->cnum), dam);
612 sectdamage(§, dam, 0);
615 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
616 xyas(fp->x, fp->y, vict), dam);
618 emp_remque(&fp->queue);
624 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
625 coord ax, coord ay, int *nfiring)
631 int dam, dam2, rel, rel2;
632 struct sctstr firing;
639 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
640 while (nxtitem(&ni, &ship)) {
641 if (ship.shp_own == 0)
644 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
647 rel = getrel(getnatp(ship.shp_own), own);
648 rel2 = getrel(getnatp(ship.shp_own), aown);
649 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
651 /* Don't shoot yourself */
652 if (ship.shp_own == aown)
654 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
655 erange = torprange(&ship);
656 if (roundrange(erange) < ni.curdist)
658 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
660 fp = search_flist(list, (struct empobj *)&ship);
664 dam2 = shp_torp(&ship, 0);
665 putship(ship.shp_uid, &ship);
669 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
672 erange = shp_fire_range(&ship);
673 if (roundrange(erange) < ni.curdist)
675 fp = search_flist(list, (struct empobj *)&ship);
679 dam2 = shp_fire(&ship);
680 putship(ship.shp_uid, &ship);
684 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
688 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
691 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
692 while (nxtitem(&ni, &land)) {
693 if (land.lnd_own == 0)
695 /* Don't shoot yourself */
696 if (land.lnd_own == aown)
699 rel = getrel(getnatp(land.lnd_own), own);
700 rel2 = getrel(getnatp(land.lnd_own), aown);
702 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
705 erange = lnd_fire_range(&land);
706 if (roundrange(erange) < ni.curdist)
709 fp = search_flist(list, (struct empobj *)&land);
713 dam2 = lnd_fire(&land);
714 putland(land.lnd_uid, &land);
721 add_to_flist(list, (struct empobj *)&land, dam2, 0);
722 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
727 * Determine if any nearby gun-equipped sectors are within
728 * range and able to fire at an attacker. Firing sectors
729 * need to have guns, shells, and military. Sector being
730 * attacked is x,y -- attacker is at ax,ay.
733 if (!opt_NO_FORT_FIRE) {
734 snxtsct_dist(&ns, ax, ay, 8);
735 while (nxtsct(&ns, &firing)) {
736 if (firing.sct_own == 0)
738 rel = getrel(getnatp(firing.sct_own), own);
739 rel2 = getrel(getnatp(firing.sct_own), aown);
741 if ((firing.sct_own != own) &&
742 ((rel != ALLIED) || (rel2 != AT_WAR)))
744 /* Don't shoot yourself */
745 if (firing.sct_own == aown)
747 erange = fortrange(&firing);
748 if (roundrange(erange) < ns.curdist)
751 fp = search_flist(list, (struct empobj *)&firing);
755 dam2 = fort_fire(&firing);
762 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
763 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
768 return *nfiring == 0 ? 0 : dam / *nfiring;
772 add_to_flist(struct emp_qelem *list,
773 struct empobj *gp, int dam, natid victim)
777 fp = malloc(sizeof(struct flist));
778 fp->type = gp->ef_type;
784 emp_insque(&fp->queue, list);
788 free_flist(struct emp_qelem *list)
790 struct emp_qelem *qp, *next;
793 for (qp = list->q_forw; qp != list; qp = next) {
795 fp = (struct flist *)qp;
796 emp_remque(&fp->queue);
802 uid_eq(struct emp_qelem *elem, void *key)
804 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
807 static struct flist *
808 search_flist(struct emp_qelem *list, struct empobj *gp)
810 return (struct flist *)emp_searchque(list, gp, uid_eq);