1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Need a check if in friendly territory

Discussion in 'Mod Creation Help' started by Bluur, Dec 2, 2019 at 8:22 AM.

  1. Bluur

    Bluur Chieftain

    Joined:
    Monday
    Messages:
    7
    Gender:
    Male
    Hello,

    New to modding, but not scripting/coding so any help is appreciated. I am making a mod that I would like to have a check if a unit is in friendly territory before executing the new function. There has to be a built in check for this for unit healing but I cant find it.


    Best regards,
    Bluur
     
  2. Laurana Kanan

    Laurana Kanan Don’t underestimate who I am.

    Joined:
    Apr 10, 2014
    Messages:
    1,871
    Gender:
    Female
    Location:
    Near the Greatest Snow on Earth
    What you want is the RequirementID "IS_FRIENDLY_TERRITORY_REQUIREMENT", which is attached to the RequirementType "REQUIREMENT_PLOT_ADJACENT_FRIENDLY_TERRITORY" and has the RequirementArguments MinRange & MaxRange set to 0.
     
  3. Bluur

    Bluur Chieftain

    Joined:
    Monday
    Messages:
    7
    Gender:
    Male
    Cool, thank you! What do the MinRange and MaxRange correspond to? Do these values change or is there a value that defines friendly vs. not friendly?
     
  4. Laurana Kanan

    Laurana Kanan Don’t underestimate who I am.

    Joined:
    Apr 10, 2014
    Messages:
    1,871
    Gender:
    Female
    Location:
    Near the Greatest Snow on Earth
    MinRange & MaxRange refer to the distance that is checked via the requirement to apply a modifier. In this instance, when both are set to 0, the modifier will only apply to units if they are standing ON a friendly territory plot.

    Another RequirementID "NEAR_FRIENDLY_TERRITORY" keeps the MinRange at 0, and the MaxRange set to 4, so the modifier applies only to units if they are standing ON a friendly territory plot OR within 4 tiles of a friendly territory plot.

    To designate a requirement for unfriendly territory, you'd need to set the "Inverse" column of the requirement to "True" or 1 in SQL.

    FYI, there is also, a RequirementID "UNIT_IN_OWNER_TERRITORY_REQUIREMENT", that only checks if the unit is in your territory, not other friendly civ territories. This requirement has no RequirementArguments (i.e. MinRange/MaxRange) defined as is, but can be added if you set up your own requirement.
     
  5. Bluur

    Bluur Chieftain

    Joined:
    Monday
    Messages:
    7
    Gender:
    Male
    If I understand correctly than the code below may work?

    Code:
        if IS_FRIENDLY_TERRITORY_REQUIREMENT="TRUE" then
    EXECUTE FUNCTION
    else break
     
  6. Laurana Kanan

    Laurana Kanan Don’t underestimate who I am.

    Joined:
    Apr 10, 2014
    Messages:
    1,871
    Gender:
    Female
    Location:
    Near the Greatest Snow on Earth
    I'm really limited in my knowledge of Lua, so I can't tell you how that would work if at all. You certainly couldn't use a Requirement as part of a Lua statement. You might need to wait for someone who is more versed in that language.
     
    Last edited: Dec 3, 2019 at 6:30 AM
  7. Infixo

    Infixo Deity

    Joined:
    Jan 9, 2016
    Messages:
    3,417
    Gender:
    Male
    Location:
    Warsaw
    Requirements are part of Modifiers subsystem, which is used only in DB via SQL definitions. You cannot use it Lua.

    In Lua, you need to:
    - get unit's owner - GetOwner()
    - get unit's plot or location - GetLocation() or GetX() / GetY() or GetPlotID() and convert it into a plot
    - get plot's owner - GetOwner()
    - check the diplo relation between the unit's owner and plot's owner - several functions in Player:GetDiplomacy()

    There is no "check if unit is in a friendly territory" in Lua.

    You may also check out the scenarios - chances are that similar functionality has been implemented by Firaxis somwhere.
     
  8. Bluur

    Bluur Chieftain

    Joined:
    Monday
    Messages:
    7
    Gender:
    Male
    I apologize for my naivety, but what do you mean by scenarios?
     
  9. Infixo

    Infixo Deity

    Joined:
    Jan 9, 2016
    Messages:
    3,417
    Gender:
    Male
    Location:
    Warsaw
  10. Bluur

    Bluur Chieftain

    Joined:
    Monday
    Messages:
    7
    Gender:
    Male
    Disclaimer - I am trying to make a mod using the mod Passable mountains + static and moving attrition by Cromcrom as a base.

    So i did some research and came up with this,

    Code:
    local pPlot = Unit:GetPlot()
    local iPlotOwner = pPlot:GetOwner()
    local pOtherPlayer
    if iPlotOwner = -1 then
        pOtherPlayer = Players[iPlotOwner]   
        Events.TurnEnd.Add(CheckIdleDamage)
        Events.UnitMoved.Add(Attrition)       
    else Break
    end
    The events work fine to apply damage to the units based on terrain without the if loop.

    The Lua log gives the following error:

    Runtime Error: C:\Users\John\Documents\My Games\Sid Meier's Civilization VI\Mods\Attrition\Rigale_StartScript.lua:227: attempt to index a nil value
    stack traceback:
    C:\Users\John\Documents\My Games\Sid Meier's Civilization VI\Mods\Attrition\Rigale_StartScript.lua:227: in function '(main chunk)'
    [C]: in function '(anonymous)'
    Lua callstack:
    Error loading file where=, file=C:\Users\John\Documents\My Games\Sid Meier's Civilization VI\Mods\Attrition\Rigale_StartScript.lua
     
  11. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,332
    Location:
    Illinois, USA
    You have multiple concept and syntax errors.
    1. These lines result in values of "nil" being assigned to the two variables "pPlot" and "iPlotOwner"
      Code:
      local pPlot = Unit:GetPlot()
      local iPlotOwner = pPlot:GetOwner()
      This is because:

      (a) Unless the Unit-Object "Unit" has been defined already elsewhere within the same script, and that definition has already executed and is still valid when your sample chunk is run, "Unit" is an undefined variable at the time the code executes
      (b) "GetPlot()" is not a valid lua method for unit-objects in Civilization 6
    2. This is incorrect syntax for all versions of lua, regardless of whether used within a Civilization API or some other application of lua like a website interface were lua being used for that:
      Code:
      if iPlotOwner = -1 then
      lua uses "==" in a check for equality and "=" to make one value equal to another

      The proper method to make an equality check as you are attempting is
      Code:
      if iPlotOwner == -1 then
      This will not solve your conceptual problem however since a value of "nil" will never be equal to "-1"
    3. "break" is never capitalized
      • "break" is an lua "command-word"
      • "Break" will be seen as being an undefined variable with a value of "nil"
      In lua Capitalization matters.
    4. A code of Players[-1] is never valid under any conditions and will simply result in a value of "nil". This is exactly what will happen were your code to actually be able to run, since you are only executing the following line when variable "iPlotOwner" has a value of "-1"
      Code:
      pOtherPlayer = Players[iPlotOwner]
    5. Further, this line is completely un-necessary:
      Code:
      else Break
      • A conditional evaluation chunk never uses a "break" command
      • The lua "command-word" if initiates a conditional evaluation chunk
      • While "else" is valid syntax within a conditional evaluation chunk for what lua ought to do when the evaluation is not met, in the case of sample code you provided it is not necessary nor really desirable. You have nothing "else" for the code-chunk to do, so an "else" clause is unnecessary
    6. Conceptual and other syntax issues to one side for a moment, the correct syntax without unnecessary extras for your conditional evaluation chunk would be:
      Code:
      if iPlotOwner == -1 then
          pOtherPlayer = Players[iPlotOwner]   
          Events.TurnEnd.Add(CheckIdleDamage)
          Events.UnitMoved.Add(Attrition)       
      end
      In all cases when variable iPlotOwner is not equal to "-1" the code would not execute anything. The effect is the same as how you were attempting to structure an "else" clause, but as already mentioned in this particular example of code no "else" condition is needed.
    7. Unless functions "CheckIdleDamage" and "Attrition" are defined elsewhere within the same script you will get nil value errors for attempting to attach a nil value as a listener to a game "event-hook"
     
    Last edited: Dec 6, 2019 at 8:26 PM
  12. Bluur

    Bluur Chieftain

    Joined:
    Monday
    Messages:
    7
    Gender:
    Male
    Thanks for taking the time to break this down! I understand the syntax errors and I now see that the example code I used for this was from the Civ 5 forum so the methods may not be usable CIv 6. I did some more playing around and came up with the code below. This is a function that deals damage with input from some other functions and works fine without my plot owner checking. I am only trying to apply damage to units outside my own territory for right now.

    I thought I could get the player ID using UnitPlotOwner = UnitPlot:GetOwner, but it returns -1 even if inside my territory.

    Code:
    function damageAndDestroyUnit(pPlayer,iUnit,minDam,maxDam)
        local tKillUnits = {}
        local pPlayerToCheck = Players[pPlayer]
        local pUnits = pPlayerToCheck:GetUnits()   
        for i, pUnit in pUnits:Members(i) do
            --Get plots unit is occupying
            local UnitPlot = Map.GetPlot(pUnit:GetLocation())
            --Get owner of plot unit is occupying
            local UnitPlotOwner = UnitPlot:GetOwner()
            --Check if plot is owned by player
            if UnitPlotOwner ~= 0 then
                print("Plot owner "..UnitPlotOwner.." of unit")       
                if(tostring(pUnit:GetID()) == tostring(iUnit)) then
                    local unitDamage = math.random(minDam,maxDam)
                    if (pUnit:GetDamage() + unitDamage) >= 100 then
                        table.insert(tKillUnits, pUnit)
                    else
                        pUnit:ChangeDamage(unitDamage)
                        print("inflicted "..unitDamage.." to unit")
                    end
                end
            end
        end
        --now we've completed the iteration through table pUnits:Members() and can safely delete units listed as members
        for k,pUnit in pairs(tKillUnits) do
            pUnits:Destroy(pUnit)
            print("a unit was destroyed because of attrition")
        end           
    end
     
  13. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,332
    Location:
    Illinois, USA
    1. Are you passing a Player ID # for argument "pPlayer" to function "damageAndDestroyUnit()" ?
    2. You don't need the "i" argument in "pUnits:Members(i)"
    3. Units that are dead (and therefore have no valid location) are still listed as items in "pUnits:Members()"
    4. When the code executes properly for retrieving the owner ID# of the plot, "0" does not indicate no one. "-1" indicates no player owns the plot. "0" is the player ID # assigned to the human player in Single Player games and as I recall in Multiplayer Games will be the host human player.
    5. Since table "tKillUnits" will be entirely an array table when the "killing off" iteration is performed, you are better off using "ipairs" instead of "pairs". This simple alteration will be insurance against de-synch between different participants in a multiplayer game if the code is ever run in a multiplayer game.
    6. If you are passing a unit ID # as argument "iUnit" to function damageAndDestroyUnit it does not really make any sense to iterate through the entire table of the player's units.
    7. All you need to do to get the Unit-Object when you have only a unit ID # is to code as
      Code:
      local pUnit = Players[PlayerID]:GetUnits():FindID(iUnitID)
      Or, as it would be in your function:
      Code:
      local pPlayerToCheck = Players[pPlayer]
      local pUnits = pPlayerToCheck:GetUnits()   
      local pUnit = pUnits:FindID(iUnit)
    8. If "iUnit" is already being passed as the unit-object to function damageAndDestroyUnit then there would be no need to "grab" the unit-object: it will already be available as an inherent variable usable in the function
    9. Here is a quick function to check if the Unit is "dead":
      Code:
      function UnitIsDead(pUnit)
      	if pUnit then
      		if pUnit:IsDead() or pUnit:IsDelayedDeath() then
      			return true
      		end
      	else
      		--the pUnit data is nil, assume the unit is ded
      		return true
      	end
      	--safety valve point: assume the unit is NOT ded if we get here
      	return false
      end
    10. I always use the following method to get a unit's plot as a plot-object that can be used with plot API "methods":
      Code:
      local iUnitX, iUnitY = pUnit:GetX(), pUnit:GetY()
      local pPlot = Map.GetPlot(iUnitX, iUnitY)
      I've never had luck with the GetLocation() method. I cannot now remember whether the GetLocation() method is only valid in a UI script which your code is most definitely not.
    11. If your intention is to create attrition on only a specific unit via your function damageAndDestroyUnit and arguments "pPlayer" and "iUnit" are being sent ID #s for the player and the unit, you can simplify the code to
      Code:
      function damageAndDestroyUnit(pPlayer,iUnit,minDam,maxDam)
      	local pPlayerToCheck = Players[pPlayer]
      	local pUnits = pPlayerToCheck:GetUnits()   
      	local pUnit = pUnits:FindID(iUnit)
      	if (pUnit ~= nil) and (pUnit:IsDead() == false) and (pUnit:IsDelayedDeath() == false) then
      		local UnitPlot = Map.GetPlot(pUnit:GetX(), pUnit:GetY())
      		if (UnitPlot ~= nil) and (UnitPlot:GetOwner() ~= pPlayer) then
      			local unitDamage = math.random(minDam,maxDam)
      			if (pUnit:GetDamage() + unitDamage) >= 100 then
      				pUnits:Destroy(pUnit)
      				print("a unit was destroyed because of attrition")
      			else
      				pUnit:ChangeDamage(unitDamage)
      				print("inflicted "..unitDamage.." to unit")
      			end
      		end
      	end
      end
    12. If your intention is to create attrition on all the player's units via one execution of function damageAndDestroyUnit then you really don't even need to pass the "iUnit" argument. You only need the other three arguments. And in this case you would execute through the list of the player's units via the "pUnits:Members()" method, check that each of these units is not already "dead" or giving a nil value, and then grab the plot object for that unit, etc. You would want to use the "tKillUnits" method as well as you never want to kill or otherwise remove an item in the Members() list while still iterating through the lust of Members().
     
  14. Bluur

    Bluur Chieftain

    Joined:
    Monday
    Messages:
    7
    Gender:
    Male
    I may have bitten off more than I can chew for my first mod, so I appreciate all your help - a good learning experience nonetheless.

    I implemented your suggestions and it now works!!! - damage only gets applied outside my own territory. Are there other ways I can make this code more robust or efficient? I pasted the entire code this time with all the functions.

    My next task will be to include a check for diplomatic relations - i.e. no damage applied in friendly territory and healing in allied territory. I have tried using Player:GetDiplomacy() as suggested previously by Infixo but with no success.



    Code:
    -- set base values for variables
    local BaseAttritionMin,BaseAttritionMax = 1,6
    local TerrainAttritionMul,CataChance,TerrainCataMul = 1,10,2
    local TerrainAttritionMulLow,TerrainAttritionMulBelowAverage,TerrainAttritionMulAverage,TerrainAttritionMulStrong,TerrainAttritionMulHuge = 0.5,2,3,4,8
    local CataChanceLow,CataChanceAverage,CataChanceAboveAverage,CataChanceStrong,CataChanceHuge = 70,150,250,350,600
    local TerrainCataMulLow,TerrainCataMulBelowAverage,TerrainCataMulAverage,TerrainCataMulStrong,TerrainCataMulHuge = 2,3,5,8,11
    
    
    
    function damageAndDestroyUnit(pPlayer,iUnit,minDam,maxDam)
        local tKillUnits = {}
        local pPlayerToCheck = Players[pPlayer]
        local pUnits = pPlayerToCheck:GetUnits()
        local pUnit = pUnits:FindID(iUnit)
        if (pUnit ~= nil) and (pUnit:IsDead() == false) and (pUnit:IsDelayedDeath() == false) then
            local UnitPlot = Map.GetPlot(pUnit:GetX(), pUnit:GetY())
            local UnitPlotOwner = UnitPlot:GetOwner()
            if (UnitPlot ~= nil) and (UnitPlotOwner ~= pPlayer) then
                local unitDamage = math.random(minDam,maxDam)
                if (pUnit:GetDamage() + unitDamage) >= 100 then
                    table.insert(tKillUnits, pUnit)
                else
                    pUnit:ChangeDamage(unitDamage)
                    print("inflicted "..unitDamage.." to unit")
                    print("Player is "..UnitPlotOwner.." ID")
                   
                end
            end
        end
        --now we've completed the iteration through table pUnits:Members() and can safely delete units listed as members
        for k,pUnit in ipairs(tKillUnits) do
            pUnits:Destroy(pUnit)
            print("a unit was destroyed because of attrition")
        end           
    end
    
    
    function getDamageFromTerrains(tTerrain)
        if tTerrain == "TERRAIN_GRASS" then 
            TerrainAttritionMul = TerrainAttritionMulLow
            CataChance = CataChanceLow
            TerrainCataMul = TerrainCataMulLow
        elseif tTerrain == "TERRAIN_GRASS_HILLS" then 
            TerrainAttritionMul = TerrainAttritionMulLow
            CataChance = CataChanceAverage
            TerrainCataMul = TerrainCataMulLow
        elseif tTerrain == "TERRAIN_PLAINS" then 
            TerrainAttritionMul = TerrainAttritionMulLow
            CataChance = CataChanceLow
            TerrainCataMul = TerrainCataMulLow
        elseif tTerrain == "TERRAIN_PLAINS_HILLS" then 
            TerrainAttritionMul = TerrainAttritionMulLow
            CataChance = CataChanceAverage
            TerrainCataMul = TerrainCataMulLow
       
       
       
        elseif tTerrain == "TERRAIN_DESERT" then 
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceAverage
            TerrainCataMul = TerrainCataMulAverage
        elseif tTerrain == "TERRAIN_DESERT_HILLS" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceStrong
            TerrainCataMul = TerrainCataMulAverage
        elseif tTerrain == "TERRAIN_DESERT_MOUNTAIN" then
            TerrainAttritionMul = TerrainAttritionMulStrong
            CataChance = CataChanceStrong
            TerrainCataMul = TerrainCataMulStrong   
        elseif tTerrain == "TERRAIN_GRASS_MOUNTAIN" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceStrong
            TerrainCataMul = TerrainCataMulAverage
        elseif tTerrain == "TERRAIN_PLAINS_MOUNTAIN" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceStrong
            TerrainCataMul = TerrainCataMulAverage   
        elseif tTerrain == "TERRAIN_TUNDRA" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceAverage
            TerrainCataMul = TerrainCataMulBelowAverage   
        elseif tTerrain == "TERRAIN_TUNDRA_HILLS" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceAboveAverage
            TerrainCataMul = TerrainCataMulBelowAverage
        elseif tTerrain == "TERRAIN_TUNDRA_MOUNTAIN" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceHuge
            TerrainCataMul = TerrainCataMulStrong   
        elseif tTerrain == "TERRAIN_SNOW" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceAverage
            TerrainCataMul = TerrainCataMulAverage
        elseif tTerrain == "TERRAIN_SNOW_HILLS" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceStrong
            TerrainCataMul = TerrainCataMulAverage
        elseif tTerrain == "TERRAIN_SNOW_MOUNTAIN" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceHuge
            TerrainCataMul = TerrainCataMulHuge
        elseif tTerrain == "TERRAIN_COAST" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceStrong
            TerrainCataMul = TerrainCataMulAverage
        elseif tTerrain == "TERRAIN_OCEAN" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceHuge
            TerrainCataMul = TerrainCataMulAverage   
        end
     return TerrainAttritionMul,CataChance,TerrainCataMul
    end
    
    function getDamageFromFeatures(plotFeature)
        if plotFeature == "FEATURE_ICE" then
            TerrainAttritionMul = TerrainAttritionMulBelowAverage
            CataChance = CataChanceHuge
            TerrainCataMul = TerrainCataMulAverage
        end
        if plotFeature == "FEATURE_JUNGLE" then
            TerrainAttritionMul = TerrainAttritionMulAverage
            CataChance = CataChanceAverage
            TerrainCataMul = TerrainCataMulAverage       
        end
        if plotFeature == "FEATURE_MARSH" then
            TerrainAttritionMul = TerrainAttritionMulStrong
            CataChance = CataChanceHuge
            TerrainCataMul = TerrainCataMulAverage
        end
        if plotFeature == "FEATURE_OASIS" then
            TerrainAttritionMul = TerrainAttritionMulLow
            CataChance = CataChanceAverage
            TerrainCataMul = TerrainCataMulAverage   
        end
        return TerrainAttritionMul,CataChance,TerrainCataMul
    end
    
    
    function Attrition(PlayerID,UnitID,UnitX,UnitY)
        local experienceFromCata = false
        local feedBack = false
       
        -- give feedback only to player
        if PlayerID == 0 then feedBack = true end
       
        -- set values for terrains
        local plotToCheck = Map.GetPlot(UnitX,UnitY)
        local tTerrain = GameInfo.Terrains[plotToCheck:GetTerrainType()].TerrainType
    
        TerrainAttritionMul,CataChance,TerrainCataMul = getDamageFromTerrains(tTerrain)   
    
        -- check features. Takes precedence on terrain.
        if plotToCheck:GetFeatureType() > -1 then
            local plotFeature = GameInfo.Features[plotToCheck:GetFeatureType()].FeatureType   
            TerrainAttritionMul,CataChance,TerrainCataMul = getDamageFromFeatures(plotFeature)
        end
    
        -- check cata chance
        if math.random(1,1000) <= CataChance then
            experienceFromCata = true
            TerrainAttritionMul = TerrainCataMul
            if feedBack then
                Game.AddWorldViewText(0, "A catastrophe happened to this unit while moving", UnitX, UnitY, 0)
            end
        end
        -- reduce attrition and cata chance if road on plot.
        if plotToCheck:GetRouteType() > -1 then
            print("There is a route here, so much less attrition.")
            CataChance = CataChance/5
            TerrainCataMul = 1
            TerrainAttritionMul = TerrainAttritionMul*0.2
        end   
       
    -- check chance to create a road when a unit moves
        local roadCheckAllowed = true
        if tTerrain == "TERRAIN_COAST" or "TERRAIN_OCEAN" then
            roadCheckAllowed = false
        end
        if roadCheckAllowed then
            local chanceToCreateRoad = math.random(1,1000)
            if chanceToCreateRoad <= 75 then
                if feedBack then
                    Game.AddWorldViewText(0, "Your unit found and marked some easy path while moving here.", UnitX, UnitY, 0)
                end       
                WorldBuilder.MapManager():SetRouteType( plotToCheck,GameInfo.Routes["ROUTE_ANCIENT_ROAD"].Index, false )
            end
        end
        -- deal damages   
        local FinalAttritionMin = BaseAttritionMin*TerrainAttritionMul
        local FinalAttritionMax = BaseAttritionMax*TerrainAttritionMul   
       
        damageAndDestroyUnit(PlayerID,UnitID,FinalAttritionMin,FinalAttritionMax)
       
        -- give XP if a cata happens.    
        if experienceFromCata then
            local pPlayerToCheck = Players[PlayerID]
            local pUnits = pPlayerToCheck:GetUnits()
            for i, pUnit in pUnits:Members() do
                if(tostring(pUnit:GetID()) == tostring(UnitID)) then
                    local currentXP = pUnit:GetExperience()
                    local currentXPPoints = currentXP:GetExperiencePoints()+5
                    --currentXPPoints = currentXPPoints+5
                    local experienceChangeAmount = math.random((FinalAttritionMin/10)+1,(FinalAttritionMax/10)+1)
                    currentXP:ChangeExperience(experienceChangeAmount)
                end
            end
        end       
    end
    
    function CheckIdleDamage()
        local players = Game.GetPlayers()
        for k, pPlayer in ipairs (players) do
            local pUnits = pPlayer:GetUnits()
            for i, pUnit in pUnits:Members() do
                local iUnitX, iUnitY = pUnit:GetX(), pUnit:GetY()
                local pPlot = Map.GetPlot(iUnitX, iUnitY)   
                local tTerrain = GameInfo.Terrains[pPlot:GetTerrainType()].TerrainType
                TerrainAttritionMul,CataChance,TerrainCataMul = getDamageFromTerrains(tTerrain)
                if pPlot:GetFeatureType() >-1 then 
                    local fFeature = GameInfo.Features[pPlot:GetFeatureType()].FeatureType            
                    TerrainAttritionMul,CataChance,TerrainCataMul = getDamageFromFeatures(fFeature)
                end
                local PlayerID = pPlayer:GetID()
                local UnitID = pUnit:GetID()
                local FinalAttritionMin = (BaseAttritionMin*TerrainAttritionMul)/2
                local FinalAttritionMax = (BaseAttritionMax*TerrainAttritionMul)/2   
                damageAndDestroyUnit(PlayerID,UnitID,FinalAttritionMin,FinalAttritionMax)
            end
        end
    end
    
    Events.TurnEnd.Add(CheckIdleDamage)
    Events.UnitMoved.Add(Attrition)
    
    
     
  15. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,332
    Location:
    Illinois, USA
    I have not looked through your entire code as yet, and may not get an opportunity to do so for a couple of days at least, but this stands out to me:
    Code:
    RouteBuilder.SetRouteType(pPlot, iRouteType)
    There's no real need to use the WorldBuilder version, although I would not think it would really hurt anything to do so.

    "iRouteType" in the example above will be the "Index" value for the proper kind of route to add to the plot.

    -----------------------------------------------------------------------------------------------------------------------------------------------------------

    Also, investigate lua tables. You can get rid of the need for "getDamageFromTerrains(tTerrain)" by using an lua table.
    Code:
    local TerrainDataTable = { [GameInfo.Terrains["TERRAIN_GRASS"].Index] = {  TerrainAttritionMul = TerrainAttritionMulLow, CataChance = CataChanceLow, TerrainCataMul = TerrainCataMulLow } ,
    [GameInfo.Terrains["TERRAIN_GRASS_HILLS"].Index] = {  TerrainAttritionMul = TerrainAttritionMulLow, CataChance = CataChanceAverage, TerrainCataMul = TerrainCataMulLow }  ,
    ….etc for the other terrain-types..... }
    Then as needed the data can be pulled directly from the table:
    Code:
    
    
    function Attrition(PlayerID,UnitID,UnitX,UnitY)
        local experienceFromCata = false
        local feedBack = false
       
        -- give feedback only to player
        if PlayerID == 0 then feedBack = true end
       
        -- set values for terrains
        local plotToCheck = Map.GetPlot(UnitX,UnitY)
        local PlotTerrainDamages = TerrainDataTable[plotToCheck:GetTerrainType()]
    
        TerrainAttritionMul,CataChance,TerrainCataMul = PlotTerrainDamages.TerrainAttritionMul,  PlotTerrainDamages.CataChance,  PlotTerrainDamages.TerrainCataMul
    
      ...etc...…
    Your code will be somewhat reduced in length in terms of the amount of code you need to create, but also since you are directly-extracting the data back out of the table, there is no need for the lua-script to evaluate multiple "elseif" clauses.

    If you later decide that TerrainType "X" should not cause any ill effects, you can simply eliminate that Terrain's info from the lua table. Your code then changes slightly here
    Code:
    function Attrition(PlayerID,UnitID,UnitX,UnitY)
        local experienceFromCata = false
        local feedBack = false
       
        -- give feedback only to player
        if PlayerID == 0 then feedBack = true end
       
        -- set values for terrains
        local plotToCheck = Map.GetPlot(UnitX,UnitY)
         local TerrainAttritionMul,CataChance,TerrainCataMul = 0, 0, 0 --set to zero here to initialize and avoid scoping issues with data alterations within sub-chunks of the function
        if TerrainDataTable[plotToCheck:GetTerrainType()] then
             local PlotTerrainDamages = TerrainDataTable[plotToCheck:GetTerrainType()]
            TerrainAttritionMul,CataChance,TerrainCataMul = PlotTerrainDamages.TerrainAttritionMul,  PlotTerrainDamages.CataChance,  PlotTerrainDamages.TerrainCataMul
         end
      ...etc...…
    No other changes will ever be needed to the code in such a case as far as grabbing the needed damage effect is concerned. Changes to the amounts of damage and the chances are made in the lua-table (including any eliminations of terrains that you later may decide can be considered "safe" for balance purposes). This is referred to as Data-Driven Code Techniques.
     
    Last edited: Dec 8, 2019 at 1:06 PM

Share This Page