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:
43 targ_land, targ_ship, targ_sub, targ_unit, targ_bogus
47 struct emp_qelem queue; /* list of fired things */
48 int type; /* ship? otherwise sector */
49 int uid; /* ship uid */
50 coord x, y; /* sector coords */
51 int defdam; /* damage defenders did */
52 int victim; /* who I was shooting at */
55 static void add_to_fired_queue(struct emp_qelem *, struct emp_qelem *);
56 static int defend(struct emp_qelem *al,
58 enum targ_type target,
59 enum targ_type attacker,
63 struct shpstr *fship, int fx, int fy, int *nd);
64 static void do_defdam(struct emp_qelem *, double);
65 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
67 static void use_ammo(struct emp_qelem *);
72 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
96 enum targ_type target, attacker, orig_attacker;
99 struct nstr_item nbst;
104 union empobj_storage item;
105 struct emp_qelem fired, defended;
110 emp_initque(&defended);
111 if (!(p = getstarg(player->argp[1],
112 "Firing from ship(s), sect(s), or land unit(s)? ",
115 type = ef_byname_from(p, ef_with_guns);
116 if (type == EF_SECTOR) {
117 if (opt_NO_FORT_FIRE) {
118 pr("Fort firing is disabled.\n");
121 orig_attacker = attacker = targ_land;
123 } else if (type == EF_SHIP) {
124 orig_attacker = attacker = targ_ship;
125 } else if (type == EF_LAND) {
126 orig_attacker = attacker = targ_unit;
128 pr("Ships, land units or sectors only!\n");
131 if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
135 if (!snxtitem(&nbst, type, ptr))
138 if (player->aborted) {
139 pr("Fire aborted.\n");
142 while (nxtitem(&nbst, &item)) {
143 attacker = orig_attacker;
144 if (attacker == targ_unit) {
145 if (!getland(item.land.lnd_uid, &fland))
147 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
149 if (item.land.lnd_own != player->cnum)
152 if (fland.lnd_frg == 0) {
153 pr("Unit %d cannot fire!\n", fland.lnd_uid);
156 if (fland.lnd_item[I_MILIT] < 1) {
157 pr("Unit %d cannot fire because it has no military!\n",
161 if (fland.lnd_ship >= 0) {
162 pr("Unit %d cannot fire because it is on a ship!\n",
166 if (fland.lnd_land >= 0) {
167 pr("Unit %d cannot fire because it is on a land unit!\n",
171 if (fland.lnd_effic < LAND_MINFIREEFF) {
172 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
173 fland.lnd_uid, LAND_MINFIREEFF);
176 resupply_commod(&fland, I_SHELL); /* Get more shells */
177 putland(fland.lnd_uid, &fland);
178 if (fland.lnd_item[I_SHELL] == 0) {
179 pr("%s -- not enough shells\n", prland(&fland));
182 } else if (attacker == targ_ship) {
183 if (!getship(item.ship.shp_uid, &fship))
185 if (item.ship.shp_own != player->cnum)
187 if (item.ship.shp_item[I_MILIT] < 1) {
188 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
191 if (item.ship.shp_glim == 0) {
192 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
195 if (item.ship.shp_item[I_GUN] == 0) {
196 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
199 if (item.ship.shp_item[I_SHELL] == 0) {
200 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
203 if (item.ship.shp_effic < 60) {
204 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
207 fshipno = fship.shp_uid;
208 } else if (attacker == targ_land) {
209 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
211 if (item.sect.sct_own != player->cnum)
213 if (item.sect.sct_type != SCT_FORTR)
215 if (item.sect.sct_effic < FORTEFF) {
216 pr("Fort not efficient enough to fire!\n");
219 if (item.sect.sct_item[I_GUN] == 0) {
220 pr("Not enough guns in sector %s!\n",
221 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
224 if (item.sect.sct_item[I_SHELL] == 0) {
225 pr("Not enough shells in sector %s!\n",
226 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
229 if (item.sect.sct_item[I_MILIT] < 5) {
230 pr("Not enough military in sector %s!\n",
231 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
234 pr("\nSector %s firing\n",
235 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
237 if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
240 if (player->aborted) {
241 pr("Fire aborted.\n");
245 (void)strcpy(vbuf, ptr);
250 if (target == targ_ship) {
251 vshipno = atoi(vbuf);
252 if (vshipno < 0 || !getship(vshipno, &vship) ||
254 pr("No such ship exists!\n");
257 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
258 targ_sub : targ_ship;
259 vict = vship.shp_own;
262 if (!getsect(x, y, &vsect)) {
263 pr("No such sector exists!\n");
267 if (!sarg_xy(vbuf, &x, &y) || !getsect(x, y, &vsect)) {
268 pr("No such sector exists!\n");
271 /* We check the sector type, but we only use it for damage, not
272 reporting. That way, you don't get extra information you wouldn't
273 normally get. Besides, what if they want to slam water? :) */
274 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
278 vict = vsect.sct_own;
282 if (attacker == targ_ship) {
283 if (fship.shp_own != player->cnum) {
284 pr("Not your ship!\n");
287 if (target == targ_sub || target == targ_ship) {
288 if (fship.shp_uid == vship.shp_uid) {
289 pr("You can't fire upon yourself!\n");
296 attacker = (mchr[fship.shp_type].m_flags & M_SUB) ?
297 targ_sub : targ_ship;
298 if (attacker == targ_sub){
299 pr("Subs may not fire normally.. use torpedo.\n");
303 attacker = targ_ship;
304 if ((mil = fship.shp_item[I_MILIT]) < 1) {
305 pr("Not enough military for firing crew.\n");
308 if (fship.shp_effic < 60) {
309 pr("Ship #%d is crippled (%d%%)\n",
310 fshipno, fship.shp_effic);
313 range = effrange(fship.shp_frnge, fship.shp_tech);
314 range2 = roundrange(range);
315 pr("range is %d.00 (%.2f)\n", range2, range);
316 if (target == targ_sub
317 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
318 dam = shp_dchrg(&fship);
319 putship(fship.shp_uid, &fship);
321 pr("Not enough shells for depth charge!\n");
325 if (target == targ_sub)
326 /* Don't tell it's a sub */
328 if (fship.shp_item[I_GUN] == 0) {
329 pr("Insufficient arms.\n");
332 dam = shp_fire(&fship);
333 putship(fship.shp_uid, &fship);
339 if (opt_NOMOBCOST == 0) {
340 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
341 putship(fship.shp_uid, &fship);
343 } else if (attacker == targ_unit) {
344 if (fland.lnd_own != player->cnum) {
345 pr("Not your unit!\n");
349 if (target == targ_land) {
350 if (fland.lnd_x == vsect.sct_x
351 && fland.lnd_y == vsect.sct_y) {
352 pr("You can't fire upon yourself!\n");
360 if (fland.lnd_frg == 0) {
361 pr("Unit %d cannot fire!\n", fland.lnd_uid);
364 if (fland.lnd_item[I_SHELL] == 0) {
365 pr("%s -- not enough shells\n", prland(&fland));
369 shell = fland.lnd_item[I_SHELL];
371 range = effrange(fland.lnd_frg, fland.lnd_tech);
372 range2 = roundrange(range);
373 pr("range is %d.00 (%.2f)\n", range2, range);
374 if (target == targ_sub) {
375 /* Don't tell it's a sub */
379 gun = fland.lnd_item[I_GUN];
381 pr("%s -- not enough guns\n", prland(&fland));
385 dam = (int)landunitgun(fland.lnd_effic, fland.lnd_dam, gun,
386 fland.lnd_ammo, shell);
387 if (target == targ_ship) {
388 if (chance(fland.lnd_acc / 100.0))
389 dam = ldround(dam / 2.0, 1);
392 resupply_commod(&fland, I_SHELL); /* Get more shells */
393 putland(fland.lnd_uid, &fland);
397 if (fsect.sct_own != player->cnum ||
398 fsect.sct_type != SCT_FORTR) {
399 pr("No fortress at %s\n",
400 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
403 if (target == targ_land) {
404 if (fsect.sct_x == vsect.sct_x
405 && fsect.sct_y == vsect.sct_y) {
406 pr("You can't fire upon yourself!\n");
410 attacker = targ_land;
411 if (fsect.sct_item[I_GUN] == 0) {
412 pr("Insufficient arms.\n");
415 if (fsect.sct_item[I_MILIT] < 5) {
416 pr("Not enough military for firing crew.\n");
419 dam = fort_fire(&fsect);
425 range = fortrange(&fsect);
426 range2 = roundrange(range);
427 pr("range is %d.00 (%.2f)\n", range2, range);
428 if (target == targ_sub) {
429 /* Don't tell it's a sub */
433 trange = mapdist(x, y, fx, fy);
434 if (trange > range2) {
435 pr("Target out of range.\n");
440 pr("Target out of range. Thud.\n");
443 pr("Target ship out of range. Splash.\n");
452 fland.lnd_mission = 0;
453 putland(fland.lnd_uid, &fland);
456 fship.shp_mission = 0;
457 putship(fship.shp_uid, &fship);
462 if (target == targ_bogus) {
463 if (vsect.sct_type == SCT_SANCT) {
464 pr("%s is a %s!!\n", vbuf,
465 dchr[SCT_SANCT].d_name);
467 } else if (vsect.sct_type == SCT_WATER) {
468 pr("You must specify a ship in sector %s!\n",
476 if (!trechk(player->cnum, vict, SEAFIR))
480 if (!trechk(player->cnum, vict, SUBFIR))
485 if (!trechk(player->cnum, vict, LANFIR))
493 if (target == targ_land) {
494 natp = getnatp(player->cnum);
495 rel = getrel(natp, vict);
496 if ((rel != AT_WAR) && (player->cnum != vict) &&
497 (vict) && (vsect.sct_oldown != player->cnum)) {
498 pr("You're not at war with them!\n");
508 if (vship.shp_rflags & RET_DCHRGED)
509 retreat_ship(&vship, 'd');
514 prb = range2 ? (double)trange / range2 : 1.0;
517 pr("Wind deflects shell%s.\n", splur(shots));
518 /* dam = (int)(dam / 2.0);*/
519 dam *= (90 - (random() % 11)) / 100.0;
528 nreport(player->cnum, N_SCT_SHELL, vict, 1);
529 if (vict && vict != player->cnum)
531 "Country #%d shelled sector %s for %d damage.\n",
532 player->cnum, xyas(x, y, vict), dam);
533 pr("Shell%s hit sector %s for %d damage.\n",
534 splur(shots), xyas(x, y, player->cnum), dam);
535 /* Ok, it wasn't a bogus target, so do damage. */
536 if (target != targ_bogus)
537 sectdamage(&vsect, dam, 0);
540 nreport(player->cnum, N_SHP_SHELL, vict, 1);
543 if ((target != targ_sub) ||
544 ((vship.shp_rflags & RET_DCHRGED) == 0))
545 check_retreat_and_do_shipdamage(&vship, dam);
547 shipdamage(&vship, dam);
550 "Country #%d shelled %s in %s for %d damage.\n",
551 player->cnum, prship(&vship),
552 xyas(vship.shp_x, vship.shp_y, vict), dam);
554 pr("Shell%s hit %s in %s for %d damage.\n",
555 splur(shots), prsub(&vship),
556 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
558 if (vship.shp_effic < SHIP_MINEFF)
559 pr("%s sunk!\n", prsub(&vship));
563 /* Ok, now, check if we had a bogus target. If so,
564 just continue on, since there is no defender. */
565 if (target == targ_bogus)
567 if (attacker == targ_unit) {
568 attacker = targ_land;
569 getsect(fland.lnd_x, fland.lnd_y, &fsect);
572 defend(&fired, &defended, target, attacker, &vsect, &fsect,
573 &vship, &fship, fx, fy, &ndefending);
579 putship(vship.shp_uid, &vship);
582 if ((totaldefdam == 0) && (target == targ_ship))
583 if (vship.shp_rflags & RET_HELPLESS)
584 retreat_ship(&vship, 'h');
590 if ((target == targ_ship) || (target == targ_sub)) {
591 if (fship.shp_effic > SHIP_MINEFF) {
592 shp_missdef(&fship, vict);
595 putship(fship.shp_uid, &fship);
602 odds = ((double)ndefending) / ((double)nfiring);
605 do_defdam(&fired, odds);
610 defend(struct emp_qelem *al, struct emp_qelem *dl, enum targ_type target,
611 enum targ_type attacker, struct sctstr *vsect, struct sctstr *fsect,
612 struct shpstr *vship, struct shpstr *fship, int fx, int fy, int *nd)
616 int vict, nfiring = 0;
620 if (attacker == targ_land)
621 aown = fsect->sct_own;
623 aown = fship->shp_own;
625 if (target == targ_land)
626 vict = vsect->sct_own;
628 vict = vship->shp_own;
631 (dam = quiet_bigdef(attacker, dl, vict, aown, fx, fy, &nfiring))) {
634 fp = malloc(sizeof(struct flist));
635 memset(fp, 0, sizeof(struct flist));
640 fp->x = fsect->sct_x;
641 fp->y = fsect->sct_y;
642 fp->type = targ_land;
645 fp->type = targ_ship;
646 fp->uid = fship->shp_uid;
649 emp_insque(&fp->queue, al);
656 do_defdam(struct emp_qelem *list, double odds)
659 int dam, vict, first = 1;
663 struct emp_qelem *qp, *next;
665 for (qp = list->q_forw; qp != list; qp = next) {
667 fp = (struct flist *)qp;
668 if (fp->type == targ_ship) {
669 if (!getship(fp->uid, &ship) || !ship.shp_own)
674 pr("\nDefenders fire back!\n");
677 dam = odds * fp->defdam;
679 if (fp->type == targ_ship) {
681 pr("Return fire hit %s in %s for %d damage.\n",
683 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
686 "Return fire hit %s in %s for %d damage.\n",
687 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
688 shipdamage(&ship, dam);
689 putship(ship.shp_uid, &ship);
691 getsect(fp->x, fp->y, §);
693 pr("Return fire hit sector %s for %d damage.\n",
694 xyas(fp->x, fp->y, player->cnum), dam);
695 sectdamage(§, dam, 0);
698 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
699 xyas(fp->x, fp->y, vict), dam);
701 emp_remque(&fp->queue);
707 quiet_bigdef(int attacker, struct emp_qelem *list, natid own, natid aown,
708 coord ax, coord ay, int *nfiring)
711 double erange, hitchance;
715 int dam, dam2, rel, rel2;
716 struct sctstr firing;
724 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
725 while (nxtitem(&ni, &ship)) {
726 if (ship.shp_own == 0)
729 if ((mchr[(int)ship.shp_type].m_flags & M_SUB) &&
730 (attacker == targ_land))
733 rel = getrel(getnatp(ship.shp_own), own);
734 rel2 = getrel(getnatp(ship.shp_own), aown);
735 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
737 /* Don't shoot yourself */
738 if (ship.shp_own == aown)
740 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
741 if (ship.shp_effic < 60)
743 gun = ship.shp_item[I_GUN];
744 shell = ship.shp_item[I_SHELL];
745 if (ship.shp_item[I_MILIT] < 1)
747 if (shell < SHP_TORP_SHELLS)
748 shell += supply_commod(ship.shp_own,
749 ship.shp_x, ship.shp_y,
750 I_SHELL, SHP_TORP_SHELLS - shell);
751 if (shell < SHP_TORP_SHELLS)
756 if (ship.shp_mobil <= 0)
759 erange = torprange(&ship);
760 range = mapdist(ship.shp_x, ship.shp_y, ax, ay);
761 if (range > roundrange(erange))
763 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
767 fp = malloc(sizeof(struct flist));
768 memset(fp, 0, sizeof(struct flist));
769 fp->type = targ_ship;
770 fp->uid = ship.shp_uid;
771 add_to_fired_queue(&fp->queue, list);
773 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
775 hitchance = DTORP_HITCHANCE(range, ship.shp_visib);
776 if (!chance(hitchance))
779 dam += TORP_DAMAGE();
781 erange = effrange(ship.shp_frnge, ship.shp_tech);
782 if (roundrange(erange) < ni.curdist)
784 dam2 = shp_fire(&ship);
785 /* no putship(&ship) because ammo is charged in use_ammo() */
789 fp = malloc(sizeof(struct flist));
790 memset(fp, 0, sizeof(struct flist));
791 fp->type = targ_ship;
792 fp->uid = ship.shp_uid;
793 add_to_fired_queue(&fp->queue, list);
794 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
798 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
799 while (nxtitem(&ni, &land)) {
800 if (land.lnd_own == 0)
802 if (land.lnd_effic < LAND_MINFIREEFF)
804 /* Can't fire if on a ship */
805 if (land.lnd_ship >= 0)
807 if (land.lnd_land >= 0)
809 /* Gotta have military */
810 if (land.lnd_item[I_MILIT] < 1)
812 /* Don't shoot yourself */
813 if (land.lnd_own == aown)
816 rel = getrel(getnatp(land.lnd_own), own);
817 rel2 = getrel(getnatp(land.lnd_own), aown);
819 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
822 erange = effrange(land.lnd_frg, land.lnd_tech);
823 if (roundrange(erange) < ni.curdist)
827 if (!has_supply(&land))
830 gun = land.lnd_item[I_GUN];
831 shell = land.lnd_item[I_SHELL];
833 if (land.lnd_item[I_MILIT] == 0 || shell == 0 || gun == 0)
836 dam2 = (int)landunitgun(land.lnd_effic, land.lnd_dam, gun,
837 land.lnd_ammo, shell);
840 fp = malloc(sizeof(struct flist));
841 memset(fp, 0, sizeof(struct flist));
842 fp->type = targ_unit;
843 fp->uid = land.lnd_uid;
844 add_to_fired_queue(&fp->queue, list);
846 putland(land.lnd_uid, &land);
847 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
852 * Determine if any nearby gun-equipped sectors are within
853 * range and able to fire at an attacker. Firing sectors
854 * need to have guns, shells, and military. Sector being
855 * attacked is x,y -- attacker is at ax,ay.
858 if (!opt_NO_FORT_FIRE) {
859 snxtsct_dist(&ns, ax, ay, 8);
860 while (nxtsct(&ns, &firing)) {
861 if (firing.sct_own == 0)
863 rel = getrel(getnatp(firing.sct_own), own);
864 rel2 = getrel(getnatp(firing.sct_own), aown);
866 if ((firing.sct_own != own) &&
867 ((rel != ALLIED) || (rel2 != AT_WAR)))
869 /* Don't shoot yourself */
870 if (firing.sct_own == aown)
872 erange = fortrange(&firing);
873 if (roundrange(erange) < ns.curdist)
876 dam2 = fort_fire(&firing);
877 /* no putsect(&firing) because ammo is charged in use_ammo() */
881 fp = malloc(sizeof(struct flist));
882 memset(fp, 0, sizeof(struct flist));
883 fp->x = firing.sct_x;
884 fp->y = firing.sct_y;
885 fp->type = targ_land;
886 add_to_fired_queue(&fp->queue, list);
887 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
892 return *nfiring == 0 ? 0 : dam / *nfiring;
896 use_ammo(struct emp_qelem *list)
898 struct emp_qelem *qp, *next;
906 /* use 1 shell from everyone */
907 for (qp = list->q_forw; qp != list; qp = next) {
909 fp = (struct flist *)qp;
910 if (fp->type == targ_ship) {
911 getship(fp->uid, &ship);
912 item = ship.shp_item;
913 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
914 shell = item[I_SHELL];
915 shell -= SHP_TORP_SHELLS - 1;
918 item[I_SHELL] = shell;
919 putship(ship.shp_uid, &ship);
920 /* mob cost = 1/2 a sect's mob */
921 ship.shp_mobil -= shp_mobcost(&ship) / 2.0;
923 } else if (fp->type == targ_land) {
924 getsect(fp->x, fp->y, §);
925 item = sect.sct_item;
927 getland(fp->uid, &land);
928 item = land.lnd_item;
930 shell = item[I_SHELL];
934 item[I_SHELL] = shell;
935 if (fp->type == targ_ship)
936 putship(ship.shp_uid, &ship);
937 else if (fp->type == targ_land)
940 putland(land.lnd_uid, &land);
942 emp_remque(&fp->queue);
949 add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
951 struct emp_qelem *qp;
952 struct flist *fp, *ep;
955 ep = (struct flist *)elem;
957 /* Don't put them on the list if they're already there */
958 for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
959 fp = (struct flist *)qp;
960 if ((fp->type == targ_ship) && (fp->uid == ep->uid))
962 if ((fp->type != targ_ship) && (fp->x == ep->x) &&
968 emp_insque(elem, list);