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 };
86 enum targ_type target;
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 (opt_NO_FORT_FIRE && type == EF_SECTOR) {
108 pr("Fort firing is disabled.\n");
112 pr("Ships, land units or sectors only!\n");
115 if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
119 if (!snxtitem(&nbst, type, ptr))
122 if (player->aborted) {
123 pr("Fire aborted.\n");
126 while (nxtitem(&nbst, &item)) {
127 if (type == EF_LAND) {
128 if (!getland(item.land.lnd_uid, &fland))
130 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
132 if (item.land.lnd_own != player->cnum)
135 if (lchr[fland.lnd_type].l_dam == 0) {
136 pr("Unit %d cannot fire!\n", fland.lnd_uid);
139 if (fland.lnd_item[I_MILIT] < 1) {
140 pr("Unit %d cannot fire because it has no military!\n",
144 if (fland.lnd_ship >= 0) {
145 pr("Unit %d cannot fire because it is on a ship!\n",
149 if (fland.lnd_land >= 0) {
150 pr("Unit %d cannot fire because it is on a land unit!\n",
154 if (fland.lnd_effic < LAND_MINFIREEFF) {
155 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
156 fland.lnd_uid, LAND_MINFIREEFF);
159 if (fland.lnd_item[I_SHELL] == 0) {
160 pr("%s -- not enough shells\n", prland(&fland));
163 } else if (type == EF_SHIP) {
164 if (!getship(item.ship.shp_uid, &fship))
166 if (item.ship.shp_own != player->cnum)
168 if (item.ship.shp_item[I_MILIT] < 1) {
169 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
172 if (mchr[item.ship.shp_type].m_glim == 0) {
173 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
176 if (item.ship.shp_item[I_GUN] == 0) {
177 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
180 if (item.ship.shp_item[I_SHELL] == 0) {
181 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
184 if (item.ship.shp_effic < 60) {
185 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
188 } else if (type == EF_SECTOR) {
189 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
191 if (item.sect.sct_own != player->cnum)
193 if (item.sect.sct_type != SCT_FORTR)
195 if (item.sect.sct_effic < FORTEFF) {
196 pr("Fort not efficient enough to fire!\n");
199 if (item.sect.sct_item[I_GUN] == 0) {
200 pr("Not enough guns in sector %s!\n",
201 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
204 if (item.sect.sct_item[I_SHELL] == 0) {
205 pr("Not enough shells in sector %s!\n",
206 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
209 if (item.sect.sct_item[I_MILIT] < 5) {
210 pr("Not enough military in sector %s!\n",
211 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
214 pr("\nSector %s firing\n",
215 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
217 if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
220 if (player->aborted) {
221 pr("Fire aborted.\n");
225 (void)strcpy(vbuf, ptr);
230 if (target == targ_ship) {
231 vshipno = atoi(vbuf);
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(vbuf, &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;
262 if (type == EF_SHIP) {
263 if (fship.shp_own != player->cnum) {
264 pr("Not your ship!\n");
267 if (target == targ_sub || target == targ_ship) {
268 if (fship.shp_uid == vship.shp_uid) {
269 pr("You can't fire upon yourself!\n");
275 if ((mil = fship.shp_item[I_MILIT]) < 1) {
276 pr("Not enough military for firing crew.\n");
279 if (fship.shp_effic < 60) {
280 pr("Ship #%d is crippled (%d%%)\n",
281 fship.shp_uid, fship.shp_effic);
284 range = shp_fire_range(&fship);
285 range2 = roundrange(range);
286 pr("range is %d.00 (%.2f)\n", range2, range);
287 if (target == targ_sub
288 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
289 dam = shp_dchrg(&fship);
290 putship(fship.shp_uid, &fship);
292 pr("Not enough shells for depth charge!\n");
296 if (target == targ_sub)
297 /* Don't tell it's a sub */
299 if (fship.shp_item[I_GUN] == 0) {
300 pr("Insufficient arms.\n");
303 dam = shp_fire(&fship);
304 putship(fship.shp_uid, &fship);
310 if (opt_NOMOBCOST == 0) {
311 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
312 putship(fship.shp_uid, &fship);
314 } else if (type == EF_LAND) {
315 if (fland.lnd_own != player->cnum) {
316 pr("Not your unit!\n");
320 if (target == targ_land) {
321 if (fland.lnd_x == vsect.sct_x
322 && fland.lnd_y == vsect.sct_y) {
323 pr("You can't fire upon yourself!\n");
331 if (lchr[fland.lnd_type].l_dam == 0) {
332 pr("Unit %d cannot fire!\n", fland.lnd_uid);
335 if (fland.lnd_item[I_GUN] == 0) {
336 pr("%s -- not enough guns\n", prland(&fland));
340 range = lnd_fire_range(&fland);
341 range2 = roundrange(range);
342 pr("range is %d.00 (%.2f)\n", range2, range);
343 if (target == targ_sub) {
344 /* Don't tell it's a sub */
348 dam = lnd_fire(&fland);
349 putland(fland.lnd_uid, &fland);
354 if (target == targ_ship) {
355 if (chance(lnd_acc(&fland) / 100.0))
356 dam = ldround(dam / 2.0, 1);
361 if (fsect.sct_own != player->cnum ||
362 fsect.sct_type != SCT_FORTR) {
363 pr("No fortress at %s\n",
364 xyas(fsect.sct_x, fsect.sct_y, player->cnum));
367 if (target == targ_land) {
368 if (fsect.sct_x == vsect.sct_x
369 && fsect.sct_y == vsect.sct_y) {
370 pr("You can't fire upon yourself!\n");
374 if (fsect.sct_item[I_GUN] == 0) {
375 pr("Insufficient arms.\n");
378 if (fsect.sct_item[I_MILIT] < 5) {
379 pr("Not enough military for firing crew.\n");
382 dam = fort_fire(&fsect);
388 range = fortrange(&fsect);
389 range2 = roundrange(range);
390 pr("range is %d.00 (%.2f)\n", range2, range);
391 if (target == targ_sub) {
392 /* Don't tell it's a sub */
396 trange = mapdist(x, y, fx, fy);
397 if (trange > range2) {
398 pr("Target out of range.\n");
404 fland.lnd_mission = 0;
405 putland(fland.lnd_uid, &fland);
408 fship.shp_mission = 0;
409 putship(fship.shp_uid, &fship);
418 if (!trechk(player->cnum, vict, SEAFIR))
422 if (!trechk(player->cnum, vict, SUBFIR))
426 if (!trechk(player->cnum, vict, LANFIR))
434 if (target == targ_land) {
435 natp = getnatp(player->cnum);
436 rel = getrel(natp, vict);
437 if ((rel != AT_WAR) && (player->cnum != vict) &&
438 (vict) && (vsect.sct_oldown != player->cnum)) {
439 pr("You're not at war with them!\n");
449 if (vship.shp_rflags & RET_DCHRGED)
450 retreat_ship(&vship, 'd');
455 prb = range2 ? (double)trange / range2 : 1.0;
458 pr("Wind deflects shells.\n");
459 /* dam = (int)(dam / 2.0);*/
460 dam *= (90 - (random() % 11)) / 100.0;
469 nreport(player->cnum, N_SCT_SHELL, vict, 1);
470 if (vict && vict != player->cnum)
472 "Country #%d shelled sector %s for %d damage.\n",
473 player->cnum, xyas(x, y, vict), dam);
474 pr("Shells hit sector %s for %d damage.\n",
475 xyas(x, y, player->cnum), dam);
476 if (target != targ_bogus)
477 sectdamage(&vsect, dam, 0);
480 nreport(player->cnum, N_SHP_SHELL, vict, 1);
483 if ((target != targ_sub) ||
484 ((vship.shp_rflags & RET_DCHRGED) == 0))
485 check_retreat_and_do_shipdamage(&vship, dam);
487 shipdamage(&vship, dam);
490 "Country #%d shelled %s in %s for %d damage.\n",
491 player->cnum, prship(&vship),
492 xyas(vship.shp_x, vship.shp_y, vict), dam);
494 pr("Shells hit %s in %s for %d damage.\n",
496 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
498 if (vship.shp_effic < SHIP_MINEFF)
499 pr("%s sunk!\n", prsub(&vship));
503 /* Ok, now, check if we had a bogus target. If so,
504 just continue on, since there is no defender. */
505 if (target == targ_bogus)
508 if (type == EF_LAND) {
509 getsect(fland.lnd_x, fland.lnd_y, &fsect);
510 attgp = (struct empobj *)&fsect;
512 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
518 putship(vship.shp_uid, &vship);
521 if ((totaldefdam == 0) && (target == targ_ship))
522 if (vship.shp_rflags & RET_HELPLESS)
523 retreat_ship(&vship, 'h');
524 switch (attgp->ef_type) {
529 if ((target == targ_ship) || (target == targ_sub)) {
530 if (fship.shp_effic > SHIP_MINEFF) {
531 shp_missdef(&fship, vict);
534 putship(fship.shp_uid, &fship);
541 free_flist(&defended);
543 odds = ((double)ndefending) / ((double)nfiring);
546 do_defdam(&fired, odds);
551 defend(struct emp_qelem *al, struct emp_qelem *dl,
552 struct empobj *attgp, natid vict, int *nd)
557 dam = quiet_bigdef(attgp->ef_type, dl, vict,
558 attgp->own, attgp->x, attgp->y, &nfiring);
562 add_to_flist(al, attgp, dam, vict);
569 do_defdam(struct emp_qelem *list, double odds)
577 struct emp_qelem *qp, *next;
579 for (qp = list->q_forw; qp != list; qp = next) {
581 fp = (struct flist *)qp;
582 if (fp->type == EF_SHIP) {
583 if (!getship(fp->uid, &ship) || !ship.shp_own)
588 pr("\nDefenders fire back!\n");
591 dam = odds * fp->defdam;
593 if (fp->type == EF_SHIP) {
595 pr("Return fire hit %s in %s for %d damage.\n",
597 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
600 "Return fire hit %s in %s for %d damage.\n",
601 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
602 shipdamage(&ship, dam);
603 putship(ship.shp_uid, &ship);
605 CANT_HAPPEN(fp->type != EF_SECTOR);
606 getsect(fp->x, fp->y, §);
608 pr("Return fire hit sector %s for %d damage.\n",
609 xyas(fp->x, fp->y, player->cnum), dam);
610 sectdamage(§, dam, 0);
613 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
614 xyas(fp->x, fp->y, vict), dam);
616 emp_remque(&fp->queue);
622 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
623 coord ax, coord ay, int *nfiring)
629 int dam, dam2, rel, rel2;
630 struct sctstr firing;
637 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
638 while (nxtitem(&ni, &ship)) {
639 if (ship.shp_own == 0)
642 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
645 rel = getrel(getnatp(ship.shp_own), own);
646 rel2 = getrel(getnatp(ship.shp_own), aown);
647 if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
649 /* Don't shoot yourself */
650 if (ship.shp_own == aown)
652 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
653 erange = torprange(&ship);
654 if (roundrange(erange) < ni.curdist)
656 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
658 fp = search_flist(list, (struct empobj *)&ship);
662 dam2 = shp_torp(&ship, 0);
663 putship(ship.shp_uid, &ship);
667 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
670 erange = shp_fire_range(&ship);
671 if (roundrange(erange) < ni.curdist)
673 fp = search_flist(list, (struct empobj *)&ship);
677 dam2 = shp_fire(&ship);
678 putship(ship.shp_uid, &ship);
682 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
686 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
689 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
690 while (nxtitem(&ni, &land)) {
691 if (land.lnd_own == 0)
693 /* Don't shoot yourself */
694 if (land.lnd_own == aown)
697 rel = getrel(getnatp(land.lnd_own), own);
698 rel2 = getrel(getnatp(land.lnd_own), aown);
700 if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
703 erange = lnd_fire_range(&land);
704 if (roundrange(erange) < ni.curdist)
707 fp = search_flist(list, (struct empobj *)&land);
711 dam2 = lnd_fire(&land);
712 putland(land.lnd_uid, &land);
719 add_to_flist(list, (struct empobj *)&land, dam2, 0);
720 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
725 * Determine if any nearby gun-equipped sectors are within
726 * range and able to fire at an attacker. Firing sectors
727 * need to have guns, shells, and military. Sector being
728 * attacked is x,y -- attacker is at ax,ay.
731 if (!opt_NO_FORT_FIRE) {
732 snxtsct_dist(&ns, ax, ay, 8);
733 while (nxtsct(&ns, &firing)) {
734 if (firing.sct_own == 0)
736 rel = getrel(getnatp(firing.sct_own), own);
737 rel2 = getrel(getnatp(firing.sct_own), aown);
739 if ((firing.sct_own != own) &&
740 ((rel != ALLIED) || (rel2 != AT_WAR)))
742 /* Don't shoot yourself */
743 if (firing.sct_own == aown)
745 erange = fortrange(&firing);
746 if (roundrange(erange) < ns.curdist)
749 fp = search_flist(list, (struct empobj *)&firing);
753 dam2 = fort_fire(&firing);
760 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
761 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
766 return *nfiring == 0 ? 0 : dam / *nfiring;
770 add_to_flist(struct emp_qelem *list,
771 struct empobj *gp, int dam, natid victim)
775 fp = malloc(sizeof(struct flist));
776 fp->type = gp->ef_type;
782 emp_insque(&fp->queue, list);
786 free_flist(struct emp_qelem *list)
788 struct emp_qelem *qp, *next;
791 for (qp = list->q_forw; qp != list; qp = next) {
793 fp = (struct flist *)qp;
794 emp_remque(&fp->queue);
800 uid_eq(struct emp_qelem *elem, void *key)
802 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
805 static struct flist *
806 search_flist(struct emp_qelem *list, struct empobj *gp)
808 return (struct flist *)emp_searchque(list, gp, uid_eq);