1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2021, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
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.
10  *
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.
15  *
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/>.
18  *
19  *  ---
20  *
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.
24  *
25  *  ---
26  *
27  *  detonate.c: Detonate a nuclear device in a sector.
28  *
29  *  Known contributors to this file:
30  *     Steve McClure, 1998-2000
31  *     Markus Armbruster, 2004-2012
32  */
33 
34 #include <config.h>
35 
36 #include "chance.h"
37 #include "land.h"
38 #include "map.h"
39 #include "misc.h"
40 #include "nat.h"
41 #include "news.h"
42 #include "nsc.h"
43 #include "nuke.h"
44 #include "optlist.h"
45 #include "plane.h"
46 #include "player.h"
47 #include "prototypes.h"
48 #include "sect.h"
49 #include "ship.h"
50 #include "xy.h"
51 
52 static void kaboom(int x, int y, int rad);
53 
54 int
detonate(struct nukstr * np,coord x,coord y,int airburst)55 detonate(struct nukstr *np, coord x, coord y, int airburst)
56 {
57     int nuketype = np->nuk_type;
58     struct nchrstr *ncp;
59     struct plnstr plane;
60     struct sctstr sect;
61     struct shpstr ship;
62     struct lndstr land;
63     struct nukstr nuke;
64     natid own;
65     int type;
66     int damage;
67     int fallout;
68     int rad;
69     struct nstr_sect ns;
70     struct nstr_item ni;
71     int changed = 0;
72 
73     pr("Releasing RVs for %s detonation...\n",
74        airburst ? "airburst" : "groundburst");
75 
76     getsect(x, y, &sect);
77     ncp = &nchr[nuketype];
78     kaboom(x, y, ncp->n_blast);
79     rad = ncp->n_blast;
80     if (!airburst)
81 	rad = rad * 2 / 3;
82     if (sect.sct_type == SCT_WATER)
83 	rad = 0;     /* Nukes falling on water affect only 1 sector */
84     np->nuk_effic = 0;
85     putnuke(np->nuk_uid, np);
86 
87     snxtsct_dist(&ns, x, y, rad);
88     while (nxtsct(&ns, &sect)) {
89 	own = sect.sct_own;
90 	type = sect.sct_type;
91 	if ((damage = nukedamage(ncp, ns.curdist, airburst)) <= 0)
92 	    continue;
93 	if (type == SCT_SANCT) {
94 	    pr("bounced off %s\n", xyas(ns.x, ns.y, player->cnum));
95 	    mpr(own, "%s nuclear device bounced off %s\n",
96 		cname(player->cnum), xyas(ns.x, ns.y, own));
97 	    nreport(player->cnum, N_NUKE, own, 1);
98 	    continue;
99 	}
100 	sect_damage(&sect, damage);
101 	if (opt_FALLOUT) {
102 	    fallout = sect.sct_fallout;
103 	    if (ncp->n_flags & N_NEUT)
104 		fallout += damage * 30;
105 	    else
106 		fallout += damage * 3;
107 	    sect.sct_fallout = MIN(fallout, FALLOUT_MAX);
108 	}
109 	if (damage > 100) {
110 	    sect.sct_oldown = 0;
111 	    sect.sct_own = 0;
112 	    if (type == SCT_WATER || type == SCT_BSPAN ||
113 		type == SCT_BTOWER) {
114 		if (type != SCT_WATER) {
115 		    pr("left nothing but water in %s\n",
116 		       xyas(ns.x, ns.y, player->cnum));
117 		    if (own != player->cnum)
118 			mpr(own,
119 			    "%s nuclear device left nothing but water in %s\n",
120 			    cname(player->cnum), xyas(ns.x, ns.y, own));
121 		    sect.sct_newtype = SCT_WATER;
122 		    sect.sct_type = SCT_WATER;
123 		}
124 	    } else {
125 		sect.sct_newtype = SCT_WASTE;
126 		sect.sct_type = SCT_WASTE;
127 		pr("turned %s into a radioactive wasteland\n",
128 		   xyas(ns.x, ns.y, player->cnum));
129 		if (own != player->cnum)
130 		    mpr(own,
131 			"%s nuclear device turned %s into a radioactive wasteland\n",
132 			cname(player->cnum), xyas(ns.x, ns.y, own));
133 	    }
134 	    changed |= map_set(player->cnum, sect.sct_x, sect.sct_y,
135 			       dchr[sect.sct_type].d_mnem, 0);
136 	} else {
137 	    pr("did %d%% damage in %s\n",
138 	       damage, xyas(ns.x, ns.y, player->cnum));
139 	    if (own != player->cnum)
140 		mpr(own, "%s nuclear device did %d%% damage in %s\n",
141 		    cname(player->cnum), damage, xyas(ns.x, ns.y, own));
142 	}
143 	(void)putsect(&sect);
144 	if (type != SCT_WATER)
145 	    nreport(player->cnum, N_NUKE, own, 1);
146     }
147 
148     if (changed)
149 	writebmap(player->cnum);
150 
151     snxtitem_dist(&ni, EF_PLANE, x, y, rad);
152     while (nxtitem(&ni, &plane)) {
153 	if ((own = plane.pln_own) == 0)
154 	    continue;
155 	if (plane.pln_flags & PLN_LAUNCHED)
156 	    continue;
157 	damage = nukedamage(ncp, ni.curdist, airburst) - plane.pln_harden;
158 	if (damage <= 0)
159 	    continue;
160 	if (plane.pln_ship >= 0) {
161 	    /* Are we on a sub? */
162 	    getship(plane.pln_ship, &ship);
163 
164 	    if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
165 		struct sctstr sect1;
166 
167 		/* Should we damage this sub? */
168 		getsect(ship.shp_x, ship.shp_y, &sect1);
169 
170 		if (sect1.sct_type == SCT_BSPAN ||
171 		    sect1.sct_type == SCT_BTOWER ||
172 		    sect1.sct_type == SCT_WATER) {
173 		    /* Ok, we're not in a harbor or trapped
174 		       inland.  Now, did we get pasted
175 		       directly? */
176 		    if (ship.shp_x != x || ship.shp_y != y) {
177 			/* Nope, so don't mess with it */
178 			continue;
179 		    }
180 		}
181 	    }
182 	}
183 	planedamage(&plane, damage);
184 	if (own == player->cnum) {
185 	    pr("%s at %s reports %d%% damage\n",
186 	       prplane(&plane),
187 	       xyas(plane.pln_x, plane.pln_y, player->cnum), damage);
188 	} else {
189 	    mpr(own, "%s nuclear device did %d%% damage to %s at %s\n",
190 		cname(player->cnum), damage,
191 		prplane(&plane), xyas(plane.pln_x, plane.pln_y, own));
192 	}
193 	putplane(ni.cur, &plane);
194     }
195 
196     snxtitem_dist(&ni, EF_LAND, x, y, rad);
197     while (nxtitem(&ni, &land)) {
198 	if ((own = land.lnd_own) == 0)
199 	    continue;
200 	if ((damage = nukedamage(ncp, ni.curdist, airburst)) <= 0)
201 	    continue;
202 
203 	if (land.lnd_ship >= 0) {
204 	    /* Are we on a sub? */
205 	    getship(land.lnd_ship, &ship);
206 
207 	    if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
208 		struct sctstr sect1;
209 
210 		/* Should we damage this sub? */
211 		getsect(ship.shp_x, ship.shp_y, &sect1);
212 
213 		if (sect1.sct_type == SCT_BSPAN ||
214 		    sect1.sct_type == SCT_BTOWER ||
215 		    sect1.sct_type == SCT_WATER) {
216 		    /* Ok, we're not in a harbor or trapped
217 		       inland.  Now, did we get pasted
218 		       directly? */
219 		    if (ship.shp_x != x || ship.shp_y != y) {
220 			/* Nope, so don't mess with it */
221 			continue;
222 		    }
223 		}
224 	    }
225 	}
226 	land_damage(&land, damage);
227 	if (own == player->cnum) {
228 	    pr("%s at %s reports %d%% damage\n",
229 	       prland(&land), xyas(land.lnd_x, land.lnd_y, player->cnum),
230 	       damage);
231 	} else {
232 	    mpr(own, "%s nuclear device did %d%% damage to %s at %s\n",
233 		cname(player->cnum), damage,
234 		prland(&land), xyas(land.lnd_x, land.lnd_y, own));
235 	}
236 	putland(land.lnd_uid, &land);
237     }
238 
239     snxtitem_dist(&ni, EF_SHIP, x, y, rad);
240     while (nxtitem(&ni, &ship)) {
241 	if ((own = ship.shp_own) == 0)
242 	    continue;
243 	if ((damage = nukedamage(ncp, ni.curdist, airburst)) <= 0)
244 	    continue;
245 	if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
246 	    struct sctstr sect1;
247 
248 	    /* Should we damage this sub? */
249 	    getsect(ship.shp_x, ship.shp_y, &sect1);
250 
251 	    if (sect1.sct_type == SCT_BSPAN ||
252 		sect1.sct_type == SCT_BTOWER ||
253 		sect1.sct_type == SCT_WATER) {
254 		/* Ok, we're not in a harbor or trapped
255 		   inland.  Now, did we get pasted
256 		   directly? */
257 		if (ship.shp_x != x || ship.shp_y != y) {
258 		    /* Nope, so don't mess with it */
259 		    continue;
260 		}
261 	    }
262 	}
263 	ship_damage(&ship, damage);
264 	if (own == player->cnum) {
265 	    pr("%s at %s reports %d%% damage\n",
266 	       prship(&ship), xyas(ship.shp_x, ship.shp_y, player->cnum),
267 	       damage);
268 	} else {
269 	    mpr(own, "%s nuclear device did %d%% damage to %s at %s\n",
270 		cname(player->cnum), damage, prship(&ship),
271 		xyas(ship.shp_x, ship.shp_y, own));
272 	}
273 	putship(ship.shp_uid, &ship);
274     }
275 
276     snxtitem_dist(&ni, EF_NUKE, x, y, rad);
277     while (nxtitem(&ni, &nuke)) {
278 	if ((own = nuke.nuk_own) == 0)
279 	    continue;
280 	if ((damage = nukedamage(ncp, ni.curdist, airburst)) <= 0)
281 	    continue;
282 	if (!pct_chance(damage))
283 	    continue;
284 	nuke.nuk_effic = 0;
285 	if (own == player->cnum) {
286 	    pr("%s at %s destroyed\n",
287 	       prnuke(&nuke), xyas(nuke.nuk_x, nuke.nuk_y, player->cnum));
288 	} else {
289 	    mpr(own, "%s nuclear device destroyed %s at %s\n",
290 		cname(player->cnum), prnuke(&nuke),
291 		      xyas(nuke.nuk_x, nuke.nuk_y, own));
292 	}
293 	putnuke(ni.cur, &nuke);
294     }
295 
296     return nukedamage(ncp, 0, airburst);
297 }
298 
299 
300 /*
301  * silly to be sure.
302  */
303 static void
kaboom(int x,int y,int rad)304 kaboom(int x, int y, int rad)
305 {
306     pr("\n\nK A B O O ");
307     while (rad-- > 1)
308 	pr("O O ");
309     pr("M ! in %s\n\n", xyas(x, y, player->cnum));
310 }
311