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_unit, targ_bogus
46 struct emp_qelem queue; /* list of fired things */
47 int type; /* ship? otherwise sector */
48 int uid; /* ship uid */
49 coord x, y; /* sector coords */
50 int defdam; /* damage defenders did */
51 int victim; /* who I was shooting at */
54 static void add_to_fired_queue(struct emp_qelem *, struct emp_qelem *);
55 static int defend(struct emp_qelem *al,
57 enum targ_type target,
58 enum targ_type attacker,
62 struct shpstr *fship, int fx, int fy, int *nd);
63 static void do_defdam(struct emp_qelem *, double);
64 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
66 static void use_ammo(struct emp_qelem *);
71 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
93 enum targ_type target, attacker, orig_attacker;
96 struct nstr_item nbst;
101 union empobj_storage item;
102 struct emp_qelem fired, defended;
107 emp_initque(&defended);
108 if (!(p = getstarg(player->argp[1],
109 "Firing from ship(s), sect(s), or land unit(s)? ",
112 type = ef_byname_from(p, ef_with_guns);
113 if (type == EF_SECTOR) {
114 if (opt_NO_FORT_FIRE) {
115 pr("Fort firing is disabled.\n");
118 orig_attacker = attacker = targ_land;
120 } else if (type == EF_SHIP) {
121 orig_attacker = attacker = targ_ship;
122 } else if (type == EF_LAND) {
123 orig_attacker = attacker = targ_unit;
125 pr("Ships, land units or sectors only!\n");
128 if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
132 if (!snxtitem(&nbst, type, ptr))
135 if (player->aborted) {
136 pr("Fire aborted.\n");
139 while (nxtitem(&nbst, &item)) {
140 attacker = orig_attacker;
141 if (attacker == targ_unit) {
142 if (!getland(item.land.lnd_uid, &fland))
144 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
146 if (item.land.lnd_own != player->cnum)
149 if (lchr[fland.lnd_type].l_dam == 0) {
150 pr("Unit %d cannot fire!\n", fland.lnd_uid);
153 if (fland.lnd_item[I_MILIT] < 1) {
154 pr("Unit %d cannot fire because it has no military!\n",
158 if (fland.lnd_ship >= 0) {
159 pr("Unit %d cannot fire because it is on a ship!\n",
163 if (fland.lnd_land >= 0) {
164 pr("Unit %d cannot fire because it is on a land unit!\n",
168 if (fland.lnd_effic < LAND_MINFIREEFF) {
169 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
170 fland.lnd_uid, LAND_MINFIREEFF);
173 if (fland.lnd_item[I_SHELL] == 0) {
174 pr("%s -- not enough shells\n", prland(&fland));
177 } else if (attacker == targ_ship) {
178 if (!getship(item.ship.shp_uid, &fship))
180 if (item.ship.shp_own != player->cnum)
182 if (item.ship.shp_item[I_MILIT] < 1) {
183 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
186 if (mchr[item.ship.shp_type].m_glim == 0) {
187 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
190 if (item.ship.shp_item[I_GUN] == 0) {
191 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
194 if (item.ship.shp_item[I_SHELL] == 0) {
195 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
198 if (item.ship.shp_effic < 60) {
199 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
202 fshipno = fship.shp_uid;
203 } else if (attacker == targ_land) {
204 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
206 if (item.sect.sct_own != player->cnum)
208 if (item.sect.sct_type != SCT_FORTR)
210 if (item.sect.sct_effic < FORTEFF) {
211 pr("Fort not efficient enough to fire!\n");
214 if (item.sect.sct_item[I_GUN] == 0) {
215 pr("Not enough guns in sector %s!\n",
216 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
219 if (item.sect.sct_item[I_SHELL] == 0) {
220 pr("Not enough shells in sector %s!\n",
221 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
224 if (item.sect.sct_item[I_MILIT] < 5) {
225 pr("Not enough military in sector %s!\n",
226 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
229 pr("\nSector %s firing\n",
230 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
232 if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
235 if (player->aborted) {
236 pr("Fire aborted.\n");
240 (void)strcpy(vbuf, ptr);
245 if (target == targ_ship) {
246 vshipno = atoi(vbuf);
247 if (vshipno < 0 || !getship(vshipno, &vship) ||
249 pr("No such ship exists!\n");
252 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
253 targ_sub : targ_ship;
254 vict = vship.shp_own;
257 if (!getsect(x, y, &vsect)) {
258 pr("No such sector exists!\n");
262 if (!sarg_xy(vbuf, &x, &y) || !getsect(x, y, &vsect)) {
263 pr("No such sector exists!\n");
266 /* We check the sector type, but we only use it for damage, not
267 reporting. That way, you don't get extra information you wouldn't
268 normally get. Besides, what if they want to slam water? :) */
269 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
273 vict = vsect.sct_own;
277 if (attacker == targ_ship) {
278 if (fship.shp_own != player->cnum) {
279 pr("Not your ship!\n");
282 if (target == targ_sub || target == targ_ship) {
283 if (fship.shp_uid == vship.shp_uid) {
284 pr("You can't fire upon yourself!\n");
291 attacker = (mchr[fship.shp_type].m_flags & M_SUB) ?
292 targ_sub : targ_ship;
293 if (attacker == targ_sub){
294 pr("Subs may not fire normally.. use torpedo.\n");
298 attacker = targ_ship;
299 if ((mil = fship.shp_item[I_MILIT]) < 1) {
300 pr("Not enough military for firing crew.\n");
303 if (fship.shp_effic < 60) {
304 pr("Ship #%d is crippled (%d%%)\n",
305 fshipno, fship.shp_effic);
308 range = shp_fire_range(&fship);
309 range2 = roundrange(range);
310 pr("range is %d.00 (%.2f)\n", range2, range);
311 if (target == targ_sub
312 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
313 dam = shp_dchrg(&fship);
314 putship(fship.shp_uid, &fship);
316 pr("Not enough shells for depth charge!\n");
320 if (target == targ_sub)
321 /* Don't tell it's a sub */
323 if (fship.shp_item[I_GUN] == 0) {
324 pr("Insufficient arms.\n");
327 dam = shp_fire(&fship);
328 putship(fship.shp_uid, &fship);
334 if (opt_NOMOBCOST == 0) {
335 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
336 putship(fship.shp_uid, &fship);
338 } else if (attacker == targ_unit) {
339 if (fland.lnd_own != player->cnum) {
340 pr("Not your unit!\n");
344 if (target == targ_land) {
345 if (fland.lnd_x == vsect.sct_x
346 && fland.lnd_y == vsect.sct_y) {
347 pr("You can't fire upon yourself!\n");
355 if (lchr[fland.lnd_type].l_dam == 0) {
356 pr("Unit %d cannot fire!\n", fland.lnd_uid);
359 if (fland.lnd_item[I_GUN] == 0) {
360 pr("%s -- not enough guns\n", prland(&fland));
364 range = lnd_fire_range(&fland);
365 range2 = roundrange(range);
366 pr("range is %d.00 (%.2f)\n", range2, range);
367 if (target == targ_sub) {
368 /* Don't tell it's a sub */
372 dam = lnd_fire(&fland);
373 putland(fland.lnd_uid, &fland);
378 if (target == targ_ship) {
379 if (chance(lnd_acc(&fland) / 100.0))
380 dam = ldround(dam / 2.0, 1);
385 if (fsect.sct_own != player->cnum ||
386 fsect.sct_type != SCT_FORTR) {
387 pr("No fortress at %s\n",
388 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
391 if (target == targ_land) {
392 if (fsect.sct_x == vsect.sct_x
393 && fsect.sct_y == vsect.sct_y) {
394 pr("You can't fire upon yourself!\n");
398 attacker = targ_land;
399 if (fsect.sct_item[I_GUN] == 0) {
400 pr("Insufficient arms.\n");
403 if (fsect.sct_item[I_MILIT] < 5) {
404 pr("Not enough military for firing crew.\n");
407 dam = fort_fire(&fsect);
413 range = fortrange(&fsect);
414 range2 = roundrange(range);
415 pr("range is %d.00 (%.2f)\n", range2, range);
416 if (target == targ_sub) {
417 /* Don't tell it's a sub */
421 trange = mapdist(x, y, fx, fy);
422 if (trange > range2) {
423 pr("Target out of range.\n");
428 pr("Target out of range. Thud.\n");
431 pr("Target ship out of range. Splash.\n");
440 fland.lnd_mission = 0;
441 putland(fland.lnd_uid, &fland);
444 fship.shp_mission = 0;
445 putship(fship.shp_uid, &fship);
450 if (target == targ_bogus) {
451 if (vsect.sct_type == SCT_SANCT) {
452 pr("%s is a %s!!\n", vbuf,
453 dchr[SCT_SANCT].d_name);
455 } else if (vsect.sct_type == SCT_WATER) {
456 pr("You must specify a ship in sector %s!\n",
464 if (!trechk(player->cnum, vict, SEAFIR))
468 if (!trechk(player->cnum, vict, SUBFIR))
473 if (!trechk(player->cnum, vict, LANFIR))
481 if (target == targ_land) {
482 natp = getnatp(player->cnum);
483 rel = getrel(natp, vict);
484 if ((rel != AT_WAR) && (player->cnum != vict) &&
485 (vict) && (vsect.sct_oldown != player->cnum)) {
486 pr("You're not at war with them!\n");
496 if (vship.shp_rflags & RET_DCHRGED)
497 retreat_ship(&vship, 'd');
502 prb = range2 ? (double)trange / range2 : 1.0;
505 pr("Wind deflects shell%s.\n", splur(shots));
506 /* dam = (int)(dam / 2.0);*/
507 dam *= (90 - (random() % 11)) / 100.0;
516 nreport(player->cnum, N_SCT_SHELL, vict, 1);
517 if (vict && vict != player->cnum)
519 "Country #%d shelled sector %s for %d damage.\n",
520 player->cnum, xyas(x, y, vict), dam);
521 pr("Shell%s hit sector %s for %d damage.\n",
522 splur(shots), xyas(x, y, player->cnum), dam);
523 /* Ok, it wasn't a bogus target, so do damage. */
524 if (target != targ_bogus)
525 sectdamage(&vsect, dam, 0);
528 nreport(player->cnum, N_SHP_SHELL, vict, 1);
531 if ((target != targ_sub) ||
532 ((vship.shp_rflags & RET_DCHRGED) == 0))
533 check_retreat_and_do_shipdamage(&vship, dam);
535 shipdamage(&vship, dam);
538 "Country #%d shelled %s in %s for %d damage.\n",
539 player->cnum, prship(&vship),
540 xyas(vship.shp_x, vship.shp_y, vict), dam);
542 pr("Shell%s hit %s in %s for %d damage.\n",
543 splur(shots), prsub(&vship),
544 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
546 if (vship.shp_effic < SHIP_MINEFF)
547 pr("%s sunk!\n", prsub(&vship));
551 /* Ok, now, check if we had a bogus target. If so,
552 just continue on, since there is no defender. */
553 if (target == targ_bogus)
555 if (attacker == targ_unit) {
556 attacker = targ_land;
557 getsect(fland.lnd_x, fland.lnd_y, &fsect);
560 defend(&fired, &defended, target, attacker, &vsect, &fsect,
561 &vship, &fship, fx, fy, &ndefending);
567 putship(vship.shp_uid, &vship);
570 if ((totaldefdam == 0) && (target == targ_ship))
571 if (vship.shp_rflags & RET_HELPLESS)
572 retreat_ship(&vship, 'h');
578 if ((target == targ_ship) || (target == targ_sub)) {
579 if (fship.shp_effic > SHIP_MINEFF) {
580 shp_missdef(&fship, vict);
583 putship(fship.shp_uid, &fship);
590 odds = ((double)ndefending) / ((double)nfiring);
593 do_defdam(&fired, odds);
598 defend(struct emp_qelem *al, struct emp_qelem *dl, enum targ_type target,
599 enum targ_type attacker, struct sctstr *vsect, struct sctstr *fsect,
600 struct shpstr *vship, struct shpstr *fship, int fx, int fy, int *nd)
604 int vict, nfiring = 0;
608 if (attacker == targ_land)
609 aown = fsect->sct_own;
611 aown = fship->shp_own;
613 if (target == targ_land)
614 vict = vsect->sct_own;
616 vict = vship->shp_own;
619 (dam = quiet_bigdef(attacker, dl, vict, aown, fx, fy, &nfiring))) {
622 fp = malloc(sizeof(struct flist));
623 memset(fp, 0, sizeof(struct flist));
628 fp->x = fsect->sct_x;
629 fp->y = fsect->sct_y;
630 fp->type = targ_land;
633 fp->type = targ_ship;
634 fp->uid = fship->shp_uid;
637 emp_insque(&fp->queue, al);
644 do_defdam(struct emp_qelem *list, double odds)
647 int dam, vict, first = 1;
651 struct emp_qelem *qp, *next;
653 for (qp = list->q_forw; qp != list; qp = next) {
655 fp = (struct flist *)qp;
656 if (fp->type == targ_ship) {
657 if (!getship(fp->uid, &ship) || !ship.shp_own)
662 pr("\nDefenders fire back!\n");
665 dam = odds * fp->defdam;
667 if (fp->type == targ_ship) {
669 pr("Return fire hit %s in %s for %d damage.\n",
671 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
674 "Return fire hit %s in %s for %d damage.\n",
675 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
676 shipdamage(&ship, dam);
677 putship(ship.shp_uid, &ship);
679 getsect(fp->x, fp->y, §);
681 pr("Return fire hit sector %s for %d damage.\n",
682 xyas(fp->x, fp->y, player->cnum), dam);
683 sectdamage(§, dam, 0);
686 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
687 xyas(fp->x, fp->y, vict), dam);
689 emp_remque(&fp->queue);
695 quiet_bigdef(int attacker, struct emp_qelem *list, natid own, natid aown,
696 coord ax, coord ay, int *nfiring)
703 int dam, dam2, rel, rel2;
704 struct sctstr firing;
711 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
712 while (nxtitem(&ni, &ship)) {
713 if (ship.shp_own == 0)
716 if ((mchr[(int)ship.shp_type].m_flags & M_SUB) &&
717 (attacker == targ_land))
720 rel = getrel(getnatp(ship.shp_own), own);
721 rel2 = getrel(getnatp(ship.shp_own), aown);
722 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
724 /* Don't shoot yourself */
725 if (ship.shp_own == aown)
727 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
728 erange = torprange(&ship);
729 range = mapdist(ship.shp_x, ship.shp_y, ax, ay);
730 if (range > roundrange(erange))
732 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
734 dam2 = shp_torp(&ship, 0);
735 /* no putship(&ship) because ammo is charged in use_ammo() */
739 fp = malloc(sizeof(struct flist));
740 memset(fp, 0, sizeof(struct flist));
741 fp->type = targ_ship;
742 fp->uid = ship.shp_uid;
743 add_to_fired_queue(&fp->queue, list);
744 if (!chance(shp_torp_hitchance(&ship, range)))
748 erange = shp_fire_range(&ship);
749 if (roundrange(erange) < ni.curdist)
751 dam2 = shp_fire(&ship);
752 /* no putship(&ship) because ammo is charged in use_ammo() */
756 fp = malloc(sizeof(struct flist));
757 memset(fp, 0, sizeof(struct flist));
758 fp->type = targ_ship;
759 fp->uid = ship.shp_uid;
760 add_to_fired_queue(&fp->queue, list);
761 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
765 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
766 while (nxtitem(&ni, &land)) {
767 if (land.lnd_own == 0)
769 /* Don't shoot yourself */
770 if (land.lnd_own == aown)
773 rel = getrel(getnatp(land.lnd_own), own);
774 rel2 = getrel(getnatp(land.lnd_own), aown);
776 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
779 erange = lnd_fire_range(&land);
780 if (roundrange(erange) < ni.curdist)
783 dam2 = lnd_fire(&land);
784 /* no putland(&land) because ammo is charged in use_ammo() */
789 fp = malloc(sizeof(struct flist));
790 memset(fp, 0, sizeof(struct flist));
791 fp->type = targ_unit;
792 fp->uid = land.lnd_uid;
793 add_to_fired_queue(&fp->queue, list);
794 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
799 * Determine if any nearby gun-equipped sectors are within
800 * range and able to fire at an attacker. Firing sectors
801 * need to have guns, shells, and military. Sector being
802 * attacked is x,y -- attacker is at ax,ay.
805 if (!opt_NO_FORT_FIRE) {
806 snxtsct_dist(&ns, ax, ay, 8);
807 while (nxtsct(&ns, &firing)) {
808 if (firing.sct_own == 0)
810 rel = getrel(getnatp(firing.sct_own), own);
811 rel2 = getrel(getnatp(firing.sct_own), aown);
813 if ((firing.sct_own != own) &&
814 ((rel != ALLIED) || (rel2 != AT_WAR)))
816 /* Don't shoot yourself */
817 if (firing.sct_own == aown)
819 erange = fortrange(&firing);
820 if (roundrange(erange) < ns.curdist)
823 dam2 = fort_fire(&firing);
824 /* no putsect(&firing) because ammo is charged in use_ammo() */
828 fp = malloc(sizeof(struct flist));
829 memset(fp, 0, sizeof(struct flist));
830 fp->x = firing.sct_x;
831 fp->y = firing.sct_y;
832 fp->type = targ_land;
833 add_to_fired_queue(&fp->queue, list);
834 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
839 return *nfiring == 0 ? 0 : dam / *nfiring;
843 use_ammo(struct emp_qelem *list)
845 struct emp_qelem *qp, *next;
853 /* use 1 shell from everyone */
854 for (qp = list->q_forw; qp != list; qp = next) {
856 fp = (struct flist *)qp;
857 if (fp->type == targ_ship) {
858 getship(fp->uid, &ship);
859 item = ship.shp_item;
860 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
861 shell = item[I_SHELL];
862 shell -= SHP_TORP_SHELLS - 1;
865 item[I_SHELL] = shell;
866 putship(ship.shp_uid, &ship);
867 /* mob cost = 1/2 a sect's mob */
868 ship.shp_mobil -= shp_mobcost(&ship) / 2.0;
870 } else if (fp->type == targ_land) {
871 getsect(fp->x, fp->y, §);
872 item = sect.sct_item;
874 getland(fp->uid, &land);
875 item = land.lnd_item;
877 shell = item[I_SHELL];
881 item[I_SHELL] = shell;
882 if (fp->type == targ_ship)
883 putship(ship.shp_uid, &ship);
884 else if (fp->type == targ_land)
887 putland(land.lnd_uid, &land);
889 emp_remque(&fp->queue);
896 add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
898 struct emp_qelem *qp;
900 struct flist *ep = (struct flist *)elem;
902 /* Don't put them on the list if they're already there */
903 for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
904 fp = (struct flist *)qp;
905 if (fp->type != targ_land && fp->uid == ep->uid)
907 if (fp->type != targ_land
908 && fp->x == ep->x && fp->y == ep->y)
911 emp_insque(elem, list);