Gleb Bazov
Warlord
- Joined
- Feb 13, 2017
- Messages
- 176
CULTURE BOMB IMPLEMENTATION in Lua - City unable to work the new tiles.
I have exhausted all avenues of trying to figure out how this function works in LUA. The code I am writing implements a Culture Bomb functionality in LUA and works without problem, apart from this: When the ownership of plot tiles is changed, the borders expand as expected, but the tiles remain unworkable by the city and no buildings or districts can be placed on them.
The only solution is to build a new city next to the culture-bombed area, and then assume ownership of tiles (swap tile ownership) by that city, using the city manager. However, if the culture bomb is detonated near two cities that already exist, neither of the cities can use the tiles. I presume that a third city is then needed to claim ownership of the tiles, and so forth.
This wouldn't be a huge problem for district culture bombs, because I can always terminate the process in XML, and create a dummy building (however, that would lose the flexibility of setting the culture bomb radius at anything I want), but, for improvement culture bombs, this is a real problem.
I've tried a variety of variations to SetOwner(), using SetOwner( pPlayer, pCity) or SetOwner( pPlayer, pCity, true ) and variations of these, but nothing works. SetOwner() appears to work both for a City and a Player, and does required one of them, as giving it a random integer to work with crashes the game.
I've borrowed liberally from LeeS' mods for the purpose, and everything works just fine, except actually allowing the city to work the tiles. Here is the small snippet of the code that zeroes in on SetOwner();
As far as I can tell, the problem is solely with Plot:SetOwner(). I just don't know what arguments go there, although I suspect I got them right the first time (playerID). The problem may also be with needing to refresh the current game state, but I don't know how to do that. Besides, the borders change, so that couldn't be the sole reason for the problem. Maybe this has to do with UI Lua files, and I'll keep digging, but I would really appreciate it if someone could look at this as well. Thank you!
Here is the full code. I am not including the separate chunks of the code that are needed for the improvement culture bomb, but the idea is the same, though the conditions are somewhat different (improvement, resource, resource visibility (check tech, etc.)).
I have exhausted all avenues of trying to figure out how this function works in LUA. The code I am writing implements a Culture Bomb functionality in LUA and works without problem, apart from this: When the ownership of plot tiles is changed, the borders expand as expected, but the tiles remain unworkable by the city and no buildings or districts can be placed on them.
The only solution is to build a new city next to the culture-bombed area, and then assume ownership of tiles (swap tile ownership) by that city, using the city manager. However, if the culture bomb is detonated near two cities that already exist, neither of the cities can use the tiles. I presume that a third city is then needed to claim ownership of the tiles, and so forth.
This wouldn't be a huge problem for district culture bombs, because I can always terminate the process in XML, and create a dummy building (however, that would lose the flexibility of setting the culture bomb radius at anything I want), but, for improvement culture bombs, this is a real problem.
I've tried a variety of variations to SetOwner(), using SetOwner( pPlayer, pCity) or SetOwner( pPlayer, pCity, true ) and variations of these, but nothing works. SetOwner() appears to work both for a City and a Player, and does required one of them, as giving it a random integer to work with crashes the game.
I've borrowed liberally from LeeS' mods for the purpose, and everything works just fine, except actually allowing the city to work the tiles. Here is the small snippet of the code that zeroes in on SetOwner();
Code:
function DetonateCultureBomb( PlotX, PlotY, iPlotRadius, pPlayer )
if (PlotX ~= nil) and (PlotY ~= nil) and (PlotX > 0) and (PlotY > 0) then
for k,pPickPlot in pairs(GetAllPlotsInRadiusR(Map.GetPlot(PlotX, PlotY), iPlotRadius, "ExcludeCenterPlot")) do
local bAddPlot = (Map.GetPlotDistance(PlotX, PlotY, pPickPlot:GetX(), pPickPlot:GetY()) <= iPlotRadius)
if bAddPlot then
pPickPlot:SetOwner(pPlayer)
end
end
end
end
As far as I can tell, the problem is solely with Plot:SetOwner(). I just don't know what arguments go there, although I suspect I got them right the first time (playerID). The problem may also be with needing to refresh the current game state, but I don't know how to do that. Besides, the borders change, so that couldn't be the sole reason for the problem. Maybe this has to do with UI Lua files, and I'll keep digging, but I would really appreciate it if someone could look at this as well. Thank you!
Here is the full code. I am not including the separate chunks of the code that are needed for the improvement culture bomb, but the idea is the same, though the conditions are somewhat different (improvement, resource, resource visibility (check tech, etc.)).
Code:
--==============================================================================
-- Culture Bomb Implementation in LUA
--===============================================================================
--==============================================================================
-- Get All Plots within range 'X' of a given plot
--==============================================================================
local AdjacentPlotDirectionCalcs = { NW = (function(X, Y) if (Y % 2 ~= 0) then return (X + 1), (Y + 1) else return X, (Y + 1) end end),
W = (function(X, Y) return (X + 1), Y end),
SW = (function(X, Y) if (Y % 2 ~= 0) then return (X + 1), (Y - 1) else return X, (Y - 1) end end),
SE = (function(X, Y) if (Y % 2 == 0) then return (X - 1), (Y - 1) else return X, (Y - 1) end end),
E = (function(X, Y) return (X - 1), Y end),
NE = (function(X, Y) if (Y % 2 == 0) then return (X - 1), (Y + 1) else return X, (Y + 1) end end) }
function GetPlotCoordsInCardinalDirectionAtRadiusR(X, Y, R, sDirection)
if (R < 1) or (sDirection == nil) or (AdjacentPlotDirectionCalcs[sDirection] == nil) then
return X, Y
end
local iX, iY = AdjacentPlotDirectionCalcs[sDirection](X, Y)
if R > 1 then
local iStartRadius = 1
while iStartRadius < R do
iX, iY = AdjacentPlotDirectionCalcs[sDirection](iX, iY)
iStartRadius = iStartRadius + 1
end
end
return iX, iY
end
function GetAllPlotsInRadiusR(pPlot, iRadius, sExcludeCenterPlot)
local tTemporaryTable = {}
if (pPlot == nil) or (iRadius == nil) or (iRadius < 1) then
return tTemporaryTable
end
local bExcludeCenter = ((sExcludeCenterPlot ~= nil) and (sExcludeCenterPlot == "ExcludeCenterPlot"))
local iCenterX, iCenterY = pPlot:GetX(), pPlot:GetY()
local iStartX, iStartY = GetPlotCoordsInCardinalDirectionAtRadiusR(iCenterX, iCenterY, iRadius, "NE")
local iEndX, iEndY = GetPlotCoordsInCardinalDirectionAtRadiusR(iCenterX, iCenterY, iRadius, "SW")
local iRowEndX, iRowEndY = GetPlotCoordsInCardinalDirectionAtRadiusR(iCenterX, iCenterY, iRadius, "NW")
local iRowStartX, iRowStartY = iStartX, iStartY
for iPlotY = iStartY, iEndY, -1 do
local pLoopPlot = nil
for iPlotX = iRowStartX, iRowEndX do
local pLoopPlot = Map.GetPlot(iPlotX, iPlotY)
if (pLoopPlot ~= nil) then
if (pLoopPlot ~= pPlot) then
table.insert(tTemporaryTable,pLoopPlot)
else
if not bExcludeCenter then
table.insert(tTemporaryTable,pLoopPlot)
end
end
end
end
if ((iPlotY - 1) >= iCenterY) then
iRowStartX = AdjacentPlotDirectionCalcs.SE(iRowStartX, iPlotY)
iRowEndX = AdjacentPlotDirectionCalcs.SW(iRowEndX, iPlotY)
else
iRowStartX = AdjacentPlotDirectionCalcs.SW(iRowStartX, iPlotY)
iRowEndX = AdjacentPlotDirectionCalcs.SE(iRowEndX, iPlotY)
end
PrintToLog("GetAllPlotsInRadiusR: After calc for a new Y-row, iRowStartX is now " .. tostring(iRowStartX) .. " and iRowEndX is now " .. tostring(iRowEndX))
end
return tTemporaryTable
end
--==============================================================================
-- Check District Adjacency Function
--==============================================================================
function CheckDistrictAdjacency( PlotX, PlotY, zPlotRadius, xRequiredFeature )
local pPlot = Map.GetPlot(PlotX, PlotY)
local iPlotIndex = pPlot:GetIndex()
local iCity = Cities.GetPlotPurchaseCity(iPlotIndex)
if (iCity ~= nil) then
print ("Starting the process of checking adjacencies.")
for k,pPickPlot in pairs(GetAllPlotsInRadiusR(Map.GetPlot(PlotX, PlotY), zPlotRadius, "ExcludeCenterPlot")) do
local bAddPlot = (Map.GetPlotDistance(PlotX, PlotY, pPickPlot:GetX(), pPickPlot:GetY()) <= zPlotRadius)
if bAddPlot then
local pPickPlotFeature = pPickPlot:GetFeatureType()
local xFeatureMatchPlot = (pPickPlotFeature == GameInfo.Features[xRequiredFeature].Index)
print ("The feature # of the checked plot is -- " .. pPickPlotFeature .. " -- is this a match? Let's see.")
if xFeatureMatchPlot then
print ("Success! Adjacency confirmed on this plot. No need to check further. Returning true.")
return true
elseif k == zPlotRadius*6 and not xFeatureMatchPlot then
break
else
print ("This feature type does not qualify for adjacency requirements. Checking the next plot.")
end
end
end
end
print ("Failure! We've run out of adjacent plots and none of them contain the required feature.")
return false
end
--==============================================================================
-- Culture Bomb Function
--==============================================================================
function DetonateCultureBomb( PlotX, PlotY, iPlotRadius, pPlayer )
if (PlotX ~= nil) and (PlotY ~= nil) and (PlotX > 0) and (PlotY > 0) then
print ("Activating the Culture Bomb at the Given Location.")
for k,pPickPlot in pairs(GetAllPlotsInRadiusR(Map.GetPlot(PlotX, PlotY), iPlotRadius, "ExcludeCenterPlot")) do
local bAddPlot = (Map.GetPlotDistance(PlotX, PlotY, pPickPlot:GetX(), pPickPlot:GetY()) <= iPlotRadius)
if bAddPlot then
pPickPlot:SetOwner(pPlayer)
end
end
end
end
--==============================================================================
-- Activate Culture Bomb When Requirements are Satisfied
--==============================================================================
local sRequiredCiv = "CIVILIZATION_EGYPT"
local iRequiredDistrict = "DISTRICT_ENCAMPMENT"
function OnRequiredDistrictCompleted( playerID, cityID, orderType, unitType, canceled, typeModifier )
if orderType == 2 and GameInfo.Districts[unitType].DistrictType == iRequiredDistrict then
print (unitType)
print ("District Conditions Satisfied")
local pPlayer = Players[ playerID ]
local uCity = pPlayer:GetCities():FindID(cityID)
local pPlayerConfig = PlayerConfigurations[ playerID ]
local pPlayerCivName = pPlayerConfig:GetCivilizationTypeName()
if pPlayerCivName == sRequiredCiv then
print ("Civilization Type Conditions Satisfied")
local zPlotRadius = 1
local xRequiredFeature = "FEATURE_JUNGLE"
if CheckDistrictAdjacency( pDistrictX, pDistrictY, zPlotRadius, xRequiredFeature ) then
local pDistrict = uCity:GetDistricts():GetDistrict(unitType)
local pDistrictX = pDistrict:GetX()
local pDistrictY = pDistrict:GetY()
local tPlotRadius = 1
DetonateCultureBomb( pDistrictX, pDistrictY, tPlotRadius, pPlayer )
end
end
end
end
Events.CityProductionCompleted.Add( OnRequiredDistrictCompleted );