So I'm a bit curious, as I've been wanting to implement something like this into one of my mods, but I can't seem to find any info on it. Is it at all possible to eliminate fallout as a byproduct from nuclear blasts?
For added context, I'm planning to create a unique unit for a custom civ which serves as an alternative to the nuclear missile. It will require no uranium resources to build, but it will cost a pretty penny to manufacture as a result. Since it is supposed to not use any fissile or otherwise radioactive materials for construction or detonation, I don't think it would make much sense for this weapon to trigger a blanket of fallout either. I haven't found any guides or tutorials which talk about this, so I was wondering if anyone knew a specific line of code I could inject into the mod files to prevent fallout from appearing if this specific weapon is used?
The guided missile is probably something you are looking for (just increase its range). Furthermore, I suspect that the fallout is related to <NukeDamageLevel> (and ensures the bans in the World congress possibly too).
The guided missile is probably something you are looking for (just increase its range). Furthermore, I suspect that the fallout is related to <NukeDamageLevel> (and ensures the bans in the World congress possibly too).
As far as I could tell, NukeDamageLevel is basically how many tiles away from the center impact tile are affected by the blast (1 = 1 tile radius, 2 = 2 tile radius, and so on). If I set the NukeDamageLevel to 0, you might be right in that there wouldn't be any fallout, but adjacent units and cities might not get damaged at all either unless they're on the center impact tile.
Also, I'd rather not use the guided missile as the class base for this unique unit, because then there wouldn't be any icbm animation or mushroom cloud or anything like that. Something tells me it would be easier to somehow tell the game "Don't place fallout when this nuke explodes, k?" rather than telling it "here's a special guided missile that is pretending to be a nuke, have it fly over the map like an icbm, it needs a mushroom cloud on impact, have it do similar damage to a nuke, etc"
As far as I could tell, NukeDamageLevel is basically how many tiles away from the center impact tile are affected by the blast (1 = 1 tile radius, 2 = 2 tile radius, and so on). If I set the NukeDamageLevel to 0, you might be right in that there wouldn't be any fallout, but adjacent units and cities might not get damaged at all either unless they're on the center impact tile.
Also, I'd rather not use the guided missile as the class base for this unique unit, because then there wouldn't be any icbm animation or mushroom cloud or anything like that. Something tells me it would be easier to somehow tell the game "Don't place fallout when this nuke explodes, k?" rather than telling it "here's a special guided missile that is pretending to be a nuke, have it fly over the map like an icbm, it needs a mushroom cloud on impact, have it do similar damage to a nuke, etc"
Then you're probably out of luck, as there do not seem to be any columns for something like FalloutRadius. You could however detect the nuclear attack using Lua, and then remove the fallout afterwards (though unfortunately the spawned fallout gets rid of features such as Forests, Jungles, Flood Plains, etc. so you'd lose those).
Adding the mushroom cloud to a Unit is not as difficult as you may think however; it's part of the ART_DEF of the unit. I once gave the swordsman a mushroom cloud animation whenever it attacked just to see what would happen. Needless to say it was quite funny to see.
Then you're probably out of luck, as there do not seem to be any columns for something like FalloutRadius. You could however detect the nuclear attack using Lua, and then remove the fallout afterwards (though unfortunately the spawned fallout gets rid of features such as Forests, Jungles, Flood Plains, etc. so you'd lose those).
Adding the mushroom cloud to a Unit is not as difficult as you may think however; it's part of the ART_DEF of the unit. I once gave the swordsman a mushroom cloud animation whenever it attacked just to see what would happen. Needless to say it was quite funny to see.
Hmm, the lua option has me intrigued. I honestly wouldn't mind the destruction of forests/jungles/flood plains/etc that much considering that a sufficiently large explosion in real life would indeed evaporate water and disintegrate trees on impact. So far that sounds like my best bet, but this ArtDefine also seems like a good secondary option. Do you happen to know what I should put in the lua to eliminate fallout the moment it's spawned from my particular unit? For reference, the unit's file name is UNIT_THERMOBARIC_MISSILE .
The following is a hook that fires whenever a nuclear missile/atomic bomb explodes, though unfortunately there doesn't seem to be a unitID passed in there.
You could try to hook into UnitPrekill as well to determine whether your unit died as that does pass the unitID. See which of the two fires first, and have that push data to a variable from which the other can read stuff.
The following loops over adjacent plots (assuming that your missile has a 1-tile radius. If not you'd have to use whoward's plotiterators)
Code:
--pPlot is defined somewhere above
for i = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
local pPlotAdj = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), i)
--the plot should not be off the map
if pPlotAdj then
--other code here
end
end
The following removes fallout from a plot if there is any on that plot (leaves forests, etc. intact)
Code:
local iFallout = GameInfoTypes.FEATURE_FALLOUT
--snip
--pPlot is defined somewhere above
if pPlot:GetFeatureType() == iFallout then
pPlot:SetFeatureType(-1)
end
Well, since there doesn't seem to be a UnitID included in there, this code might not be very useful to me, unless there's a way to link it to the UnitID some other way. I want to make sure the fallout is removed only if it's my unique unit that detonates, while regular nukes would still disperse fallout normally.
If there's absolutely no way to do that, I guess that means I have to do this the hard way and use a guided missile as my base while making the unique unit look like a nuke, which means I have to:
- Give it the ICBM flying animation
- Have it generate a mushroom cloud on impact (partially solved thanks to that previous line of code for ArtDefines you posted)
- Have it damage units in a certain tile radius, and not just units on the specific tile of impact
- Otherwise fully replace the Nuclear Missile with this new unit to the point that, on the surface, you wouldn't be able to tell the difference besides the lack of fallout
Well, since there doesn't seem to be a UnitID included in there, this code might not be very useful to me, unless there's a way to link it to the UnitID some other way.
You could try to hook into UnitPrekill as well to determine whether your unit died as that does pass the unitID. See which of the two fires first, and have that push data to a variable from which the other can read stuff.
I've gone ahead and tried to work that out and got the following result:
Spoiler:
Code:
local iAtomicBomb = GameInfoTypes.UNIT_ATOMIC_BOMB;
local bAnyBombJustExploded = false;
local iLastX = -1;
local iLastY = -1;
function MissilePrekill(iPlayer, iUnit, _, iX, iY, bDelay, iByPlayer)
print("MissilePrekill: "..tostring(iPlayer).." "..tostring(iUnit).." "..tostring(iX).." "..tostring(iY).." "..tostring(bDelay).." "..tostring(iByPlayer));
if bDelay then
local pPlayer = Players[iPlayer]
local pUnit = pPlayer:GetUnitByID(iUnit);
if pUnit:NukeDamageLevel() > 0 and iByPlayer == -1 then
if pUnit:GetUnitType() == iAtomicBomb then
if bAnyBombJustExploded then
--do UU stuff here
print("UU STUFF NOW WHOA");
end
end
bAnyBombJustExploded = false;
end
end
end
GameEvents.UnitPrekill.Add(MissilePrekill)
function MissileFired(iPlayer, iX, iY, bWar, bBystander)
print("MissileFired: "..tostring(iPlayer).." "..tostring(iX).." "..tostring(iY).." "..tostring(bWar).." "..tostring(bBystander));
bAnyBombJustExploded = true;
iLastX = iX;
iLastY = iY;
end
GameEvents.NuclearDetonation.Add(MissileFired);
The following seems to hold in all of the tests I conducted: NuclearDetonation fires before UnitPrekill (and for each unit UnitPrekill with bDelay = false fires before UnitPrekill with bDelay = true)
1. UnitPrekill always fires whenever NuclearDetonation fires (kinda logical but is very important for how this functions!)
2. If a unit dies due to a Nuclear blast and is not the unit that performed that nuclear blast, then UnitPrekill only fires with bDelay = false.
3. If a unit dies due to a Nuclear blast and is not the unit that performed that nuclear blast, then iByPlayer is the ID of the player that performed the blast.
4. If a unit performs a nuclear blast then iByPlayer will be -1 (as no other unit is directly responsible for that unit being killed)
5. UnitPrekill fires first for units that die in a nuclear blast, and only after that does UnitPrekill fire for the bomb itself
6. This works in Strategic View (I did not try Quick Combat/Quick Movement, but I'm very optimistic about that since it also works in SV)
An example from the Lua console where I fired an atomic bomb adjacent to my own city which stationed more atomic bombs:
The code utilizes all of these points in order to work.
If UnitPrekill fires but no bomb has been fired, then bAnyBombJustExploded will be false so the code doesn't fire.
If an atomic bomb kills another atomic bomb then the bDelay = true check and the iByPlayer-check will ensure that only the unit performing the blast is considered. (The latter check is kind of redundant though, but it's just a sanity check and doesn't cause issues)
tldr; After a nuclear blast has been performed the code will 'wait' for the UnitPrekill event of the unit that performed that blast. It then checks the unitType and does the UU stuff that's required.
NOTE: This will not work if for some reason two missiles fire simultaneously, causing NuclearDetonation to fire twice before UnitPrekill fires for the first blasting unit. I'm almost certain though that such a scenario cannot take place, as IIRC the game engine will wait with firing the second missile until the first one completed its destruction even if you're using code to fire them.
NOTE: iX and iY in UnitPrekill return the plot from which the missile was fired, not on which it landed! Don't be tempted to use those variables!
So if I'm reading this correctly, this code makes it so that an atomic bomb with the specific UU signature will have the unique unit affect trigger, specially the elimination of fallout once it's been spawned, right? This is absolutely wonderful, and thank you for going through the time and effort to code this, LUA is something that I am generally unfamiliar with so it would have taken me a very long while to learn and be able to compile something like this myself.
I assume that I can just replace all instances of "AtomicBomb" and "ATOMIC_BOMB" with my unique unit id, "ThermobaricMissile" and "THERMOBARIC_MISSILE", and it'll only affect the unique unit in question? Or do I need more complicated coding for it to be able to reference the UU only? Also, let's say that my custom civ is an ai player, and they fire the UU nuke, would there be a way for this code to affect that as well? Sorry if I'm missing any details or if you've already made it so those features work out properly, I kind of have to take your word for it considering, again, I am a huge newbie when it comes to LUA coding.
So if I'm reading this correctly, this code makes it so that an atomic bomb with the specific UU signature will have the unique unit affect trigger, specially the elimination of fallout once it's been spawned, right? This is absolutely wonderful, and thank you for going through the time and effort to code this, LUA is something that I am generally unfamiliar with so it would have taken me a very long while to learn and be able to compile something like this myself.
Almost correct. It will do stuff if and only if UNIT_ATOMIC_BOMB was fired off. UNIT_ATOMIC_BOMB can be a Unique Unit or not; the code only looks at the UnitType and not the UnitClass (Note that making it work based on the unitClass will only involve minor changes).
E.g. The code will fire for a non UU-atomic bomb: UNIT_ATOMIC_BOMB with UNITCLASS_ATOMIC_BOMB
E.g. The code will fire for a UU-atomic bomb: UNIT_ATOMIC_BOMB with UNITCLASS_ATOMIC_BOMB_DEFAULT
Note that only one unit with a UnitType of UNIT_ATOMIC_BOMB may exist (though multiple units can have the same UnitClass)
The implementation of removing the fallout has not yet been added though (but it should be below the "UU STUFF NOW WHOA" message); I'm going to give you a try first . (For some hints on how to remove fallout view my reply two replies up.)
I assume that I can just replace all instances of "AtomicBomb" and "ATOMIC_BOMB" with my unique unit id, "ThermobaricMissile" and "THERMOBARIC_MISSILE", and it'll only affect the unique unit in question? Or do I need more complicated coding for it to be able to reference the UU only?
Yes, as simple as that. No more complicated coding needed. I just tested it for the atomic bomb so I didn't have to add another unit myself
Also, let's say that my custom civ is an ai player, and they fire the UU nuke, would there be a way for this code to affect that as well? Sorry if I'm missing any details or if you've already made it so those features work out properly, I kind of have to take your word for it considering, again, I am a huge newbie when it comes to LUA coding.
GameEvents.NuclearDetonation and GameEvents.UnitPrekill fire for AI players as well (Rule of thumb: Don't use them if they don't fire for the AI, in SV, with Quick Combat/Movement, or when in the FoW!).
For an example of NuclearDetonation firing for an AI player view the 'Nuke the World' Achievement in my Additional Achievements mod (it requires an AI Gandhi to nuke someone).
For an example of UnitPrekill firing for an AI player view pretty much any mod as this is quite a commonly used hook.
Alright, so this is what I've worked out in the LUA file I made. I probably loused it up somewhere, but I just want to have it quickly proofread before I implement it and start actually testing if it can work.
local iThermobaricMissile = GameInfoTypes.UNIT_THERMOBARIC_MISSILE;
local bAnyBombJustExploded = false;
local iLastX = -1;
local iLastY = -1;
local iFallout = GameInfoTypes.FEATURE_FALLOUT
This part is all fine. Global variables and constants go at the top of the code.
Code:
function MissilePrekill(iPlayer, iUnit, _, iX, iY, bDelay, iByPlayer)
print("MissilePrekill: "..tostring(iPlayer).." "..tostring(iUnit).." "..tostring(iX).." "..tostring(iY).." "..tostring(bDelay).." "..tostring(iByPlayer));
if bDelay then
local pPlayer = Players[iPlayer]
local pUnit = pPlayer:GetUnitByID(iUnit);
if pUnit:NukeDamageLevel() > 0 and iByPlayer == -1 then
if pUnit:GetUnitType() == iThermobaricMissile then
if bAnyBombJustExploded then
You could remove the print(..) from this piece. It doesn't really add anything.
Code:
for i = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
local pPlotAdj = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), i)
--the plot should not be off the map
if pPlotAdj then
--other code here
end
end
if pPlot:GetFeatureType() == iFallout then
pPlot:SetFeatureType(-1)
end
print("UU STUFF NOW WHOA");
Here's where stuff goes wrong though. Line 2 of this snippet requires you to have pPlot. This was however never defined so you'll get an error related to 'a nil value'. (pPlot is a pointer referring to a plot/hex on the map).
Now, for the sake of example suppose that pPlot was defined above this snippet and actually refers to the correct plot (I.e. the plot where the missile stroke. Let's call that the center plot). Then we'd iterate over a nice for-loop and... well.. do nothing with that.
After the for-loop we remove fallout from the center plot (which is good).
Now, we'd like to do the same to the adjacent plots (the pPlotAdj's). So if we'd copy the bottom if-statement (that removes the fallout) to inside of the for-loop then we'd also remove fallout on those plots. Watch out though as we'll need to change the variables from the piece we copy!
To actually find a plot on the map based on XY-coordinates we can use pPlot = Map.GetPlot(iX,iY). Since we'd want the XY-coordinates of where the missile hit, rather than from where it fired, we stored the XY-coordinates (iLastX and iLastY) from the NuclearDetonation-event.
Code:
end
end
bAnyBombJustExploded = false;
end
end
end
GameEvents.UnitPrekill.Add(MissilePrekill)
function MissileFired(iPlayer, iX, iY, bWar, bBystander)
print("MissileFired: "..tostring(iPlayer).." "..tostring(iX).." "..tostring(iY).." "..tostring(bWar).." "..tostring(bBystander));
bAnyBombJustExploded = true;
iLastX = iX;
iLastY = iY;
end
GameEvents.NuclearDetonation.Add(MissileFired);
I'm not sure why you added these here. We already subscribed to these hooks (using GameEvents.NAME_OF_HOOK.Add(NameOfFunction)). Simply remove this.
Spoiler:
We end up with:
Code:
local iThermobaricMissile = GameInfoTypes.UNIT_THERMOBARIC_MISSILE;
local bAnyBombJustExploded = false;
local iLastX = -1;
local iLastY = -1;
local iFallout = GameInfoTypes.FEATURE_FALLOUT
function MissilePrekill(iPlayer, iUnit, _, iX, iY, bDelay, iByPlayer)
if bDelay then
local pPlayer = Players[iPlayer]
local pUnit = pPlayer:GetUnitByID(iUnit);
if pUnit:NukeDamageLevel() > 0 and iByPlayer == -1 then
if pUnit:GetUnitType() == iThermobaricMissile then
if bAnyBombJustExploded then
print("Thermobaric Missile exploded! Removing the resulting fallout...");
local pPlot = Map.GetPlot(iLastX, iLastY) --get the plot that was hit by the missile
for i = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do --iterate over the plots adjacent to the center plot
local pPlotAdj = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), i) --get one of the adjacent plots
--the plot should not be off the map
if pPlotAdj then --pPlotAdj == nil if the plot does not exist. E.g. it is off the map near the North or South pole
if pPlotAdj:GetFeatureType() == iFallout then --if there's fallout
pPlotAdj:SetFeatureType(-1) --remove it
end
end
end
--remove fallout from the center plot
if pPlot:GetFeatureType() == iFallout then
pPlot:SetFeatureType(-1)
end
end
end
bAnyBombJustExploded = false;
end
end
end
GameEvents.UnitPrekill.Add(MissilePrekill)
function MissileFired(iPlayer, iX, iY, bWar, bBystander)
bAnyBombJustExploded = true;
iLastX = iX;
iLastY = iY;
end
GameEvents.NuclearDetonation.Add(MissileFired);
Thank you, this will do perfectly. I'll be sure to credit you for your assistance with the LUA coding if I ever decide to release this mod to the public. I do have one more question, though.
In the coding, it makes it so that any fallout generated in adjacent tiles to the point of impact is removed. What if the weapon has a NukeDamageLevel of 2, meaning that fallout can generate anywhere within a two tile radius? I just tested this to confirm whether or not this might be an issue, and it seems that fallout did generate two tiles away from impact, but the fallout in directly adjacent tiles to the point of impact was removed. Would it be as simple as changing the values "-1, 1" for '
for i = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do' to "-2, 2"?
Thank you, this will do perfectly. I'll be sure to credit you for your assistance with the LUA coding if I ever decide to release this mod to the public. I do have one more question, though.
In the coding, it makes it so that any fallout generated in adjacent tiles to the point of impact is removed. What if the weapon has a NukeDamageLevel of 2, meaning that fallout can generate anywhere within a two tile radius? I just tested this to confirm whether or not this might be an issue, and it seems that fallout did generate two tiles away from impact, but the fallout in directly adjacent tiles to the point of impact was removed. Would it be as simple as changing the values "-1, 1" for '
for i = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do' to "-2, 2"?
For anything further away than a 1-tile radius you'd have to use Whoward's PlotIterators. It's a utility that allows you to iterate over all tiles in a radius of N.
Simply changing the -1 and 1 to -2 and 2 won't work.
-----------------------
X: The first part of the for-loop states at which value your variable starts. In this case 0.
Y: The 2nd part of the for-loop states until which value(s) for our variable (upto and including) we should execute the loop.
Z: The last part of the for-loop states with how much we should increase our variable each iteration.
Code:
for i=X, Y, Z do
--body
end
--other code below
In other words:
i starts at a value of X. Each iteration (I.e. every time the body is executed) i is increased by Z (so: i = i + Z). We will keep iterating (I.e. executing the body) until X <= Y does not hold anymore. Once this condition does not hold we exit the for-loop and continue executing the other code below.
-----------------------
Let's illustrate with an example:
Code:
for i=0, DirectionTypes.NUM_DIRECTION_TYPES -1, 1 do
print(i)
end
DirectionTypes.NUM_DIRECTION_TYPES will evaluate to 6 (as a hex has 6 adjacent hexes). This is just a define in civ5.
So what we actually have is:
Code:
for i=0, 6-1, 1 do
--which is the same as
for i=0, 5, 1 do
I'd suggest you read up a bit on how for-loops work if you do not understand yet how this works.
Note that Lua uses a bit of a different syntax for for-loops compared to other languages, but in essence it's the same principle
Alright, I see how that wouldn't work out ideally. So now I'm left with two options: Either decrease the blast radius of my nuke to affect only adjacent tiles (which I found out is not affected by NukeDamageLevel apparently, so I'll have to try something else), or use Whoward's PlotIterators to inject even more complex code into the system so all tiles within a 2-tile radius are checked. I've been doing a search for the PlotIterators, but I can't find any specific thread where it's available for download, nor can I find a place in whoward69's download list where the PlotIterator stuff is available, unless I'm more blind than I thought. Also, sorry if I seem really slow when it comes to understanding all this, I ended up failing my high school computer science class so that should tell you how much of a dullard I am when it comes to serious coding.
You can download it from this thread: https://forums.civfanatics.com/threads/border-and-area-plot-iterators.474634/
There's a lot of information on how it works in there (I.e. scary code) but in the end you "just" include a file with the provided code. So your code doesn't become more complex whatsoever, it just utilizes a complex utilty!
A checklist if what you should do:
1. Copy the code in the last code block of the first post in that thread to a file named PlotIterators.lua. Set VFS = true for this file. Do not add this file as an InGameUIAddin!
This allows your file to use the code in the PlotIterators.lua file.
3. Instead of the for-loop we currently use, use this as the iterator instead:
Code:
for pAreaPlot in PlotAreaSpiralIterator(pPlot, INSERT_YOUR_RANGE_HERE, SECTOR_NORTH, DIRECTION_CLOCKWISE, DIRECTION_OUTWARDS, CENTRE_INCLUDE) do
and replace INSERT_YOUR_RANGE_HERE by the range in which fallout will maximally spawn, so 2.
pPlot is the center plot from which we start iterating. SECTOR_NORTH is the direction from which we start iterating over the plots. The rest should be self explanatory (or is otherwise explained in the linked thread)
4. Rename the pPlotAdj variables to pAreaPlot accordingly.
You can also remove the final if-statement to remove fallout on the center plot, as PlotIterators also iterates over the center plot due to CENTRE_INCLUDE
Alright, this is coming along very nicely. Seems we're on the home stretch, so I'll post the two luas and once they're both confirmed to be working properly, I think we'll be all finished here. Thanks again for all the help.
for pAreaPlot in PlotAreaSpiralIterator(pPlot, 2, SECTOR_NORTH, DIRECTION_CLOCKWISE, DIRECTION_OUTWARDS, CENTRE_INCLUDE) do --iterate over the plots adjacent to the center plot
local pAreaPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), i) --get one of the adjacent plots
The 2nd line overwrites the pAreaPlot variable that we define on line 1 which we do not want! (Also, there's no variable i defined here so this statement would also throw a nasty error!)
Code:
--remove fallout from the center plot
if pPlot:GetFeatureType() == iFallout then
pPlot:SetFeatureType(-1)
end
As I already mentioned this part can be omitted since plotIterators takes care of the center plot too. Other than that it looks good.
Well, with those corrections made, I've done some playtesting and I can confirm that the coding 100% works, my special nukes are no longer generating any fallout and I'm seeing no errors. Thank you for everything, man, you're the real MVP. As I said before, I'll make sure to credit you and whoward69 in the special thanks if I make this mod public.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.