2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2014, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure, Markus Armbruster
6 * Empire 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 3 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, see <http://www.gnu.org/licenses/>.
21 * See files README, COPYING and CREDITS in the root of the source
22 * tree for related information and legal notices. It is expected
23 * that future projects/authors will amend these files as needed.
27 * multifire.c: Fire at other sectors/ships
29 * Known contributors to this file:
31 * Markus Armbruster, 2004-2014
44 targ_land, targ_ship, targ_sub, targ_bogus
48 struct emp_qelem queue; /* list of fired things */
49 short type; /* EF_SECTOR, EF_SHIP or EF_LAND */
52 int defdam; /* damage defenders did */
56 static int defend(struct emp_qelem *, struct emp_qelem *,
57 struct empobj *, natid, int *);
58 static void do_defdam(struct emp_qelem *, double);
59 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
61 static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
62 static void free_flist(struct emp_qelem *);
63 static struct flist *search_flist(struct emp_qelem *, struct empobj *);
68 static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
86 enum targ_type target;
87 struct nstr_item nbst;
93 union empobj_storage item;
94 struct emp_qelem fired, defended;
99 emp_initque(&defended);
100 p = getstarg(player->argp[1],
101 "Firing from ship(s), sect(s), or land unit(s)? ", buf);
104 type = ef_byname_from(p, ef_with_guns);
105 if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
106 pr("Fort firing is disabled.\n");
110 pr("Ships, land units or sectors only!\n");
113 if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
116 while (nxtitem(&nbst, &item)) {
117 if (type == EF_LAND) {
118 if (!getland(item.land.lnd_uid, &fland))
120 if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
122 if (item.land.lnd_own != player->cnum)
125 if (lchr[fland.lnd_type].l_dam == 0) {
126 pr("Unit %d cannot fire!\n", fland.lnd_uid);
129 if (fland.lnd_item[I_MILIT] < 1) {
130 pr("Unit %d cannot fire because it has no military!\n",
134 if (fland.lnd_ship >= 0) {
135 pr("Unit %d cannot fire because it is on a ship!\n",
139 if (fland.lnd_land >= 0) {
140 pr("Unit %d cannot fire because it is on a land unit!\n",
144 if (fland.lnd_effic < LAND_MINFIREEFF) {
145 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
146 fland.lnd_uid, LAND_MINFIREEFF);
149 if (fland.lnd_item[I_GUN] == 0) {
150 pr("%s -- not enough guns\n", prland(&fland));
154 if (fland.lnd_item[I_SHELL] == 0) {
155 pr("%s -- not enough shells\n", prland(&fland));
158 pr("%s%s ready to fire\n", sep, prland(&fland));
161 } else if (type == EF_SHIP) {
162 if (!getship(item.ship.shp_uid, &fship))
164 if (item.ship.shp_own != player->cnum)
166 if (item.ship.shp_item[I_MILIT] < 1) {
167 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
170 if (mchr[item.ship.shp_type].m_glim == 0
171 && !(mchr[fship.shp_type].m_flags & M_DCH)) {
172 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
175 if (item.ship.shp_item[I_GUN] == 0) {
176 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
179 if (item.ship.shp_item[I_SHELL] == 0) {
180 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
183 if (item.ship.shp_effic < 60) {
184 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
187 pr("%s%s ready to fire\n", sep, prship(&fship));
191 if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
193 if (item.sect.sct_own != player->cnum)
195 if (item.sect.sct_type != SCT_FORTR)
197 if (item.sect.sct_effic < FORTEFF) {
198 pr("Fort not efficient enough to fire!\n");
201 if (item.sect.sct_item[I_GUN] == 0) {
202 pr("Not enough guns in sector %s!\n",
203 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
206 if (item.sect.sct_item[I_SHELL] == 0) {
207 pr("Not enough shells in sector %s!\n",
208 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
211 if (item.sect.sct_item[I_MILIT] < 5) {
212 pr("Not enough military in sector %s!\n",
213 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
216 pr("%sSector %s ready to fire\n", sep,
217 xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
223 ptr = getstarg(player->argp[3], "Firing at? ", buf);
228 if (!issector(ptr)) {
230 if (vshipno < 0 || !getship(vshipno, &vship) ||
232 pr("No such ship exists!\n");
235 target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
236 targ_sub : targ_ship;
237 vict = vship.shp_own;
240 if (!getsect(x, y, &vsect)) {
241 pr("No such sector exists!\n");
245 if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
246 pr("No such sector exists!\n");
249 /* We check the sector type, but we only use it for damage, not
250 reporting. That way, you don't get extra information you wouldn't
251 normally get. Besides, what if they want to slam water? :) */
252 if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
256 vict = vsect.sct_own;
261 trange = mapdist(x, y, fx, fy);
263 if (type == EF_SHIP) {
264 if (!check_ship_ok(&fship))
266 if (target == targ_sub || target == targ_ship) {
267 if (fship.shp_uid == vship.shp_uid) {
268 pr("You can't fire upon yourself!\n");
272 range = shp_fire_range(&fship);
273 range2 = roundrange(range);
274 pr("range is %d.00 (%.2f)\n", range2, range);
275 if (target == targ_sub
277 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
278 dam = shp_dchrg(&fship);
280 if (target == targ_sub)
281 /* Don't tell it's a sub */
283 if (fship.shp_item[I_GUN] == 0) {
284 pr("Insufficient arms.\n");
287 dam = shp_fire(&fship);
289 putship(fship.shp_uid, &fship);
290 if (CANT_HAPPEN(dam < 0)) {
294 if (opt_NOMOBCOST == 0) {
295 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
296 putship(fship.shp_uid, &fship);
298 } else if (type == EF_LAND) {
299 if (!check_land_ok(&fland))
301 if (target == targ_land) {
302 if (fland.lnd_x == vsect.sct_x
303 && fland.lnd_y == vsect.sct_y) {
304 pr("You can't fire upon yourself!\n");
309 range = lnd_fire_range(&fland);
310 range2 = roundrange(range);
311 pr("range is %d.00 (%.2f)\n", range2, range);
312 if (target == targ_sub) {
313 /* Don't tell it's a sub */
317 dam = lnd_fire(&fland);
318 putland(fland.lnd_uid, &fland);
319 if (CANT_HAPPEN(dam < 0)) {
323 if (target == targ_ship) {
324 if (chance(lnd_acc(&fland) / 100.0))
325 dam = ldround(dam / 2.0, 1);
328 if (!check_sect_ok(&fsect))
330 if (target == targ_land) {
331 if (fsect.sct_x == vsect.sct_x
332 && fsect.sct_y == vsect.sct_y) {
333 pr("You can't fire upon yourself!\n");
337 dam = fort_fire(&fsect);
339 if (CANT_HAPPEN(dam < 0)) {
343 range = fortrange(&fsect);
344 range2 = roundrange(range);
345 pr("range is %d.00 (%.2f)\n", range2, range);
346 if (target == targ_sub) {
347 /* Don't tell it's a sub */
351 if (trange > range2) {
352 pr("Target out of range.\n");
358 fland.lnd_mission = 0;
359 putland(fland.lnd_uid, &fland);
362 fship.shp_mission = 0;
363 putship(fship.shp_uid, &fship);
385 nreport(player->cnum, N_SCT_SHELL, vict, 1);
386 if (vict && vict != player->cnum)
388 "Country #%d shelled sector %s for %d damage.\n",
389 player->cnum, xyas(x, y, vict), dam);
390 pr("Shells hit sector %s for %d damage.\n",
391 xyas(x, y, player->cnum), dam);
394 nreport(player->cnum, N_SHP_SHELL, vict, 1);
397 if (vict && vict != player->cnum) {
399 "Country #%d shelled %s in %s for %d damage.\n",
400 player->cnum, prship(&vship),
401 xyas(vship.shp_x, vship.shp_y, vict), dam);
403 pr("Shells hit %s in %s for %d damage.\n",
405 xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
408 /* Ok, now, check if we had a bogus target. If so,
409 just continue on, since there is no defender. */
410 if (target == targ_bogus)
413 if (type == EF_LAND) {
414 getsect(fland.lnd_x, fland.lnd_y, &fsect);
415 attgp = (struct empobj *)&fsect;
417 totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
420 getsect(x, y, &vsect);
421 sectdamage(&vsect, dam);
425 getship(vshipno, &vship);
426 check_retreat_and_do_shipdamage(&vship, dam);
427 if (vship.shp_effic < SHIP_MINEFF)
428 pr("%s sunk!\n", prsub(&vship));
429 else if (target == targ_sub
430 && (vship.shp_rflags & RET_DCHRGED)
431 && !(vship.shp_rflags & RET_INJURED))
432 retreat_ship(&vship, 'd');
433 else if (totaldefdam == 0
434 && (vship.shp_rflags & RET_HELPLESS)
435 && !(vship.shp_rflags & RET_INJURED))
436 retreat_ship(&vship, 'h');
437 putship(vship.shp_uid, &vship);
440 switch (attgp->ef_type) {
445 if ((target == targ_ship) || (target == targ_sub)) {
446 if (fship.shp_effic > SHIP_MINEFF) {
447 shp_missdef(&fship, vict);
450 putship(fship.shp_uid, &fship);
457 free_flist(&defended);
459 odds = ((double)ndefending) / ((double)nfiring);
462 do_defdam(&fired, odds);
467 defend(struct emp_qelem *al, struct emp_qelem *dl,
468 struct empobj *attgp, natid vict, int *nd)
473 dam = quiet_bigdef(attgp->ef_type, dl, vict,
474 attgp->own, attgp->x, attgp->y, &nfiring);
478 add_to_flist(al, attgp, dam, vict);
485 do_defdam(struct emp_qelem *list, double odds)
493 struct emp_qelem *qp, *next;
495 for (qp = list->q_forw; qp != list; qp = next) {
497 fp = (struct flist *)qp;
498 if (fp->type == EF_SHIP) {
499 if (!getship(fp->uid, &ship) || !ship.shp_own)
504 pr("\nDefenders fire back!\n");
507 dam = odds * fp->defdam;
509 if (fp->type == EF_SHIP) {
511 pr("Return fire hit %s in %s for %d damage.\n",
513 xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
516 "Return fire hit %s in %s for %d damage.\n",
517 prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
518 shipdamage(&ship, dam);
519 putship(ship.shp_uid, &ship);
521 CANT_HAPPEN(fp->type != EF_SECTOR);
522 getsect(fp->x, fp->y, §);
524 pr("Return fire hit sector %s for %d damage.\n",
525 xyas(fp->x, fp->y, player->cnum), dam);
526 sectdamage(§, dam);
529 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
530 xyas(fp->x, fp->y, vict), dam);
532 emp_remque(&fp->queue);
538 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
539 coord ax, coord ay, int *nfiring)
546 struct sctstr firing;
553 snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
554 while (nxtitem(&ni, &ship)) {
555 if (!feels_like_helping(ship.shp_own, own, aown))
558 if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
561 if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
562 erange = torprange(&ship);
563 if (roundrange(erange) < ni.curdist)
565 if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
567 fp = search_flist(list, (struct empobj *)&ship);
571 dam2 = shp_torp(&ship, 0);
572 putship(ship.shp_uid, &ship);
576 if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
579 erange = shp_fire_range(&ship);
580 if (roundrange(erange) < ni.curdist)
582 fp = search_flist(list, (struct empobj *)&ship);
586 dam2 = shp_fire(&ship);
587 putship(ship.shp_uid, &ship);
591 nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
595 add_to_flist(list, (struct empobj *)&ship, dam2, 0);
598 snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
599 while (nxtitem(&ni, &land)) {
600 if (!feels_like_helping(land.lnd_own, own, aown))
603 erange = lnd_fire_range(&land);
604 if (roundrange(erange) < ni.curdist)
607 fp = search_flist(list, (struct empobj *)&land);
611 dam2 = lnd_fire(&land);
612 putland(land.lnd_uid, &land);
619 add_to_flist(list, (struct empobj *)&land, dam2, 0);
620 nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
621 if (type == EF_SHIP) {
622 if (chance(lnd_acc(&land) / 100.0))
623 dam2 = ldround(dam2 / 2.0, 1);
629 * Determine if any nearby gun-equipped sectors are within
630 * range and able to fire at an attacker. Firing sectors
631 * need to have guns, shells, and military. Sector being
632 * attacked is x,y -- attacker is at ax,ay.
635 if (!opt_NO_FORT_FIRE) {
636 snxtsct_dist(&ns, ax, ay, 8);
637 while (nxtsct(&ns, &firing)) {
638 if (!feels_like_helping(firing.sct_own, own, aown))
641 erange = fortrange(&firing);
642 if (roundrange(erange) < ns.curdist)
645 fp = search_flist(list, (struct empobj *)&firing);
649 dam2 = fort_fire(&firing);
656 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
657 nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
662 return *nfiring == 0 ? 0 : dam / *nfiring;
666 add_to_flist(struct emp_qelem *list,
667 struct empobj *gp, int dam, natid victim)
671 fp = malloc(sizeof(struct flist));
672 fp->type = gp->ef_type;
678 emp_insque(&fp->queue, list);
682 free_flist(struct emp_qelem *list)
684 struct emp_qelem *qp, *next;
687 for (qp = list->q_forw; qp != list; qp = next) {
689 fp = (struct flist *)qp;
690 emp_remque(&fp->queue);
696 uid_eq(struct emp_qelem *elem, void *key)
698 return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
701 static struct flist *
702 search_flist(struct emp_qelem *list, struct empobj *gp)
704 return (struct flist *)emp_searchque(list, gp, uid_eq);