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_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 };
85 enum targ_type target;
88 struct nstr_item nbst;
94 union empobj_storage item;
95 struct emp_qelem fired, defended;
100 emp_initque(&defended);
101 if (!(p = getstarg(player->argp[1],
102 "Firing from ship(s), sect(s), or land unit(s)? ",
105 type = ef_byname_from(p, ef_with_guns);
106 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
107 pr("Fort firing is disabled.\n");
111 pr("Ships, land units or sectors only!\n");
114 if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
118 if (!snxtitem(&nbst, type, ptr))
121 if (player->aborted) {
122 pr("Fire aborted.\n");
125 while (nxtitem(&nbst, &item)) {
126 if (type == EF_LAND) {
127 if (!getland(item.land.lnd_uid, &fland))
129 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
131 if (item.land.lnd_own != player->cnum)
134 if (lchr[fland.lnd_type].l_dam == 0) {
135 pr("Unit %d cannot fire!\n", fland.lnd_uid);
138 if (fland.lnd_item[I_MILIT] < 1) {
139 pr("Unit %d cannot fire because it has no military!\n",
143 if (fland.lnd_ship >= 0) {
144 pr("Unit %d cannot fire because it is on a ship!\n",
148 if (fland.lnd_land >= 0) {
149 pr("Unit %d cannot fire because it is on a land unit!\n",
153 if (fland.lnd_effic < LAND_MINFIREEFF) {
154 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
155 fland.lnd_uid, LAND_MINFIREEFF);
158 if (fland.lnd_item[I_SHELL] == 0) {
159 pr("%s -- not enough shells\n", 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);
191 } else if (type == EF_SECTOR) {
192 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
194 if (item.sect.sct_own != player->cnum)
196 if (item.sect.sct_type != SCT_FORTR)
198 if (item.sect.sct_effic < FORTEFF) {
199 pr("Fort not efficient enough to fire!\n");
202 if (item.sect.sct_item[I_GUN] == 0) {
203 pr("Not enough guns in sector %s!\n",
204 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
207 if (item.sect.sct_item[I_SHELL] == 0) {
208 pr("Not enough shells in sector %s!\n",
209 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
212 if (item.sect.sct_item[I_MILIT] < 5) {
213 pr("Not enough military in sector %s!\n",
214 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
217 pr("\nSector %s firing\n",
218 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
223 if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
226 if (player->aborted) {
227 pr("Fire aborted.\n");
230 if (!issector(ptr)) {
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(ptr, &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;
263 trange = mapdist(x, y, fx, fy);
265 if (type == EF_SHIP) {
266 if (!check_ship_ok(&fship))
268 if (fship.shp_own != player->cnum) {
269 pr("Not your ship!\n");
272 if (target == targ_sub || target == targ_ship) {
273 if (fship.shp_uid == vship.shp_uid) {
274 pr("You can't fire upon yourself!\n");
278 if ((mil = fship.shp_item[I_MILIT]) < 1) {
279 pr("Not enough military for firing crew.\n");
282 if (fship.shp_effic < 60) {
283 pr("Ship #%d is crippled (%d%%)\n",
284 fship.shp_uid, fship.shp_effic);
287 range = shp_fire_range(&fship);
288 range2 = roundrange(range);
289 pr("range is %d.00 (%.2f)\n", range2, range);
290 if (target == targ_sub
292 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
293 dam = shp_dchrg(&fship);
295 if (target == targ_sub)
296 /* Don't tell it's a sub */
298 if (fship.shp_item[I_GUN] == 0) {
299 pr("Insufficient arms.\n");
302 dam = shp_fire(&fship);
304 putship(fship.shp_uid, &fship);
309 if (opt_NOMOBCOST == 0) {
310 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
311 putship(fship.shp_uid, &fship);
313 } else if (type == EF_LAND) {
314 if (!check_land_ok(&fland))
316 if (fland.lnd_own != player->cnum) {
317 pr("Not your unit!\n");
321 if (target == targ_land) {
322 if (fland.lnd_x == vsect.sct_x
323 && fland.lnd_y == vsect.sct_y) {
324 pr("You can't fire upon yourself!\n");
329 if (lchr[fland.lnd_type].l_dam == 0) {
330 pr("Unit %d cannot fire!\n", fland.lnd_uid);
333 if (fland.lnd_item[I_GUN] == 0) {
334 pr("%s -- not enough guns\n", prland(&fland));
338 range = lnd_fire_range(&fland);
339 range2 = roundrange(range);
340 pr("range is %d.00 (%.2f)\n", range2, range);
341 if (target == targ_sub) {
342 /* Don't tell it's a sub */
346 dam = lnd_fire(&fland);
347 putland(fland.lnd_uid, &fland);
352 if (target == targ_ship) {
353 if (chance(lnd_acc(&fland) / 100.0))
354 dam = ldround(dam / 2.0, 1);
357 if (!check_sect_ok(&fsect))
359 if (fsect.sct_own != player->cnum ||
360 fsect.sct_type != SCT_FORTR) {
361 pr("No fortress at %s\n",
362 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
365 if (target == targ_land) {
366 if (fsect.sct_x == vsect.sct_x
367 && fsect.sct_y == vsect.sct_y) {
368 pr("You can't fire upon yourself!\n");
372 if (fsect.sct_item[I_GUN] == 0) {
373 pr("Insufficient arms.\n");
376 if (fsect.sct_item[I_MILIT] < 5) {
377 pr("Not enough military for firing crew.\n");
380 dam = fort_fire(&fsect);
386 range = fortrange(&fsect);
387 range2 = roundrange(range);
388 pr("range is %d.00 (%.2f)\n", range2, range);
389 if (target == targ_sub) {
390 /* Don't tell it's a sub */
394 if (trange > range2) {
395 pr("Target out of range.\n");
401 fland.lnd_mission = 0;
402 putland(fland.lnd_uid, &fland);
405 fship.shp_mission = 0;
406 putship(fship.shp_uid, &fship);
415 if (!trechk(player->cnum, vict, SEAFIR))
419 if (!trechk(player->cnum, vict, SUBFIR))
423 if (!trechk(player->cnum, vict, LANFIR))
431 if (target == targ_land) {
432 natp = getnatp(player->cnum);
433 rel = getrel(natp, vict);
434 if ((rel != AT_WAR) && (player->cnum != vict) &&
435 (vict) && (vsect.sct_oldown != player->cnum)) {
436 pr("You're not at war with them!\n");
446 if (vship.shp_rflags & RET_DCHRGED)
447 retreat_ship(&vship, 'd');
452 prb = range2 ? (double)trange / range2 : 1.0;
455 pr("Wind deflects shells.\n");
456 /* dam = (int)(dam / 2.0);*/
457 dam *= (90 - (random() % 11)) / 100.0;
466 nreport(player->cnum, N_SCT_SHELL, vict, 1);
467 if (vict && vict != player->cnum)
469 "Country #%d shelled sector %s for %d damage.\n",
470 player->cnum, xyas(x, y, vict), dam);
471 pr("Shells hit sector %s for %d damage.\n",
472 xyas(x, y, player->cnum), dam);
473 if (target != targ_bogus)
474 sectdamage(&vsect, dam);
477 nreport(player->cnum, N_SHP_SHELL, vict, 1);
480 if ((target != targ_sub) ||
481 ((vship.shp_rflags & RET_DCHRGED) == 0))
482 check_retreat_and_do_shipdamage(&vship, dam);
484 shipdamage(&vship, dam);
487 "Country #%d shelled %s in %s for %d damage.\n",
488 player->cnum, prship(&vship),
489 xyas(vship.shp_x, vship.shp_y, vict), dam);
491 pr("Shells hit %s in %s for %d damage.\n",
493 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
495 if (vship.shp_effic < SHIP_MINEFF)
496 pr("%s sunk!\n", prsub(&vship));
500 /* Ok, now, check if we had a bogus target. If so,
501 just continue on, since there is no defender. */
502 if (target == targ_bogus)
505 if (type == EF_LAND) {
506 getsect(fland.lnd_x, fland.lnd_y, &fsect);
507 attgp = (struct empobj *)&fsect;
509 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
515 putship(vship.shp_uid, &vship);
518 if ((totaldefdam == 0) && (target == targ_ship))
519 if (vship.shp_rflags & RET_HELPLESS)
520 retreat_ship(&vship, 'h');
521 switch (attgp->ef_type) {
526 if ((target == targ_ship) || (target == targ_sub)) {
527 if (fship.shp_effic > SHIP_MINEFF) {
528 shp_missdef(&fship, vict);
531 putship(fship.shp_uid, &fship);
538 free_flist(&defended);
540 odds = ((double)ndefending) / ((double)nfiring);
543 do_defdam(&fired, odds);
548 defend(struct emp_qelem *al, struct emp_qelem *dl,
549 struct empobj *attgp, natid vict, int *nd)
554 dam = quiet_bigdef(attgp->ef_type, dl, vict,
555 attgp->own, attgp->x, attgp->y, &nfiring);
559 add_to_flist(al, attgp, dam, vict);
566 do_defdam(struct emp_qelem *list, double odds)
574 struct emp_qelem *qp, *next;
576 for (qp = list->q_forw; qp != list; qp = next) {
578 fp = (struct flist *)qp;
579 if (fp->type == EF_SHIP) {
580 if (!getship(fp->uid, &ship) || !ship.shp_own)
585 pr("\nDefenders fire back!\n");
588 dam = odds * fp->defdam;
590 if (fp->type == EF_SHIP) {
592 pr("Return fire hit %s in %s for %d damage.\n",
594 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
597 "Return fire hit %s in %s for %d damage.\n",
598 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
599 shipdamage(&ship, dam);
600 putship(ship.shp_uid, &ship);
602 CANT_HAPPEN(fp->type != EF_SECTOR);
603 getsect(fp->x, fp->y, §);
605 pr("Return fire hit sector %s for %d damage.\n",
606 xyas(fp->x, fp->y, player->cnum), dam);
607 sectdamage(§, dam);
610 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
611 xyas(fp->x, fp->y, vict), dam);
613 emp_remque(&fp->queue);
619 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
620 coord ax, coord ay, int *nfiring)
626 int dam, dam2, rel, rel2;
627 struct sctstr firing;
634 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
635 while (nxtitem(&ni, &ship)) {
636 if (ship.shp_own == 0)
639 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
642 rel = getrel(getnatp(ship.shp_own), own);
643 rel2 = getrel(getnatp(ship.shp_own), aown);
644 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
646 /* Don't shoot yourself */
647 if (ship.shp_own == aown)
649 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
650 erange = torprange(&ship);
651 if (roundrange(erange) < ni.curdist)
653 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
655 fp = search_flist(list, (struct empobj *)&ship);
659 dam2 = shp_torp(&ship, 0);
660 putship(ship.shp_uid, &ship);
664 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
667 erange = shp_fire_range(&ship);
668 if (roundrange(erange) < ni.curdist)
670 fp = search_flist(list, (struct empobj *)&ship);
674 dam2 = shp_fire(&ship);
675 putship(ship.shp_uid, &ship);
679 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
683 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
686 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
687 while (nxtitem(&ni, &land)) {
688 if (land.lnd_own == 0)
690 /* Don't shoot yourself */
691 if (land.lnd_own == aown)
694 rel = getrel(getnatp(land.lnd_own), own);
695 rel2 = getrel(getnatp(land.lnd_own), aown);
697 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
700 erange = lnd_fire_range(&land);
701 if (roundrange(erange) < ni.curdist)
704 fp = search_flist(list, (struct empobj *)&land);
708 dam2 = lnd_fire(&land);
709 putland(land.lnd_uid, &land);
716 add_to_flist(list, (struct empobj *)&land, dam2, 0);
717 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
722 * Determine if any nearby gun-equipped sectors are within
723 * range and able to fire at an attacker. Firing sectors
724 * need to have guns, shells, and military. Sector being
725 * attacked is x,y -- attacker is at ax,ay.
728 if (!opt_NO_FORT_FIRE) {
729 snxtsct_dist(&ns, ax, ay, 8);
730 while (nxtsct(&ns, &firing)) {
731 if (firing.sct_own == 0)
733 rel = getrel(getnatp(firing.sct_own), own);
734 rel2 = getrel(getnatp(firing.sct_own), aown);
736 if ((firing.sct_own != own) &&
737 ((rel != ALLIED) || (rel2 != AT_WAR)))
739 /* Don't shoot yourself */
740 if (firing.sct_own == aown)
742 erange = fortrange(&firing);
743 if (roundrange(erange) < ns.curdist)
746 fp = search_flist(list, (struct empobj *)&firing);
750 dam2 = fort_fire(&firing);
757 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
758 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
763 return *nfiring == 0 ? 0 : dam / *nfiring;
767 add_to_flist(struct emp_qelem *list,
768 struct empobj *gp, int dam, natid victim)
772 fp = malloc(sizeof(struct flist));
773 fp->type = gp->ef_type;
779 emp_insque(&fp->queue, list);
783 free_flist(struct emp_qelem *list)
785 struct emp_qelem *qp, *next;
788 for (qp = list->q_forw; qp != list; qp = next) {
790 fp = (struct flist *)qp;
791 emp_remque(&fp->queue);
797 uid_eq(struct emp_qelem *elem, void *key)
799 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
802 static struct flist *
803 search_flist(struct emp_qelem *list, struct empobj *gp)
805 return (struct flist *)emp_searchque(list, gp, uid_eq);