Zeritum
Chieftain
I'm using Lua at the moment to try and detect when a civilization destroys an encampment so I can spawn a unit on that spot (it's a unique trait of a custom civ)
I can't seem to find any event to detect when a civilization destroys an encampment.
So:
Thanks in advance
EDIT FOR SOLUTION::
You'll need two Lua scripts. I've named mine PlotIterators and SpawnSettlerWhenCampGone. A lot of this code is from RawSasquatch's Fallout Civs, however it has been adapted and stripped down to just spawn a specific unit (in my case, a Settler) when a barbarian camp is destroyed by a certain civ (defined by the local CivID at the top of the SpawnSettlerWhenCampGone script).
PlotIterators.lua (Import VFS = true)
SECTOR_NORTH = 1
SECTOR_NORTHEAST = 2
SECTOR_SOUTHEAST = 3
SECTOR_SOUTH = 4
SECTOR_SOUTHWEST = 5
SECTOR_NORTHWEST = 6
DIRECTION_CLOCKWISE = false
DIRECTION_ANTICLOCKWISE = true
DIRECTION_OUTWARDS = false
DIRECTION_INWARDS = true
CENTRE_INCLUDE = true
CENTRE_EXCLUDE = false
function PlotRingIterator(pPlot, r, sector, anticlock)
--print(string.format("PlotRingIterator((%i, %i), r=%i, s=%i, d=%s)", pPlot:GetX(), pPlot:GetY(), r, (sector or SECTOR_NORTH), (anticlock and "rev" or "fwd")))
-- The important thing to remember with hex-coordinates is that x+y+z = 0
-- so we never actually need to store z as we can always calculate it as -(x+y)
-- See http://keekerdc.com/2011/03/hexagon-grids-coordinate-systems-and-distance-calculations/
if (pPlot ~= nil and r > 0) then
local hex = ToHexFromGrid({x=pPlot:GetX(), y=pPlot:GetY()})
local x, y = hex.x, hex.y
-- Along the North edge of the hex (x-r, y+r, z) to (x, y+r, z-r)
local function north(x, y, r, i) return {x=x-r+i, y=y+r} end
-- Along the North-East edge (x, y+r, z-r) to (x+r, y, z-r)
local function northeast(x, y, r, i) return {x=x+i, y=y+r-i} end
-- Along the South-East edge (x+r, y, z-r) to (x+r, y-r, z)
local function southeast(x, y, r, i) return {x=x+r, y=y-i} end
-- Along the South edge (x+r, y-r, z) to (x, y-r, z+r)
local function south(x, y, r, i) return {x=x+r-i, y=y-r} end
-- Along the South-West edge (x, y-r, z+r) to (x-r, y, z+r)
local function southwest(x, y, r, i) return {x=x-i, y=y-r+i} end
-- Along the North-West edge (x-r, y, z+r) to (x-r, y+r, z)
local function northwest(x, y, r, i) return {x=x-r, y=y+i} end
local side = {north, northeast, southeast, south, southwest, northwest}
if (sector) then
for i=(anticlock and 1 or 2), sector, 1 do
table.insert(side, table.remove(side, 1))
end
end
-- This coroutine walks the edges of the hex centered on pPlot at radius r
local next = coroutine.create(function ()
if (anticlock) then
for s=6, 1, -1 do
for i=r, 1, -1 do
coroutine.yield(side[s](x, y, r, i))
end
end
else
for s=1, 6, 1 do
for i=0, r-1, 1 do
coroutine.yield(side[s](x, y, r, i))
end
end
end
return nil
end)
-- This function returns the next edge plot in the sequence, ignoring those that fall off the edges of the map
return function ()
local pEdgePlot = nil
local success, hex = coroutine.resume(next)
--if (hex ~= nil) then print(string.format("hex(%i, %i, %i)", hex.x, hex.y, -1 * (hex.x+hex.y))) else print("hex(nil)") end
while (success and hex ~= nil and pEdgePlot == nil) do
pEdgePlot = Map.GetPlot(ToGridFromHex(hex.x, hex.y))
if (pEdgePlot == nil) then success, hex = coroutine.resume(next) end
end
return success and pEdgePlot or nil
end
else
-- Iterators have to return a function, so return a function that returns nil
return function () return nil end
end
end
function PlotAreaSpiralIterator(pPlot, r, sector, anticlock, inwards, centre)
--print(string.format("PlotAreaSpiralIterator((%i, %i), r=%i, s=%i, d=%s, w=%s, c=%s)", pPlot:GetX(), pPlot:GetY(), r, (sector or SECTOR_NORTH), (anticlock and "rev" or "fwd"), (inwards and "in" or "out"), (centre and "yes" or "no")))
-- This coroutine walks each ring in sequence
local next = coroutine.create(function ()
if (centre and not inwards) then
coroutine.yield(pPlot)
end
if (inwards) then
for i=r, 1, -1 do
for pEdgePlot in PlotRingIterator(pPlot, i, sector, anticlock) do
coroutine.yield(pEdgePlot)
end
end
else
for i=1, r, 1 do
for pEdgePlot in PlotRingIterator(pPlot, i, sector, anticlock) do
coroutine.yield(pEdgePlot)
end
end
end
if (centre and inwards) then
coroutine.yield(pPlot)
end
return nil
end)
-- This function returns the next plot in the sequence
return function ()
local success, pAreaPlot = coroutine.resume(next)
return success and pAreaPlot or nil
end
end
function PlotAreaSweepIterator(pPlot, r, sector, anticlock, inwards, centre)
--print(string.format("PlotAreaSweepIterator((%i, %i), r=%i, s=%i, d=%s, w=%s, c=%s)", pPlot:GetX(), pPlot:GetY(), r, (sector or SECTOR_NORTH), (anticlock and "rev" or "fwd"), (inwards and "in" or "out"), (centre and "yes" or "no")))
-- This coroutine walks each radial in sequence
local next = coroutine.create(function ()
if (centre and not inwards) then
coroutine.yield(pPlot)
end
local iterators = {}
for i=1, r, 1 do
iterators[i] = PlotRingIterator(pPlot, i, sector, anticlock)
end
for s=1, 6, 1 do
-- In ring (n+1) there are (n+1)*6 or 6n + 6 plots,
-- so we need to call the (n+1)th iterator six more times than the nth, or once more per outer loop
-- eg for 3 rings we need a plot from ring 1, 2, 3, 2, 3, 3 repeated 6 times
if (inwards) then
for i=r, 1, -1 do
for j=r, i, -1 do
coroutine.yield(iterators[j]())
end
end
else
for i=1, r, 1 do
for j=i, r, 1 do
coroutine.yield(iterators[j]())
end
end
end
end
if (centre and inwards) then
coroutine.yield(pPlot)
end
return nil
end)
-- This function returns the next plot in the sequence
return function ()
local success, pAreaPlot = coroutine.resume(next)
return success and pAreaPlot or nil
end
end
--
-- Test functions
--
highlights = {
RED = {x=1.0, y=0.0, z=0.0, w=1.0},
GREEN = {x=0.0, y=1.0, z=0.0, w=1.0},
BLUE = {x=0.0, y=0.0, z=1.0, w=1.0},
CYAN = {x=0.0, y=1.0, z=1.0, w=1.0},
YELLOW = {x=1.0, y=1.0, z=0.0, w=1.0},
MAGENTA = {x=1.0, y=0.0, z=1.0, w=1.0},
BLACK = {x=0.5, y=0.5, z=0.5, w=1.0}
}
function TestPlotHighlight(pPlot, highlight)
print(pPlot:GetX(), pPlot:GetY())
if (highlight ~= nil) then
Events.SerialEventHexHighlight(ToHexFromGrid(Vector2(pPlot:GetX(), pPlot:GetY())), true, highlight)
end
end
function TestPlotRingIterator(pPlot, r, sector, anticlock, highlight)
for pEdgePlot in PlotRingIterator(pPlot, r, sector, anticlock) do
TestPlotHighlight(pEdgePlot, highlight)
end
end
function TestPlotAreaSpiralIterator(pPlot, r, sector, anticlock, inwards, centre, highlight)
for pAreaPlot in PlotAreaSpiralIterator(pPlot, r, sector, anticlock, inwards, centre) do
TestPlotHighlight(pAreaPlot, highlight)
end
end
function TestPlotAreaSweepIterator(pPlot, r, sector, anticlock, inwards, centre, highlight)
for pAreaPlot in PlotAreaSweepIterator(pPlot, r, sector, anticlock, inwards, centre) do
TestPlotHighlight(pAreaPlot, highlight)
end
end
-- TestPlotRingIterator(Players[0]:GetCapitalCity():Plot(), 4, SECTOR_NORTH, DIRECTION_CLOCKWISE, highlights.RED)
-- TestPlotAreaSpiralIterator(Players[0]:GetCapitalCity():Plot(), 3, SECTOR_SOUTH, DIRECTION_ANTICLOCKWISE, DIRECTION_OUTWARDS, CENTRE_INCLUDE)
-- TestPlotAreaSweepIterator(Players[0]:GetCapitalCity():Plot(), 3, SECTOR_SOUTH, DIRECTION_ANTICLOCKWISE, DIRECTION_OUTWARDS, CENTRE_INCLUDE)
SpawnSettlerWhenCampGone.lua (No need to import to VFS. Set the type as InGameUIAddin in the Content tab on the mod properties.)
Thank you calcul8or and LeeS for your help
I can't seem to find any event to detect when a civilization destroys an encampment.
So:
- Does such an event even exist?
- If not, is there a workaround that isn't too heavy on the turn times?
- Lastly, is Lua the best way to do this, or is there a simple way to do it in XML? It seemed far too complex for that so I didn't even try.
Thanks in advance
EDIT FOR SOLUTION::
You'll need two Lua scripts. I've named mine PlotIterators and SpawnSettlerWhenCampGone. A lot of this code is from RawSasquatch's Fallout Civs, however it has been adapted and stripped down to just spawn a specific unit (in my case, a Settler) when a barbarian camp is destroyed by a certain civ (defined by the local CivID at the top of the SpawnSettlerWhenCampGone script).
PlotIterators.lua (Import VFS = true)
Spoiler :
SECTOR_NORTH = 1
SECTOR_NORTHEAST = 2
SECTOR_SOUTHEAST = 3
SECTOR_SOUTH = 4
SECTOR_SOUTHWEST = 5
SECTOR_NORTHWEST = 6
DIRECTION_CLOCKWISE = false
DIRECTION_ANTICLOCKWISE = true
DIRECTION_OUTWARDS = false
DIRECTION_INWARDS = true
CENTRE_INCLUDE = true
CENTRE_EXCLUDE = false
function PlotRingIterator(pPlot, r, sector, anticlock)
--print(string.format("PlotRingIterator((%i, %i), r=%i, s=%i, d=%s)", pPlot:GetX(), pPlot:GetY(), r, (sector or SECTOR_NORTH), (anticlock and "rev" or "fwd")))
-- The important thing to remember with hex-coordinates is that x+y+z = 0
-- so we never actually need to store z as we can always calculate it as -(x+y)
-- See http://keekerdc.com/2011/03/hexagon-grids-coordinate-systems-and-distance-calculations/
if (pPlot ~= nil and r > 0) then
local hex = ToHexFromGrid({x=pPlot:GetX(), y=pPlot:GetY()})
local x, y = hex.x, hex.y
-- Along the North edge of the hex (x-r, y+r, z) to (x, y+r, z-r)
local function north(x, y, r, i) return {x=x-r+i, y=y+r} end
-- Along the North-East edge (x, y+r, z-r) to (x+r, y, z-r)
local function northeast(x, y, r, i) return {x=x+i, y=y+r-i} end
-- Along the South-East edge (x+r, y, z-r) to (x+r, y-r, z)
local function southeast(x, y, r, i) return {x=x+r, y=y-i} end
-- Along the South edge (x+r, y-r, z) to (x, y-r, z+r)
local function south(x, y, r, i) return {x=x+r-i, y=y-r} end
-- Along the South-West edge (x, y-r, z+r) to (x-r, y, z+r)
local function southwest(x, y, r, i) return {x=x-i, y=y-r+i} end
-- Along the North-West edge (x-r, y, z+r) to (x-r, y+r, z)
local function northwest(x, y, r, i) return {x=x-r, y=y+i} end
local side = {north, northeast, southeast, south, southwest, northwest}
if (sector) then
for i=(anticlock and 1 or 2), sector, 1 do
table.insert(side, table.remove(side, 1))
end
end
-- This coroutine walks the edges of the hex centered on pPlot at radius r
local next = coroutine.create(function ()
if (anticlock) then
for s=6, 1, -1 do
for i=r, 1, -1 do
coroutine.yield(side[s](x, y, r, i))
end
end
else
for s=1, 6, 1 do
for i=0, r-1, 1 do
coroutine.yield(side[s](x, y, r, i))
end
end
end
return nil
end)
-- This function returns the next edge plot in the sequence, ignoring those that fall off the edges of the map
return function ()
local pEdgePlot = nil
local success, hex = coroutine.resume(next)
--if (hex ~= nil) then print(string.format("hex(%i, %i, %i)", hex.x, hex.y, -1 * (hex.x+hex.y))) else print("hex(nil)") end
while (success and hex ~= nil and pEdgePlot == nil) do
pEdgePlot = Map.GetPlot(ToGridFromHex(hex.x, hex.y))
if (pEdgePlot == nil) then success, hex = coroutine.resume(next) end
end
return success and pEdgePlot or nil
end
else
-- Iterators have to return a function, so return a function that returns nil
return function () return nil end
end
end
function PlotAreaSpiralIterator(pPlot, r, sector, anticlock, inwards, centre)
--print(string.format("PlotAreaSpiralIterator((%i, %i), r=%i, s=%i, d=%s, w=%s, c=%s)", pPlot:GetX(), pPlot:GetY(), r, (sector or SECTOR_NORTH), (anticlock and "rev" or "fwd"), (inwards and "in" or "out"), (centre and "yes" or "no")))
-- This coroutine walks each ring in sequence
local next = coroutine.create(function ()
if (centre and not inwards) then
coroutine.yield(pPlot)
end
if (inwards) then
for i=r, 1, -1 do
for pEdgePlot in PlotRingIterator(pPlot, i, sector, anticlock) do
coroutine.yield(pEdgePlot)
end
end
else
for i=1, r, 1 do
for pEdgePlot in PlotRingIterator(pPlot, i, sector, anticlock) do
coroutine.yield(pEdgePlot)
end
end
end
if (centre and inwards) then
coroutine.yield(pPlot)
end
return nil
end)
-- This function returns the next plot in the sequence
return function ()
local success, pAreaPlot = coroutine.resume(next)
return success and pAreaPlot or nil
end
end
function PlotAreaSweepIterator(pPlot, r, sector, anticlock, inwards, centre)
--print(string.format("PlotAreaSweepIterator((%i, %i), r=%i, s=%i, d=%s, w=%s, c=%s)", pPlot:GetX(), pPlot:GetY(), r, (sector or SECTOR_NORTH), (anticlock and "rev" or "fwd"), (inwards and "in" or "out"), (centre and "yes" or "no")))
-- This coroutine walks each radial in sequence
local next = coroutine.create(function ()
if (centre and not inwards) then
coroutine.yield(pPlot)
end
local iterators = {}
for i=1, r, 1 do
iterators[i] = PlotRingIterator(pPlot, i, sector, anticlock)
end
for s=1, 6, 1 do
-- In ring (n+1) there are (n+1)*6 or 6n + 6 plots,
-- so we need to call the (n+1)th iterator six more times than the nth, or once more per outer loop
-- eg for 3 rings we need a plot from ring 1, 2, 3, 2, 3, 3 repeated 6 times
if (inwards) then
for i=r, 1, -1 do
for j=r, i, -1 do
coroutine.yield(iterators[j]())
end
end
else
for i=1, r, 1 do
for j=i, r, 1 do
coroutine.yield(iterators[j]())
end
end
end
end
if (centre and inwards) then
coroutine.yield(pPlot)
end
return nil
end)
-- This function returns the next plot in the sequence
return function ()
local success, pAreaPlot = coroutine.resume(next)
return success and pAreaPlot or nil
end
end
--
-- Test functions
--
highlights = {
RED = {x=1.0, y=0.0, z=0.0, w=1.0},
GREEN = {x=0.0, y=1.0, z=0.0, w=1.0},
BLUE = {x=0.0, y=0.0, z=1.0, w=1.0},
CYAN = {x=0.0, y=1.0, z=1.0, w=1.0},
YELLOW = {x=1.0, y=1.0, z=0.0, w=1.0},
MAGENTA = {x=1.0, y=0.0, z=1.0, w=1.0},
BLACK = {x=0.5, y=0.5, z=0.5, w=1.0}
}
function TestPlotHighlight(pPlot, highlight)
print(pPlot:GetX(), pPlot:GetY())
if (highlight ~= nil) then
Events.SerialEventHexHighlight(ToHexFromGrid(Vector2(pPlot:GetX(), pPlot:GetY())), true, highlight)
end
end
function TestPlotRingIterator(pPlot, r, sector, anticlock, highlight)
for pEdgePlot in PlotRingIterator(pPlot, r, sector, anticlock) do
TestPlotHighlight(pEdgePlot, highlight)
end
end
function TestPlotAreaSpiralIterator(pPlot, r, sector, anticlock, inwards, centre, highlight)
for pAreaPlot in PlotAreaSpiralIterator(pPlot, r, sector, anticlock, inwards, centre) do
TestPlotHighlight(pAreaPlot, highlight)
end
end
function TestPlotAreaSweepIterator(pPlot, r, sector, anticlock, inwards, centre, highlight)
for pAreaPlot in PlotAreaSweepIterator(pPlot, r, sector, anticlock, inwards, centre) do
TestPlotHighlight(pAreaPlot, highlight)
end
end
-- TestPlotRingIterator(Players[0]:GetCapitalCity():Plot(), 4, SECTOR_NORTH, DIRECTION_CLOCKWISE, highlights.RED)
-- TestPlotAreaSpiralIterator(Players[0]:GetCapitalCity():Plot(), 3, SECTOR_SOUTH, DIRECTION_ANTICLOCKWISE, DIRECTION_OUTWARDS, CENTRE_INCLUDE)
-- TestPlotAreaSweepIterator(Players[0]:GetCapitalCity():Plot(), 3, SECTOR_SOUTH, DIRECTION_ANTICLOCKWISE, DIRECTION_OUTWARDS, CENTRE_INCLUDE)
SpawnSettlerWhenCampGone.lua (No need to import to VFS. Set the type as InGameUIAddin in the Content tab on the mod properties.)
Spoiler :
include("PlotIterators.lua")
print("Odrysian Trait loaded");
local OdrysianCivID = GameInfoTypes.CIVILIZATION_ODRYSIA;
local GoodHut = GameInfoTypes.IMPROVEMENT_BARBARIAN_CAMP;
local GoodyHuts = {}
GameEvents.PlayerDoTurn.Add(function(iPlayer)
local pPlayer = Players[iPlayer];
if pPlayer:GetCivilizationType() == LegionCivID then
for iPlot, bool in pairs(GoodyHuts) do
if iPlot:GetImprovementType() ~= GoodHut then
GoodyHuts[iPlot] = nil;
end
end
end
if pPlayer:IsBarbarian() then
AddNewCamps(iPlayer)
end
end)
function AddNewCamps(iPlayer)
for iUnit in Players[iPlayer]:Units() do
local iPlot = iUnit:GetPlot()
if iPlot:GetImprovementType() == GoodHut then
GoodyHuts[iPlot] = true;
end
end
end
function Odrysianxy(player, unit, x, y)
if Players[player]:GetUnitByID(unit) ~= nil then
local cUnit = Players[player]:GetUnitByID(unit);
cPlot = cUnit:GetPlot();
if cPlot ~= nil then
if Players[player]:GetCivilizationType() == OdrysianCivID then
if GoodyHuts[cPlot] ~= nil then
GoodyHuts[cPlot] = nil;
if cPlot:GetOwner() == -1 then
local cCapital = Players[player]:GetCapitalCity();
if cCapital then
local unit
unit = Players[player]:InitUnit(GameInfoTypes["UNIT_SETTLER"], x, y, UNITAI_SETTLE, DIRECTION_NORTHWEST);
end
end
end
end
end
end
end
GameEvents.UnitSetXY.Add(Odrysianxy)
print("Odrysian Trait loaded");
local OdrysianCivID = GameInfoTypes.CIVILIZATION_ODRYSIA;
local GoodHut = GameInfoTypes.IMPROVEMENT_BARBARIAN_CAMP;
local GoodyHuts = {}
GameEvents.PlayerDoTurn.Add(function(iPlayer)
local pPlayer = Players[iPlayer];
if pPlayer:GetCivilizationType() == LegionCivID then
for iPlot, bool in pairs(GoodyHuts) do
if iPlot:GetImprovementType() ~= GoodHut then
GoodyHuts[iPlot] = nil;
end
end
end
if pPlayer:IsBarbarian() then
AddNewCamps(iPlayer)
end
end)
function AddNewCamps(iPlayer)
for iUnit in Players[iPlayer]:Units() do
local iPlot = iUnit:GetPlot()
if iPlot:GetImprovementType() == GoodHut then
GoodyHuts[iPlot] = true;
end
end
end
function Odrysianxy(player, unit, x, y)
if Players[player]:GetUnitByID(unit) ~= nil then
local cUnit = Players[player]:GetUnitByID(unit);
cPlot = cUnit:GetPlot();
if cPlot ~= nil then
if Players[player]:GetCivilizationType() == OdrysianCivID then
if GoodyHuts[cPlot] ~= nil then
GoodyHuts[cPlot] = nil;
if cPlot:GetOwner() == -1 then
local cCapital = Players[player]:GetCapitalCity();
if cCapital then
local unit
unit = Players[player]:InitUnit(GameInfoTypes["UNIT_SETTLER"], x, y, UNITAI_SETTLE, DIRECTION_NORTHWEST);
end
end
end
end
end
end
end
GameEvents.UnitSetXY.Add(Odrysianxy)
Thank you calcul8or and LeeS for your help