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));
162 } else if (type == EF_SHIP) {
163 if (!getship(item.ship.shp_uid, &fship))
165 if (item.ship.shp_own != player->cnum)
167 if (item.ship.shp_item[I_MILIT] < 1) {
168 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
171 if (mchr[item.ship.shp_type].m_glim == 0) {
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 } else if (type == EF_SECTOR) {
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));
216 if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
219 if (player->aborted) {
220 pr("Fire aborted.\n");
223 if (!issector(ptr)) {
225 if (vshipno < 0 || !getship(vshipno, &vship) ||
227 pr("No such ship exists!\n");
230 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
231 targ_sub : targ_ship;
232 vict = vship.shp_own;
235 if (!getsect(x, y, &vsect)) {
236 pr("No such sector exists!\n");
240 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
241 pr("No such sector exists!\n");
244 /* We check the sector type, but we only use it for damage, not
245 reporting. That way, you don't get extra information you wouldn't
246 normally get. Besides, what if they want to slam water? :) */
247 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
251 vict = vsect.sct_own;
255 if (type == EF_SHIP) {
256 if (fship.shp_own != player->cnum) {
257 pr("Not your ship!\n");
260 if (target == targ_sub || target == targ_ship) {
261 if (fship.shp_uid == vship.shp_uid) {
262 pr("You can't fire upon yourself!\n");
268 if ((mil = fship.shp_item[I_MILIT]) < 1) {
269 pr("Not enough military for firing crew.\n");
272 if (fship.shp_effic < 60) {
273 pr("Ship #%d is crippled (%d%%)\n",
274 fship.shp_uid, fship.shp_effic);
277 range = shp_fire_range(&fship);
278 range2 = roundrange(range);
279 pr("range is %d.00 (%.2f)\n", range2, range);
280 if (target == targ_sub
281 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
282 dam = shp_dchrg(&fship);
283 putship(fship.shp_uid, &fship);
285 pr("Not enough shells for depth charge!\n");
289 if (target == targ_sub)
290 /* Don't tell it's a sub */
292 if (fship.shp_item[I_GUN] == 0) {
293 pr("Insufficient arms.\n");
296 dam = shp_fire(&fship);
297 putship(fship.shp_uid, &fship);
303 if (opt_NOMOBCOST == 0) {
304 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
305 putship(fship.shp_uid, &fship);
307 } else if (type == EF_LAND) {
308 if (fland.lnd_own != player->cnum) {
309 pr("Not your unit!\n");
313 if (target == targ_land) {
314 if (fland.lnd_x == vsect.sct_x
315 && fland.lnd_y == vsect.sct_y) {
316 pr("You can't fire upon yourself!\n");
324 if (lchr[fland.lnd_type].l_dam == 0) {
325 pr("Unit %d cannot fire!\n", fland.lnd_uid);
328 if (fland.lnd_item[I_GUN] == 0) {
329 pr("%s -- not enough guns\n", prland(&fland));
333 range = lnd_fire_range(&fland);
334 range2 = roundrange(range);
335 pr("range is %d.00 (%.2f)\n", range2, range);
336 if (target == targ_sub) {
337 /* Don't tell it's a sub */
341 dam = lnd_fire(&fland);
342 putland(fland.lnd_uid, &fland);
347 if (target == targ_ship) {
348 if (chance(lnd_acc(&fland) / 100.0))
349 dam = ldround(dam / 2.0, 1);
354 if (fsect.sct_own != player->cnum ||
355 fsect.sct_type != SCT_FORTR) {
356 pr("No fortress at %s\n",
357 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
360 if (target == targ_land) {
361 if (fsect.sct_x == vsect.sct_x
362 && fsect.sct_y == vsect.sct_y) {
363 pr("You can't fire upon yourself!\n");
367 if (fsect.sct_item[I_GUN] == 0) {
368 pr("Insufficient arms.\n");
371 if (fsect.sct_item[I_MILIT] < 5) {
372 pr("Not enough military for firing crew.\n");
375 dam = fort_fire(&fsect);
381 range = fortrange(&fsect);
382 range2 = roundrange(range);
383 pr("range is %d.00 (%.2f)\n", range2, range);
384 if (target == targ_sub) {
385 /* Don't tell it's a sub */
389 trange = mapdist(x, y, fx, fy);
390 if (trange > range2) {
391 pr("Target out of range.\n");
397 fland.lnd_mission = 0;
398 putland(fland.lnd_uid, &fland);
401 fship.shp_mission = 0;
402 putship(fship.shp_uid, &fship);
411 if (!trechk(player->cnum, vict, SEAFIR))
415 if (!trechk(player->cnum, vict, SUBFIR))
419 if (!trechk(player->cnum, vict, LANFIR))
427 if (target == targ_land) {
428 natp = getnatp(player->cnum);
429 rel = getrel(natp, vict);
430 if ((rel != AT_WAR) && (player->cnum != vict) &&
431 (vict) && (vsect.sct_oldown != player->cnum)) {
432 pr("You're not at war with them!\n");
442 if (vship.shp_rflags & RET_DCHRGED)
443 retreat_ship(&vship, 'd');
448 prb = range2 ? (double)trange / range2 : 1.0;
451 pr("Wind deflects shells.\n");
452 /* dam = (int)(dam / 2.0);*/
453 dam *= (90 - (random() % 11)) / 100.0;
462 nreport(player->cnum, N_SCT_SHELL, vict, 1);
463 if (vict && vict != player->cnum)
465 "Country #%d shelled sector %s for %d damage.\n",
466 player->cnum, xyas(x, y, vict), dam);
467 pr("Shells hit sector %s for %d damage.\n",
468 xyas(x, y, player->cnum), dam);
469 if (target != targ_bogus)
470 sectdamage(&vsect, dam);
473 nreport(player->cnum, N_SHP_SHELL, vict, 1);
476 if ((target != targ_sub) ||
477 ((vship.shp_rflags & RET_DCHRGED) == 0))
478 check_retreat_and_do_shipdamage(&vship, dam);
480 shipdamage(&vship, dam);
483 "Country #%d shelled %s in %s for %d damage.\n",
484 player->cnum, prship(&vship),
485 xyas(vship.shp_x, vship.shp_y, vict), dam);
487 pr("Shells hit %s in %s for %d damage.\n",
489 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
491 if (vship.shp_effic < SHIP_MINEFF)
492 pr("%s sunk!\n", prsub(&vship));
496 /* Ok, now, check if we had a bogus target. If so,
497 just continue on, since there is no defender. */
498 if (target == targ_bogus)
501 if (type == EF_LAND) {
502 getsect(fland.lnd_x, fland.lnd_y, &fsect);
503 attgp = (struct empobj *)&fsect;
505 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
511 putship(vship.shp_uid, &vship);
514 if ((totaldefdam == 0) && (target == targ_ship))
515 if (vship.shp_rflags & RET_HELPLESS)
516 retreat_ship(&vship, 'h');
517 switch (attgp->ef_type) {
522 if ((target == targ_ship) || (target == targ_sub)) {
523 if (fship.shp_effic > SHIP_MINEFF) {
524 shp_missdef(&fship, vict);
527 putship(fship.shp_uid, &fship);
534 free_flist(&defended);
536 odds = ((double)ndefending) / ((double)nfiring);
539 do_defdam(&fired, odds);
544 defend(struct emp_qelem *al, struct emp_qelem *dl,
545 struct empobj *attgp, natid vict, int *nd)
550 dam = quiet_bigdef(attgp->ef_type, dl, vict,
551 attgp->own, attgp->x, attgp->y, &nfiring);
555 add_to_flist(al, attgp, dam, vict);
562 do_defdam(struct emp_qelem *list, double odds)
570 struct emp_qelem *qp, *next;
572 for (qp = list->q_forw; qp != list; qp = next) {
574 fp = (struct flist *)qp;
575 if (fp->type == EF_SHIP) {
576 if (!getship(fp->uid, &ship) || !ship.shp_own)
581 pr("\nDefenders fire back!\n");
584 dam = odds * fp->defdam;
586 if (fp->type == EF_SHIP) {
588 pr("Return fire hit %s in %s for %d damage.\n",
590 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
593 "Return fire hit %s in %s for %d damage.\n",
594 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
595 shipdamage(&ship, dam);
596 putship(ship.shp_uid, &ship);
598 CANT_HAPPEN(fp->type != EF_SECTOR);
599 getsect(fp->x, fp->y, §);
601 pr("Return fire hit sector %s for %d damage.\n",
602 xyas(fp->x, fp->y, player->cnum), dam);
603 sectdamage(§, dam);
606 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
607 xyas(fp->x, fp->y, vict), dam);
609 emp_remque(&fp->queue);
615 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
616 coord ax, coord ay, int *nfiring)
622 int dam, dam2, rel, rel2;
623 struct sctstr firing;
630 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
631 while (nxtitem(&ni, &ship)) {
632 if (ship.shp_own == 0)
635 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
638 rel = getrel(getnatp(ship.shp_own), own);
639 rel2 = getrel(getnatp(ship.shp_own), aown);
640 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
642 /* Don't shoot yourself */
643 if (ship.shp_own == aown)
645 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
646 erange = torprange(&ship);
647 if (roundrange(erange) < ni.curdist)
649 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
651 fp = search_flist(list, (struct empobj *)&ship);
655 dam2 = shp_torp(&ship, 0);
656 putship(ship.shp_uid, &ship);
660 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
663 erange = shp_fire_range(&ship);
664 if (roundrange(erange) < ni.curdist)
666 fp = search_flist(list, (struct empobj *)&ship);
670 dam2 = shp_fire(&ship);
671 putship(ship.shp_uid, &ship);
675 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
679 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
682 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
683 while (nxtitem(&ni, &land)) {
684 if (land.lnd_own == 0)
686 /* Don't shoot yourself */
687 if (land.lnd_own == aown)
690 rel = getrel(getnatp(land.lnd_own), own);
691 rel2 = getrel(getnatp(land.lnd_own), aown);
693 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
696 erange = lnd_fire_range(&land);
697 if (roundrange(erange) < ni.curdist)
700 fp = search_flist(list, (struct empobj *)&land);
704 dam2 = lnd_fire(&land);
705 putland(land.lnd_uid, &land);
712 add_to_flist(list, (struct empobj *)&land, dam2, 0);
713 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
718 * Determine if any nearby gun-equipped sectors are within
719 * range and able to fire at an attacker. Firing sectors
720 * need to have guns, shells, and military. Sector being
721 * attacked is x,y -- attacker is at ax,ay.
724 if (!opt_NO_FORT_FIRE) {
725 snxtsct_dist(&ns, ax, ay, 8);
726 while (nxtsct(&ns, &firing)) {
727 if (firing.sct_own == 0)
729 rel = getrel(getnatp(firing.sct_own), own);
730 rel2 = getrel(getnatp(firing.sct_own), aown);
732 if ((firing.sct_own != own) &&
733 ((rel != ALLIED) || (rel2 != AT_WAR)))
735 /* Don't shoot yourself */
736 if (firing.sct_own == aown)
738 erange = fortrange(&firing);
739 if (roundrange(erange) < ns.curdist)
742 fp = search_flist(list, (struct empobj *)&firing);
746 dam2 = fort_fire(&firing);
753 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
754 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
759 return *nfiring == 0 ? 0 : dam / *nfiring;
763 add_to_flist(struct emp_qelem *list,
764 struct empobj *gp, int dam, natid victim)
768 fp = malloc(sizeof(struct flist));
769 fp->type = gp->ef_type;
775 emp_insque(&fp->queue, list);
779 free_flist(struct emp_qelem *list)
781 struct emp_qelem *qp, *next;
784 for (qp = list->q_forw; qp != list; qp = next) {
786 fp = (struct flist *)qp;
787 emp_remque(&fp->queue);
793 uid_eq(struct emp_qelem *elem, void *key)
795 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
798 static struct flist *
799 search_flist(struct emp_qelem *list, struct empobj *gp)
801 return (struct flist *)emp_searchque(list, gp, uid_eq);