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);
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 (lchr[(int)fland.lnd_type].l_flags & L_HEAVY
334 && target != targ_land) {
335 pr("%s is too ponderous to target ships!\n",
339 if (fland.lnd_item[I_GUN] == 0) {
340 pr("%s -- not enough guns\n", prland(&fland));
344 range = lnd_fire_range(&fland);
345 range2 = roundrange(range);
346 pr("range is %d.00 (%.2f)\n", range2, range);
347 if (target == targ_sub) {
348 /* Don't tell it's a sub */
352 dam = lnd_fire(&fland);
353 putland(fland.lnd_uid, &fland);
359 lnd_unlimber(&fland);
360 if (target == targ_ship) {
361 if (chance(lnd_acc(&fland) / 100.0))
362 dam = ldround(dam / 2.0, 1);
365 if (!check_sect_ok(&fsect))
367 if (fsect.sct_own != player->cnum ||
368 fsect.sct_type != SCT_FORTR) {
369 pr("No fortress at %s\n",
370 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
373 if (target == targ_land) {
374 if (fsect.sct_x == vsect.sct_x
375 && fsect.sct_y == vsect.sct_y) {
376 pr("You can't fire upon yourself!\n");
380 if (fsect.sct_item[I_GUN] == 0) {
381 pr("Insufficient arms.\n");
384 if (fsect.sct_item[I_MILIT] < 5) {
385 pr("Not enough military for firing crew.\n");
388 dam = fort_fire(&fsect);
394 range = fortrange(&fsect);
395 range2 = roundrange(range);
396 pr("range is %d.00 (%.2f)\n", range2, range);
397 if (target == targ_sub) {
398 /* Don't tell it's a sub */
402 if (trange > range2) {
403 pr("Target out of range.\n");
409 fland.lnd_mission = 0;
410 putland(fland.lnd_uid, &fland);
413 fship.shp_mission = 0;
414 putship(fship.shp_uid, &fship);
423 if (!trechk(player->cnum, vict, SEAFIR))
427 if (!trechk(player->cnum, vict, SUBFIR))
431 if (!trechk(player->cnum, vict, LANFIR))
439 if (target == targ_land) {
440 natp = getnatp(player->cnum);
441 rel = getrel(natp, vict);
442 if ((rel != AT_WAR) && (player->cnum != vict) &&
443 (vict) && (vsect.sct_oldown != player->cnum)) {
444 pr("You're not at war with them!\n");
458 prb = range2 ? (double)trange / range2 : 1.0;
461 pr("Wind deflects shells.\n");
462 /* dam = (int)(dam / 2.0);*/
463 dam *= (90 - (random() % 11)) / 100.0;
472 nreport(player->cnum, N_SCT_SHELL, vict, 1);
473 if (vict && vict != player->cnum)
475 "Country #%d shelled sector %s for %d damage.\n",
476 player->cnum, xyas(x, y, vict), dam);
477 pr("Shells hit sector %s for %d damage.\n",
478 xyas(x, y, player->cnum), dam);
481 nreport(player->cnum, N_SHP_SHELL, vict, 1);
486 "Country #%d shelled %s in %s for %d damage.\n",
487 player->cnum, prship(&vship),
488 xyas(vship.shp_x, vship.shp_y, vict), dam);
490 pr("Shells hit %s in %s for %d damage.\n",
492 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
495 /* Ok, now, check if we had a bogus target. If so,
496 just continue on, since there is no defender. */
497 if (target == targ_bogus)
500 if (type == EF_LAND) {
501 getsect(fland.lnd_x, fland.lnd_y, &fsect);
502 attgp = (struct empobj *)&fsect;
504 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
507 getsect(x, y, &vsect);
508 sectdamage(&vsect, dam);
512 getship(vshipno, &vship);
513 check_retreat_and_do_shipdamage(&vship, dam);
514 if (vship.shp_effic < SHIP_MINEFF)
515 pr("%s sunk!\n", prsub(&vship));
516 else if (target == targ_sub
517 && (vship.shp_rflags & RET_DCHRGED)
518 && !(vship.shp_rflags & RET_INJURED))
519 retreat_ship(&vship, 'd');
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);
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)
698 /* Too ponderous for counter-battery fire */
699 if (lchr[(int)land.lnd_type].l_flags & L_HEAVY)
702 rel = getrel(getnatp(land.lnd_own), own);
703 rel2 = getrel(getnatp(land.lnd_own), aown);
705 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
708 erange = lnd_fire_range(&land);
709 if (roundrange(erange) < ni.curdist)
712 fp = search_flist(list, (struct empobj *)&land);
716 dam2 = lnd_fire(&land);
717 putland(land.lnd_uid, &land);
725 add_to_flist(list, (struct empobj *)&land, dam2, 0);
726 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
731 * Determine if any nearby gun-equipped sectors are within
732 * range and able to fire at an attacker. Firing sectors
733 * need to have guns, shells, and military. Sector being
734 * attacked is x,y -- attacker is at ax,ay.
737 if (!opt_NO_FORT_FIRE) {
738 snxtsct_dist(&ns, ax, ay, 8);
739 while (nxtsct(&ns, &firing)) {
740 if (firing.sct_own == 0)
742 rel = getrel(getnatp(firing.sct_own), own);
743 rel2 = getrel(getnatp(firing.sct_own), aown);
745 if ((firing.sct_own != own) &&
746 ((rel != ALLIED) || (rel2 != AT_WAR)))
748 /* Don't shoot yourself */
749 if (firing.sct_own == aown)
751 erange = fortrange(&firing);
752 if (roundrange(erange) < ns.curdist)
755 fp = search_flist(list, (struct empobj *)&firing);
759 dam2 = fort_fire(&firing);
766 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
767 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
772 return *nfiring == 0 ? 0 : dam / *nfiring;
776 add_to_flist(struct emp_qelem *list,
777 struct empobj *gp, int dam, natid victim)
781 fp = malloc(sizeof(struct flist));
782 fp->type = gp->ef_type;
788 emp_insque(&fp->queue, list);
792 free_flist(struct emp_qelem *list)
794 struct emp_qelem *qp, *next;
797 for (qp = list->q_forw; qp != list; qp = next) {
799 fp = (struct flist *)qp;
800 emp_remque(&fp->queue);
806 uid_eq(struct emp_qelem *elem, void *key)
808 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
811 static struct flist *
812 search_flist(struct emp_qelem *list, struct empobj *gp)
814 return (struct flist *)emp_searchque(list, gp, uid_eq);