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 (!check_ship_ok(&fship))
258 if (fship.shp_own != player->cnum) {
259 pr("Not your ship!\n");
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");
270 if ((mil = fship.shp_item[I_MILIT]) < 1) {
271 pr("Not enough military for firing crew.\n");
274 if (fship.shp_effic < 60) {
275 pr("Ship #%d is crippled (%d%%)\n",
276 fship.shp_uid, fship.shp_effic);
279 range = shp_fire_range(&fship);
280 range2 = roundrange(range);
281 pr("range is %d.00 (%.2f)\n", range2, range);
282 if (target == targ_sub
283 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
284 dam = shp_dchrg(&fship);
285 putship(fship.shp_uid, &fship);
287 pr("Not enough shells for depth charge!\n");
291 if (target == targ_sub)
292 /* Don't tell it's a sub */
294 if (fship.shp_item[I_GUN] == 0) {
295 pr("Insufficient arms.\n");
298 dam = shp_fire(&fship);
299 putship(fship.shp_uid, &fship);
305 if (opt_NOMOBCOST == 0) {
306 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
307 putship(fship.shp_uid, &fship);
309 } else if (type == EF_LAND) {
310 if (!check_land_ok(&fland))
312 if (fland.lnd_own != player->cnum) {
313 pr("Not your unit!\n");
317 if (target == targ_land) {
318 if (fland.lnd_x == vsect.sct_x
319 && fland.lnd_y == vsect.sct_y) {
320 pr("You can't fire upon yourself!\n");
328 if (lchr[fland.lnd_type].l_dam == 0) {
329 pr("Unit %d cannot fire!\n", fland.lnd_uid);
332 if (fland.lnd_item[I_GUN] == 0) {
333 pr("%s -- not enough guns\n", prland(&fland));
337 range = lnd_fire_range(&fland);
338 range2 = roundrange(range);
339 pr("range is %d.00 (%.2f)\n", range2, range);
340 if (target == targ_sub) {
341 /* Don't tell it's a sub */
345 dam = lnd_fire(&fland);
346 putland(fland.lnd_uid, &fland);
351 if (target == targ_ship) {
352 if (chance(lnd_acc(&fland) / 100.0))
353 dam = ldround(dam / 2.0, 1);
356 if (!check_sect_ok(&fsect))
360 if (fsect.sct_own != player->cnum ||
361 fsect.sct_type != SCT_FORTR) {
362 pr("No fortress at %s\n",
363 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
366 if (target == targ_land) {
367 if (fsect.sct_x == vsect.sct_x
368 && fsect.sct_y == vsect.sct_y) {
369 pr("You can't fire upon yourself!\n");
373 if (fsect.sct_item[I_GUN] == 0) {
374 pr("Insufficient arms.\n");
377 if (fsect.sct_item[I_MILIT] < 5) {
378 pr("Not enough military for firing crew.\n");
381 dam = fort_fire(&fsect);
387 range = fortrange(&fsect);
388 range2 = roundrange(range);
389 pr("range is %d.00 (%.2f)\n", range2, range);
390 if (target == targ_sub) {
391 /* Don't tell it's a sub */
395 trange = mapdist(x, y, fx, fy);
396 if (trange > range2) {
397 pr("Target out of range.\n");
403 fland.lnd_mission = 0;
404 putland(fland.lnd_uid, &fland);
407 fship.shp_mission = 0;
408 putship(fship.shp_uid, &fship);
417 if (!trechk(player->cnum, vict, SEAFIR))
421 if (!trechk(player->cnum, vict, SUBFIR))
425 if (!trechk(player->cnum, vict, LANFIR))
433 if (target == targ_land) {
434 natp = getnatp(player->cnum);
435 rel = getrel(natp, vict);
436 if ((rel != AT_WAR) && (player->cnum != vict) &&
437 (vict) && (vsect.sct_oldown != player->cnum)) {
438 pr("You're not at war with them!\n");
448 if (vship.shp_rflags & RET_DCHRGED)
449 retreat_ship(&vship, 'd');
454 prb = range2 ? (double)trange / range2 : 1.0;
457 pr("Wind deflects shells.\n");
458 /* dam = (int)(dam / 2.0);*/
459 dam *= (90 - (random() % 11)) / 100.0;
468 nreport(player->cnum, N_SCT_SHELL, vict, 1);
469 if (vict && vict != player->cnum)
471 "Country #%d shelled sector %s for %d damage.\n",
472 player->cnum, xyas(x, y, vict), dam);
473 pr("Shells hit sector %s for %d damage.\n",
474 xyas(x, y, player->cnum), dam);
475 if (target != targ_bogus)
476 sectdamage(&vsect, dam);
479 nreport(player->cnum, N_SHP_SHELL, vict, 1);
482 if ((target != targ_sub) ||
483 ((vship.shp_rflags & RET_DCHRGED) == 0))
484 check_retreat_and_do_shipdamage(&vship, dam);
486 shipdamage(&vship, dam);
489 "Country #%d shelled %s in %s for %d damage.\n",
490 player->cnum, prship(&vship),
491 xyas(vship.shp_x, vship.shp_y, vict), dam);
493 pr("Shells hit %s in %s for %d damage.\n",
495 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
497 if (vship.shp_effic < SHIP_MINEFF)
498 pr("%s sunk!\n", prsub(&vship));
502 /* Ok, now, check if we had a bogus target. If so,
503 just continue on, since there is no defender. */
504 if (target == targ_bogus)
507 if (type == EF_LAND) {
508 getsect(fland.lnd_x, fland.lnd_y, &fsect);
509 attgp = (struct empobj *)&fsect;
511 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
517 putship(vship.shp_uid, &vship);
520 if ((totaldefdam == 0) && (target == targ_ship))
521 if (vship.shp_rflags & RET_HELPLESS)
522 retreat_ship(&vship, 'h');
523 switch (attgp->ef_type) {
528 if ((target == targ_ship) || (target == targ_sub)) {
529 if (fship.shp_effic > SHIP_MINEFF) {
530 shp_missdef(&fship, vict);
533 putship(fship.shp_uid, &fship);
540 free_flist(&defended);
542 odds = ((double)ndefending) / ((double)nfiring);
545 do_defdam(&fired, odds);
550 defend(struct emp_qelem *al, struct emp_qelem *dl,
551 struct empobj *attgp, natid vict, int *nd)
556 dam = quiet_bigdef(attgp->ef_type, dl, vict,
557 attgp->own, attgp->x, attgp->y, &nfiring);
561 add_to_flist(al, attgp, dam, vict);
568 do_defdam(struct emp_qelem *list, double odds)
576 struct emp_qelem *qp, *next;
578 for (qp = list->q_forw; qp != list; qp = next) {
580 fp = (struct flist *)qp;
581 if (fp->type == EF_SHIP) {
582 if (!getship(fp->uid, &ship) || !ship.shp_own)
587 pr("\nDefenders fire back!\n");
590 dam = odds * fp->defdam;
592 if (fp->type == EF_SHIP) {
594 pr("Return fire hit %s in %s for %d damage.\n",
596 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
599 "Return fire hit %s in %s for %d damage.\n",
600 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
601 shipdamage(&ship, dam);
602 putship(ship.shp_uid, &ship);
604 CANT_HAPPEN(fp->type != EF_SECTOR);
605 getsect(fp->x, fp->y, §);
607 pr("Return fire hit sector %s for %d damage.\n",
608 xyas(fp->x, fp->y, player->cnum), dam);
609 sectdamage(§, dam);
612 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
613 xyas(fp->x, fp->y, vict), dam);
615 emp_remque(&fp->queue);
621 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
622 coord ax, coord ay, int *nfiring)
628 int dam, dam2, rel, rel2;
629 struct sctstr firing;
636 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
637 while (nxtitem(&ni, &ship)) {
638 if (ship.shp_own == 0)
641 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
644 rel = getrel(getnatp(ship.shp_own), own);
645 rel2 = getrel(getnatp(ship.shp_own), aown);
646 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
648 /* Don't shoot yourself */
649 if (ship.shp_own == aown)
651 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
652 erange = torprange(&ship);
653 if (roundrange(erange) < ni.curdist)
655 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
657 fp = search_flist(list, (struct empobj *)&ship);
661 dam2 = shp_torp(&ship, 0);
662 putship(ship.shp_uid, &ship);
666 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
669 erange = shp_fire_range(&ship);
670 if (roundrange(erange) < ni.curdist)
672 fp = search_flist(list, (struct empobj *)&ship);
676 dam2 = shp_fire(&ship);
677 putship(ship.shp_uid, &ship);
681 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
685 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
688 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
689 while (nxtitem(&ni, &land)) {
690 if (land.lnd_own == 0)
692 /* Don't shoot yourself */
693 if (land.lnd_own == aown)
696 rel = getrel(getnatp(land.lnd_own), own);
697 rel2 = getrel(getnatp(land.lnd_own), aown);
699 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
702 erange = lnd_fire_range(&land);
703 if (roundrange(erange) < ni.curdist)
706 fp = search_flist(list, (struct empobj *)&land);
710 dam2 = lnd_fire(&land);
711 putland(land.lnd_uid, &land);
718 add_to_flist(list, (struct empobj *)&land, dam2, 0);
719 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
724 * Determine if any nearby gun-equipped sectors are within
725 * range and able to fire at an attacker. Firing sectors
726 * need to have guns, shells, and military. Sector being
727 * attacked is x,y -- attacker is at ax,ay.
730 if (!opt_NO_FORT_FIRE) {
731 snxtsct_dist(&ns, ax, ay, 8);
732 while (nxtsct(&ns, &firing)) {
733 if (firing.sct_own == 0)
735 rel = getrel(getnatp(firing.sct_own), own);
736 rel2 = getrel(getnatp(firing.sct_own), aown);
738 if ((firing.sct_own != own) &&
739 ((rel != ALLIED) || (rel2 != AT_WAR)))
741 /* Don't shoot yourself */
742 if (firing.sct_own == aown)
744 erange = fortrange(&firing);
745 if (roundrange(erange) < ns.curdist)
748 fp = search_flist(list, (struct empobj *)&firing);
752 dam2 = fort_fire(&firing);
759 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
760 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
765 return *nfiring == 0 ? 0 : dam / *nfiring;
769 add_to_flist(struct emp_qelem *list,
770 struct empobj *gp, int dam, natid victim)
774 fp = malloc(sizeof(struct flist));
775 fp->type = gp->ef_type;
781 emp_insque(&fp->queue, list);
785 free_flist(struct emp_qelem *list)
787 struct emp_qelem *qp, *next;
790 for (qp = list->q_forw; qp != list; qp = next) {
792 fp = (struct flist *)qp;
793 emp_remque(&fp->queue);
799 uid_eq(struct emp_qelem *elem, void *key)
801 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
804 static struct flist *
805 search_flist(struct emp_qelem *list, struct empobj *gp)
807 return (struct flist *)emp_searchque(list, gp, uid_eq);