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 *, struct emp_qelem *,
56 struct empobj *, natid, int *);
57 static void do_defdam(struct emp_qelem *, double);
58 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
60 static void use_ammo(struct emp_qelem *);
65 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
86 enum targ_type target, attacker, orig_attacker;
89 struct nstr_item nbst;
95 union empobj_storage item;
96 struct emp_qelem fired, defended;
101 emp_initque(&defended);
102 if (!(p = getstarg(player->argp[1],
103 "Firing from ship(s), sect(s), or land unit(s)? ",
106 type = ef_byname_from(p, ef_with_guns);
107 if (type == EF_SECTOR) {
108 if (opt_NO_FORT_FIRE) {
109 pr("Fort firing is disabled.\n");
112 orig_attacker = attacker = targ_land;
114 } else if (type == EF_SHIP) {
115 orig_attacker = attacker = targ_ship;
116 } else if (type == EF_LAND) {
117 orig_attacker = attacker = targ_unit;
119 pr("Ships, land units or sectors only!\n");
122 if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
126 if (!snxtitem(&nbst, type, ptr))
129 if (player->aborted) {
130 pr("Fire aborted.\n");
133 while (nxtitem(&nbst, &item)) {
134 attacker = orig_attacker;
135 if (attacker == targ_unit) {
136 if (!getland(item.land.lnd_uid, &fland))
138 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
140 if (item.land.lnd_own != player->cnum)
143 if (lchr[fland.lnd_type].l_dam == 0) {
144 pr("Unit %d cannot fire!\n", fland.lnd_uid);
147 if (fland.lnd_item[I_MILIT] < 1) {
148 pr("Unit %d cannot fire because it has no military!\n",
152 if (fland.lnd_ship >= 0) {
153 pr("Unit %d cannot fire because it is on a ship!\n",
157 if (fland.lnd_land >= 0) {
158 pr("Unit %d cannot fire because it is on a land unit!\n",
162 if (fland.lnd_effic < LAND_MINFIREEFF) {
163 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
164 fland.lnd_uid, LAND_MINFIREEFF);
167 if (fland.lnd_item[I_SHELL] == 0) {
168 pr("%s -- not enough shells\n", prland(&fland));
171 } else if (attacker == targ_ship) {
172 if (!getship(item.ship.shp_uid, &fship))
174 if (item.ship.shp_own != player->cnum)
176 if (item.ship.shp_item[I_MILIT] < 1) {
177 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
180 if (mchr[item.ship.shp_type].m_glim == 0) {
181 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
184 if (item.ship.shp_item[I_GUN] == 0) {
185 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
188 if (item.ship.shp_item[I_SHELL] == 0) {
189 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
192 if (item.ship.shp_effic < 60) {
193 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
196 } else if (attacker == targ_land) {
197 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
199 if (item.sect.sct_own != player->cnum)
201 if (item.sect.sct_type != SCT_FORTR)
203 if (item.sect.sct_effic < FORTEFF) {
204 pr("Fort not efficient enough to fire!\n");
207 if (item.sect.sct_item[I_GUN] == 0) {
208 pr("Not enough guns in sector %s!\n",
209 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
212 if (item.sect.sct_item[I_SHELL] == 0) {
213 pr("Not enough shells in sector %s!\n",
214 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
217 if (item.sect.sct_item[I_MILIT] < 5) {
218 pr("Not enough military in sector %s!\n",
219 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
222 pr("\nSector %s firing\n",
223 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
225 if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
228 if (player->aborted) {
229 pr("Fire aborted.\n");
233 (void)strcpy(vbuf, ptr);
238 if (target == targ_ship) {
239 vshipno = atoi(vbuf);
240 if (vshipno < 0 || !getship(vshipno, &vship) ||
242 pr("No such ship exists!\n");
245 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
246 targ_sub : targ_ship;
247 vict = vship.shp_own;
250 if (!getsect(x, y, &vsect)) {
251 pr("No such sector exists!\n");
255 if (!sarg_xy(vbuf, &x, &y) || !getsect(x, y, &vsect)) {
256 pr("No such sector exists!\n");
259 /* We check the sector type, but we only use it for damage, not
260 reporting. That way, you don't get extra information you wouldn't
261 normally get. Besides, what if they want to slam water? :) */
262 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
266 vict = vsect.sct_own;
270 if (attacker == targ_ship) {
271 if (fship.shp_own != player->cnum) {
272 pr("Not your ship!\n");
275 if (target == targ_sub || target == targ_ship) {
276 if (fship.shp_uid == vship.shp_uid) {
277 pr("You can't fire upon yourself!\n");
284 attacker = (mchr[fship.shp_type].m_flags & M_SUB) ?
285 targ_sub : targ_ship;
286 if (attacker == targ_sub){
287 pr("Subs may not fire normally.. use torpedo.\n");
291 attacker = targ_ship;
292 if ((mil = fship.shp_item[I_MILIT]) < 1) {
293 pr("Not enough military for firing crew.\n");
296 if (fship.shp_effic < 60) {
297 pr("Ship #%d is crippled (%d%%)\n",
298 fship.shp_uid, fship.shp_effic);
301 range = shp_fire_range(&fship);
302 range2 = roundrange(range);
303 pr("range is %d.00 (%.2f)\n", range2, range);
304 if (target == targ_sub
305 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
306 dam = shp_dchrg(&fship);
307 putship(fship.shp_uid, &fship);
309 pr("Not enough shells for depth charge!\n");
313 if (target == targ_sub)
314 /* Don't tell it's a sub */
316 if (fship.shp_item[I_GUN] == 0) {
317 pr("Insufficient arms.\n");
320 dam = shp_fire(&fship);
321 putship(fship.shp_uid, &fship);
327 if (opt_NOMOBCOST == 0) {
328 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
329 putship(fship.shp_uid, &fship);
331 } else if (attacker == targ_unit) {
332 if (fland.lnd_own != player->cnum) {
333 pr("Not your unit!\n");
337 if (target == targ_land) {
338 if (fland.lnd_x == vsect.sct_x
339 && fland.lnd_y == vsect.sct_y) {
340 pr("You can't fire upon yourself!\n");
348 if (lchr[fland.lnd_type].l_dam == 0) {
349 pr("Unit %d cannot fire!\n", fland.lnd_uid);
352 if (fland.lnd_item[I_GUN] == 0) {
353 pr("%s -- not enough guns\n", prland(&fland));
357 range = lnd_fire_range(&fland);
358 range2 = roundrange(range);
359 pr("range is %d.00 (%.2f)\n", range2, range);
360 if (target == targ_sub) {
361 /* Don't tell it's a sub */
365 dam = lnd_fire(&fland);
366 putland(fland.lnd_uid, &fland);
371 if (target == targ_ship) {
372 if (chance(lnd_acc(&fland) / 100.0))
373 dam = ldround(dam / 2.0, 1);
378 if (fsect.sct_own != player->cnum ||
379 fsect.sct_type != SCT_FORTR) {
380 pr("No fortress at %s\n",
381 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
384 if (target == targ_land) {
385 if (fsect.sct_x == vsect.sct_x
386 && fsect.sct_y == vsect.sct_y) {
387 pr("You can't fire upon yourself!\n");
391 attacker = targ_land;
392 if (fsect.sct_item[I_GUN] == 0) {
393 pr("Insufficient arms.\n");
396 if (fsect.sct_item[I_MILIT] < 5) {
397 pr("Not enough military for firing crew.\n");
400 dam = fort_fire(&fsect);
406 range = fortrange(&fsect);
407 range2 = roundrange(range);
408 pr("range is %d.00 (%.2f)\n", range2, range);
409 if (target == targ_sub) {
410 /* Don't tell it's a sub */
414 trange = mapdist(x, y, fx, fy);
415 if (trange > range2) {
416 pr("Target out of range.\n");
421 pr("Target out of range. Thud.\n");
424 pr("Target ship out of range. Splash.\n");
433 fland.lnd_mission = 0;
434 putland(fland.lnd_uid, &fland);
437 fship.shp_mission = 0;
438 putship(fship.shp_uid, &fship);
443 if (target == targ_bogus) {
444 if (vsect.sct_type == SCT_SANCT) {
445 pr("%s is a %s!!\n", vbuf,
446 dchr[SCT_SANCT].d_name);
448 } else if (vsect.sct_type == SCT_WATER) {
449 pr("You must specify a ship in sector %s!\n",
457 if (!trechk(player->cnum, vict, SEAFIR))
461 if (!trechk(player->cnum, vict, SUBFIR))
466 if (!trechk(player->cnum, vict, LANFIR))
474 if (target == targ_land) {
475 natp = getnatp(player->cnum);
476 rel = getrel(natp, vict);
477 if ((rel != AT_WAR) && (player->cnum != vict) &&
478 (vict) && (vsect.sct_oldown != player->cnum)) {
479 pr("You're not at war with them!\n");
489 if (vship.shp_rflags & RET_DCHRGED)
490 retreat_ship(&vship, 'd');
495 prb = range2 ? (double)trange / range2 : 1.0;
498 pr("Wind deflects shell%s.\n", splur(shots));
499 /* dam = (int)(dam / 2.0);*/
500 dam *= (90 - (random() % 11)) / 100.0;
509 nreport(player->cnum, N_SCT_SHELL, vict, 1);
510 if (vict && vict != player->cnum)
512 "Country #%d shelled sector %s for %d damage.\n",
513 player->cnum, xyas(x, y, vict), dam);
514 pr("Shell%s hit sector %s for %d damage.\n",
515 splur(shots), xyas(x, y, player->cnum), dam);
516 /* Ok, it wasn't a bogus target, so do damage. */
517 if (target != targ_bogus)
518 sectdamage(&vsect, dam, 0);
521 nreport(player->cnum, N_SHP_SHELL, vict, 1);
524 if ((target != targ_sub) ||
525 ((vship.shp_rflags & RET_DCHRGED) == 0))
526 check_retreat_and_do_shipdamage(&vship, dam);
528 shipdamage(&vship, dam);
531 "Country #%d shelled %s in %s for %d damage.\n",
532 player->cnum, prship(&vship),
533 xyas(vship.shp_x, vship.shp_y, vict), dam);
535 pr("Shell%s hit %s in %s for %d damage.\n",
536 splur(shots), prsub(&vship),
537 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
539 if (vship.shp_effic < SHIP_MINEFF)
540 pr("%s sunk!\n", prsub(&vship));
544 /* Ok, now, check if we had a bogus target. If so,
545 just continue on, since there is no defender. */
546 if (target == targ_bogus)
549 if (attacker == targ_unit) {
550 attacker = targ_land;
551 getsect(fland.lnd_x, fland.lnd_y, &fsect);
552 attgp = (struct empobj *)&fsect;
554 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
560 putship(vship.shp_uid, &vship);
563 if ((totaldefdam == 0) && (target == targ_ship))
564 if (vship.shp_rflags & RET_HELPLESS)
565 retreat_ship(&vship, 'h');
571 if ((target == targ_ship) || (target == targ_sub)) {
572 if (fship.shp_effic > SHIP_MINEFF) {
573 shp_missdef(&fship, vict);
576 putship(fship.shp_uid, &fship);
583 odds = ((double)ndefending) / ((double)nfiring);
586 do_defdam(&fired, odds);
591 defend(struct emp_qelem *al, struct emp_qelem *dl,
592 struct empobj *attgp, natid vict, int *nd)
599 dam = quiet_bigdef(attgp->ef_type, dl, vict,
600 attgp->own, attgp->x, attgp->y, &nfiring);
604 fp = malloc(sizeof(struct flist));
605 memset(fp, 0, sizeof(struct flist));
608 switch (attgp->ef_type) {
612 fp->type = targ_land;
615 fp->type = targ_ship;
616 fp->uid = attgp->uid;
619 emp_insque(&fp->queue, al);
626 do_defdam(struct emp_qelem *list, double odds)
629 int dam, vict, first = 1;
633 struct emp_qelem *qp, *next;
635 for (qp = list->q_forw; qp != list; qp = next) {
637 fp = (struct flist *)qp;
638 if (fp->type == targ_ship) {
639 if (!getship(fp->uid, &ship) || !ship.shp_own)
644 pr("\nDefenders fire back!\n");
647 dam = odds * fp->defdam;
649 if (fp->type == targ_ship) {
651 pr("Return fire hit %s in %s for %d damage.\n",
653 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
656 "Return fire hit %s in %s for %d damage.\n",
657 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
658 shipdamage(&ship, dam);
659 putship(ship.shp_uid, &ship);
661 getsect(fp->x, fp->y, §);
663 pr("Return fire hit sector %s for %d damage.\n",
664 xyas(fp->x, fp->y, player->cnum), dam);
665 sectdamage(§, dam, 0);
668 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
669 xyas(fp->x, fp->y, vict), dam);
671 emp_remque(&fp->queue);
677 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
678 coord ax, coord ay, int *nfiring)
684 int dam, dam2, rel, rel2;
685 struct sctstr firing;
692 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
693 while (nxtitem(&ni, &ship)) {
694 if (ship.shp_own == 0)
697 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
700 rel = getrel(getnatp(ship.shp_own), own);
701 rel2 = getrel(getnatp(ship.shp_own), aown);
702 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
704 /* Don't shoot yourself */
705 if (ship.shp_own == aown)
707 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
708 erange = torprange(&ship);
709 if (roundrange(erange) < ni.curdist)
711 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
713 dam2 = shp_torp(&ship, 0);
714 /* no putship(&ship) because ammo is charged in use_ammo() */
717 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
720 erange = shp_fire_range(&ship);
721 if (roundrange(erange) < ni.curdist)
723 dam2 = shp_fire(&ship);
724 /* no putship(&ship) because ammo is charged in use_ammo() */
727 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
730 fp = malloc(sizeof(struct flist));
731 memset(fp, 0, sizeof(struct flist));
732 fp->type = targ_ship;
733 fp->uid = ship.shp_uid;
734 add_to_fired_queue(&fp->queue, list);
737 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
738 while (nxtitem(&ni, &land)) {
739 if (land.lnd_own == 0)
741 /* Don't shoot yourself */
742 if (land.lnd_own == aown)
745 rel = getrel(getnatp(land.lnd_own), own);
746 rel2 = getrel(getnatp(land.lnd_own), aown);
748 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
751 erange = lnd_fire_range(&land);
752 if (roundrange(erange) < ni.curdist)
755 dam2 = lnd_fire(&land);
756 /* no putland(&land) because ammo is charged in use_ammo() */
761 fp = malloc(sizeof(struct flist));
762 memset(fp, 0, sizeof(struct flist));
763 fp->type = targ_unit;
764 fp->uid = land.lnd_uid;
765 add_to_fired_queue(&fp->queue, list);
766 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
771 * Determine if any nearby gun-equipped sectors are within
772 * range and able to fire at an attacker. Firing sectors
773 * need to have guns, shells, and military. Sector being
774 * attacked is x,y -- attacker is at ax,ay.
777 if (!opt_NO_FORT_FIRE) {
778 snxtsct_dist(&ns, ax, ay, 8);
779 while (nxtsct(&ns, &firing)) {
780 if (firing.sct_own == 0)
782 rel = getrel(getnatp(firing.sct_own), own);
783 rel2 = getrel(getnatp(firing.sct_own), aown);
785 if ((firing.sct_own != own) &&
786 ((rel != ALLIED) || (rel2 != AT_WAR)))
788 /* Don't shoot yourself */
789 if (firing.sct_own == aown)
791 erange = fortrange(&firing);
792 if (roundrange(erange) < ns.curdist)
795 dam2 = fort_fire(&firing);
796 /* no putsect(&firing) because ammo is charged in use_ammo() */
800 fp = malloc(sizeof(struct flist));
801 memset(fp, 0, sizeof(struct flist));
802 fp->x = firing.sct_x;
803 fp->y = firing.sct_y;
804 fp->type = targ_land;
805 add_to_fired_queue(&fp->queue, list);
806 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
811 return *nfiring == 0 ? 0 : dam / *nfiring;
815 use_ammo(struct emp_qelem *list)
817 struct emp_qelem *qp, *next;
825 /* use 1 shell from everyone */
826 for (qp = list->q_forw; qp != list; qp = next) {
828 fp = (struct flist *)qp;
829 if (fp->type == targ_ship) {
830 getship(fp->uid, &ship);
831 item = ship.shp_item;
832 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
833 shell = item[I_SHELL];
834 shell -= SHP_TORP_SHELLS - 1;
837 item[I_SHELL] = shell;
838 putship(ship.shp_uid, &ship);
839 /* mob cost = 1/2 a sect's mob */
840 ship.shp_mobil -= shp_mobcost(&ship) / 2.0;
842 } else if (fp->type == targ_land) {
843 getsect(fp->x, fp->y, §);
844 item = sect.sct_item;
846 getland(fp->uid, &land);
847 item = land.lnd_item;
849 shell = item[I_SHELL];
853 item[I_SHELL] = shell;
854 if (fp->type == targ_ship)
855 putship(ship.shp_uid, &ship);
856 else if (fp->type == targ_land)
859 putland(land.lnd_uid, &land);
861 emp_remque(&fp->queue);
868 add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
870 struct emp_qelem *qp;
872 struct flist *ep = (struct flist *)elem;
874 /* Don't put them on the list if they're already there */
875 for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
876 fp = (struct flist *)qp;
877 if (fp->type == targ_land
878 ? fp->x == ep->x && fp->y == ep->y
879 : fp->uid == ep->uid) {
884 emp_insque(elem, list);