[MOD] Help with buildings

Arkatakor

King
Joined
Mar 11, 2006
Messages
621
Location
Stockholm, Sweden
I am adding some new buildings to a mod of mine and wanted to ask about how to achieve the following:

1) Have a building that does 10 damage a turn to units within 2 squares from a city
2) Have a building that offers +2 production and + 1 science when city is connected to the capital

For #1 I looked into JFD's Stalin leader mod (which has a feature wherby units take 10 damage a turn when in Russia's territory) however my searches came up short. For #2 I did not see any column names in the building table that matched what I was looking for. I will continue searching in other building related tables.
 
I can't think of specific XML columns that would do these things, but with Lua this'd certainly be possible.

Here's a general rundown of I'd implement it
Code:
PlayerDoTurn:
- Loop through all the player's cities
- check if the city has BUILDING_STANDING_NEAR_ME_HURTS
- if yes:
    - Loop through all plots within a 2-tile range (using Whoward's plotiterators)
    - Loop through all non-trade units on a plot
    -- OPTIONAL: Check if the unit owner is at war with the city owner (the one you're running PlayerDoTurn on). if yes:
        -deal 10 damage
Code:
PlayerDoTurn:
- loop through all the player's cities
- if pPlayer:IsCapitalConnectedToCity(pCity) and city doesn't have the dummy building then
  - grant a dummy building providing +2 production and +1 science
- elseif city has the dummy
  -remove the dummy

You could give your dummy an entry in the <Building_BuildingClassYieldChanges>-table similar to how Neuschwanstein grants Culture and Gold to Castles. This way, your yields will still show up on your normal non-dummy building!
Code:
<Building_BuildingClassYieldChanges>
        <Row>
            <BuildingType>BUILDING_NEUSCHWANSTEIN</BuildingType>
            <BuildingClassType>BUILDINGCLASS_CASTLE</BuildingClassType>
            <YieldType>YIELD_GOLD</YieldType>
            <YieldChange>3</YieldChange>
        </Row>
</Building_BuildingClassYieldChanges>
Edit: As LeeS pointed out this table grants a global effect, which I completely forgot...
 
Last edited:
For table Building_BuildingClassYieldChanges, every copy of a building stated as BuildingType adds an additional yield to all buildings anywhere in the same empire within the class specified by BuildingClassType. So, no, you don't want to use that table.

Note that the Firaxis UI does not always reflect this properly when multiple copies of a building have all added yields to another building-class via Building_BuildingClassYieldChanges, and there are some issues as I recall with not everything getting wiped properly when such additional copies of the "granter" BuildingType are removed from the game.

Long Story Short, you should generally only use Building_BuildingClassYieldChanges when making a World or National Wonder give additional yields to "regular" buildings.
 
Thanks for the responses guys - sorry for the late reply; i've been sick the last few days; will take a serious look at this in the next few days and get back to you.
 
Sorry for the long delay on this. I finally sat down to give this concept a shot. I was able to find some related code in JFD's Stalins mod (which damages enemy units in Stalins territory every turn) to base myself on. I was not able to find a reference in the Lua UI under 'Unit' for how to actually damage a unit - could anyone point out how to do that? Here is my code (check the 'damage unit here' comment).

Code:
local buildingKnossosID        = GameInfoTypes["BUILDING_KNOSSOS"]

GameEvents.PlayerDoTurn.Add( KnossosDamage)

local g_iKnossosPlayerID = nil
local g_iKnossosBuildingPlot = nil

-------------------------------------------------------------------------------
--    Event function - fires every turn
-------------------------------------------------------------------------------
function  KnossosDamage(playerID)   
    --    Also stores the plot location of knossos if it exists
    g_iKnossosPlayerID = GetKnossosPlayerID()

    if (g_iKnossosPlayerID == nil)
        return
    end

    local player = Players[playerID]

    if (player:IsAlive() and IsPlayerAtWarWithKnossosOwner(playerID)) then
        local bIsUnitNearKnossos = false

        local playerUnits = player:Units()

        --    loop thru players units and damage them if in vicinity to knossos
        for _, unit in ipairs(playerUnits) do
            local plot = unit:GetPlot()           
            if (plot and (IsUnitPlotNearKnossos(plot))) then
                bIsUnitNearKnossos = true
                --    damage unit here
            end           
        end
    end
end
-------------------------------------------------------------------------------
--    Returns player ID of knossos owner.  also stores plot location of knossoss
--    in the global g_iKnossosBuildingPlot variable
-------------------------------------------------------------------------------
function GetKnossosPlayerID()
  
    for playerID = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
        local player = Players[playerID]           
        if (player:IsAlive()) then
            for city in player:Cities() do
                if city:IsHasBuilding(buildingKnossosID) then
                    g_iKnossosPlayerID = playerID
                    --    store the knossos building location in global variable
                    g_iKnossosBuildingPlot = city:Plot()
                    return g_iKnossosPlayerID
                end
            end
        end
    end
  
    return nil
end


-------------------------------------------------------------------------------
--    Checks if player is at war with knossos owner
-------------------------------------------------------------------------------
function IsPlayerAtWarWithKnossosOwner(playerID)
    local isAtWarWithKnossos = false
    if playerID ~= g_iKnossosPlayerID then
        local player = Players[playerID]
        local knossosPlayer = Players[g_iKnossosPlayerID]
        if Teams[player:GetTeam()]:IsAtWar(knossosPlayer:GetTeam()) then
            isAtWarWithKnossos = true
        end
    end
  
    return isAtWarWithKnossos
end

-------------------------------------------------------------------------------
--    If unit is within 2 plots of knossos return true
-------------------------------------------------------------------------------
function IsUnitPlotNearKnossos(plot)
  
    local plotDistance = Map.PlotDistance(plot.GetX(), plot.GetY(), g_iKnossosBuildingPlot.GetX(), g_iKnossosBuildingPlot.GetY())

    if plotDistance <= 2 then
        return true
    end
  
    return false
end

Would be keen to know if there is a better way of doing what I wrote above; this is my first shot at it and i'm a Lua noob :)
 
To Damage a Unit, you could use this (it also provides floaty red damage text automatically):
Code:
pUnit:ChangeDamage(iDamageAmount);
--==============
One issue I can see is that your code won't work if multiple Knossos's are constructed. This is fine if it's a Wonder (though that depends on who you'd ask :p), but if it's not you'll need to change things up.

The way JFD's Stalin mod works is that it grants a promotion to units in his territory. This promotion is similar to the 'Enemy Blade' promotion, in that it deals damage when ending your turn inside enemy territory. Depending on if you want enemies to only take damage in your territory (or neutral territory as there's also a column in the Promotions-table for that), then this approach could work as well.
(It also seems that JFDs stalin code will only work for the first stalin that is in play and alive. I.e. no multiple copies of the civ)

Here's the relevant bit of code:
Spoiler :

Code:
----------------------------------------------------------------------------------------------------------------------------
-- JFD_WarsawPact
----------------------------------------------------------------------------------------------------------------------------
local promotionWarsawPactID = GameInfoTypes["PROMOTION_JFD_WARSAW_PACT"]

function JFD_WarsawPact(playerID, unitID, unitX, unitY)
    local player = Players[playerID]
    if (player:IsAlive() and JFD_IsPlayerAtWarWithStalin(playerID)) then
        local isUnitInSovietTerritory = false
        local unit = player:GetUnitByID(unitID)
        local plot = unit:GetPlot()
        if (plot and (plot:GetOwner() == stalinPlayerID or JFD_IsPlotBelongToSovietUnion(plot))) then
            isUnitInSovietTerritory = true
        end
      
        unit:SetHasPromotion(promotionWarsawPactID, isUnitInSovietTerritory)
    end
end

if isUSSRStalinCivActive then
    GameEvents.UnitSetXY.Add(JFD_WarsawPact)
end


--==============

(I also suggest you only ask questions once/in one thread only, since that ensures that possible answers/replies are in a single place.)
 
To Damage a Unit, you could use this (it also provides floaty red damage text automatically):
Code:
pUnit:ChangeDamage(iDamageAmount);
--==============
One issue I can see is that your code won't work if multiple Knossos's are constructed. This is fine if it's a Wonder (though that depends on who you'd ask :p), but if it's not you'll need to change things up.

The way JFD's Stalin mod works is that it grants a promotion to units in his territory. This promotion is similar to the 'Enemy Blade' promotion, in that it deals damage when ending your turn inside enemy territory. Depending on if you want enemies to only take damage in your territory (or neutral territory as there's also a column in the Promotions-table for that), then this approach could work as well.
(It also seems that JFDs stalin code will only work for the first stalin that is in play and alive. I.e. no multiple copies of the civ)

Here's the relevant bit of code:
Spoiler :

Code:
----------------------------------------------------------------------------------------------------------------------------
-- JFD_WarsawPact
----------------------------------------------------------------------------------------------------------------------------
local promotionWarsawPactID = GameInfoTypes["PROMOTION_JFD_WARSAW_PACT"]

function JFD_WarsawPact(playerID, unitID, unitX, unitY)
    local player = Players[playerID]
    if (player:IsAlive() and JFD_IsPlayerAtWarWithStalin(playerID)) then
        local isUnitInSovietTerritory = false
        local unit = player:GetUnitByID(unitID)
        local plot = unit:GetPlot()
        if (plot and (plot:GetOwner() == stalinPlayerID or JFD_IsPlotBelongToSovietUnion(plot))) then
            isUnitInSovietTerritory = true
        end
 
        unit:SetHasPromotion(promotionWarsawPactID, isUnitInSovietTerritory)
    end
end

if isUSSRStalinCivActive then
    GameEvents.UnitSetXY.Add(JFD_WarsawPact)
end


--==============

(I also suggest you only ask questions once/in one thread only, since that ensures that possible answers/replies are in a single place.)
Thanks for the reply Troller0001 - yes my building is a wonder so the code should be fine. Thanks for pointing out that JFD used a promotion to cause damage to the units; I missed that. Talk about a workaround - am quite surprised there is not simple way to deduct hitpoints from a unit. But if the promotion route is what I have to take then, so be it - i'll adopt that apprach. I intend to set the damage to units if they are within 2 tiles of the city, regardless of which territory they are in. I hope the following apprach will work, assuming that the global variable g_iKnossosBuildingPlot is assinged the knossos plot:
Code:
-------------------------------------------------------------------------------
--    If unit is within 2 plots of knossos return true
-------------------------------------------------------------------------------
function IsUnitPlotNearKnossos(plot)
 
    local plotDistance = Map.PlotDistance(plot.GetX(), plot.GetY(), g_iKnossosBuildingPlot.GetX(), g_iKnossosBuildingPlot.GetY())

    if plotDistance <= 2 then
        return true
    end
 
    return false
end

I'll avoid double posting in the future, was just not sure if this post had become to convoluted for readers or not.
 
Last edited:
I think the reason JFD added the effect via a promotion was so that the damage would only be implemented if the unit ended it's next turn in the territory. So the unit could have a chance to move away on its next turn without being damaged, I think

You can directly change a unit's damage state through the method troller mentioned.
 
I think the reason JFD added the effect via a promotion was so that the damage would only be implemented if the unit ended it's next turn in the territory. So the unit could have a chance to move away on its next turn without being damaged, I think

You can directly change a unit's damage state through the method troller mentioned.
I have gone ahead and taken that approach - there was not much to it really, just add an entry in the promotions table. The code has yet to be tested however - one thing that I can forsee (I checked JFD's code for this as well) is that in the event that the unit has the promotion (or debuff) assinged to it and that peace is declared, there is no point in time where the promotion is removed. So my concern is that while the war is on, my code will work correctly, up until peace is declared at which point the promotion will still be there. I might have to create another event to remove all the promotions upon peace declared. Here is my updated code (yet to be tested), again comments are welcome :)


Code:
----------------------------------------------------------------------------------------------------------------------------
-- Knossos Damage Debuff
----------------------------------------------------------------------------------------------------------------------------
local debuffKnossosDamageID = GameInfoTypes["DEBUFF_KNOSSOS_DAMAGE"]
local buildingKnossosID        = GameInfoTypes["BUILDING_KNOSSOS"]

local g_iKnossosPlayerID = nil
local g_iKnossosBuildingPlot = nil


-------------------------------------------------------------------------------
--    Event function - fires every turn
-------------------------------------------------------------------------------
function  KnossosDamage(playerID)
    --    Also stores the plot location of knossos if it exists
    g_iKnossosPlayerID = GetKnossosPlayerID()

    if (g_iKnossosPlayerID == nil)
        return
    end

    local player = Players[playerID]

    if (player:IsAlive() and IsPlayerAtWarWithKnossosOwner(playerID)) then
        local bIsUnitNearKnossos = false

        local playerUnits = player:Units()

        --    loop thru players units and damage them if in vicinity to knossos
        for _, unit in ipairs(playerUnits) do
            local plot = unit:GetPlot()        
            if (plot and (IsUnitPlotNearKnossos(plot))) then
                bIsUnitNearKnossos = true
                --    damage unit here
            else
                bIsUnitNearKnossos = false
            end
            unit:SetHasPromotion(debuffKnossosDamageID, bIsUnitNearKnossos)    
        end
    end
end

GameEvents.PlayerDoTurn.Add( KnossosDamage)
GameEvents.UnitSetXY.Add( KnossosDamage)

-------------------------------------------------------------------------------
--    Returns player ID of knossos owner.  also stores plot location of knossoss
--    in the global g_iKnossosBuildingPlot variable
-------------------------------------------------------------------------------
function GetKnossosPlayerID()
 
    for playerID = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
        local player = Players[playerID]        
        if (player:IsAlive()) then
            for city in player:Cities() do
                if city:IsHasBuilding(buildingKnossosID) then
                    g_iKnossosPlayerID = playerID
                    --    store the knossos building location in global variable
                    g_iKnossosBuildingPlot = city:Plot()
                    return g_iKnossosPlayerID
                end
            end
        end
    end
 
    return nil
end

-------------------------------------------------------------------------------
--    Checks if player is at war with knossos owner
-------------------------------------------------------------------------------
function IsPlayerAtWarWithKnossosOwner(playerID)
    local bIsAtWarWithKnossos = false
    if playerID ~= g_iKnossosPlayerID then
        local player = Players[playerID]
        local knossosPlayer = Players[g_iKnossosPlayerID]
        if Teams[player:GetTeam()]:IsAtWar(knossosPlayer:GetTeam()) then
            bIsAtWarWithKnossos = true
        end
    end
 
    return bIsAtWarWithKnossos
end

-------------------------------------------------------------------------------
--    If unit is within 2 plots of knossos return true
-------------------------------------------------------------------------------
function IsUnitPlotNearKnossos(plot)
 
    local plotDistance = Map.PlotDistance(plot.GetX(), plot.GetY(), g_iKnossosBuildingPlot.GetX(), g_iKnossosBuildingPlot.GetY())

    if plotDistance <= 2 then
        return true
    end
 
    return false
end
 
Back
Top Bottom