[MOD] How do I remove 10 hit points from a unit?

Arkatakor

King
Joined
Mar 11, 2006
Messages
621
Location
Stockholm, Sweden
I have created some code in which I would like to damage a unit for 10 hit points. Having scoured the Civ5 Lua API I was unable to find out how to do so. See the comment in the code below 'damage unit here' deep in the KnossosDamage function. Would appreciate help on what function to call to remove 10 hitpoints from a unit, thanks!


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
 
i've used this in the past:
Code:
pUnit:SetDamage(iUnitDamage);

Where iUnitDamage is the amount of HP's you want to remove from that unit (IIRC).
 
SetDamage() makes the unit's total damage the amount specified
ChangeDamage() is the method needed to add more damage to a unit.

There is also a GetDamage() method that allows you to check the unit's current damage level (0 - 99) and then you can make your code "decide" whether to give more damage if the unit is already at greater than 90 damage, for example.

This should also work, if you aren't comfortable with the ChangeDamage() method:
Code:
unit:SetDamage(unit:GetDamage() + 10)
or
Code:
local iCurrentDamage = unit:GetDamage()
if iCurrentDamage < 90 then
    unit:SetDamage(iCurrentDamage + 10)
end
 
Last edited:
i've used this in the past:
Code:
pUnit:SetDamage(iUnitDamage);

Where iUnitDamage is the amount of HP's you want to remove from that unit (IIRC).
Thanks for that - much easier!

SetDamage() makes the unit's total damage the amount specified
ChangeDamage() is the method needed to add more damage to a unit.

There is also a GetDamage() method that allows you to check the unit's current damage level (0 - 99) and then you can make your code "decide" whether to give more damage if the unit is already at greater than 90 damage, for example.

This should also work, if you aren't comfortable with the ChangeDamage() method:
Code:
unit:SetDamage(unit:GetDamage() + 10)
or
Code:
local iCurrentDamage = unit:GetDamage()
if iCurrentDamage < 90 then
    unit:SetDamage(iCurrentDamage + 10)
end
Thanks for pointing this out. I had seen those functions while browsing the API but their names are very misleading which is why I didn't use them in the first place. They should be called "SetHitPoints" or "SetHealth" instead of SetDamage. I very much prefer using this method as opposed to a promotion.

I have another question while we are discussing this; I have put all the code in the OP exactly as is in a standalone Lua file (it has no other dependencies). After which I did the following:

1) Via "Content" in the project properties, I referenced that Lua file as an "InGameUIAddIn".
2) VFS for that Lua file is set to FALSE

Is the above setup correct? My code as is did not work, so my next step will be to put some print statements to figure out whats going on. I assume that the print statements would be readable in the Lua.log file?
 
Last edited:
See whoward69's what ModBuddy setting for what file types tutorial and this post within it: https://forums.civfanatics.com/thre...addin-or-updatedatabase.487846/#post-12212467

If you typed the name of the file manually you may have mismatched the filename or not correctly entered the file path

Otherwise your method for setting up the file is correct.

Yes, print commands will show in the lua.log. Make sure you have logging enabled.
Glad to know that I set that up correctly. I have now gone ahead and debugged the lua file (there were miscellaneous syntax errors and whatnot) and after about an hour of tinkering got it to work. Scripting is a chore though because in the absence of a compiler, syntax errors or uses of undeclared variables are not highighted from the beginning. Either way it works now and i'm very satisfied with the result, much thanks to you all.

The next question I have is how to create a prompt for the player that X unit has taken damage from from knossos (like players get prompted when units get damaged from other units). Anyone know how to do this? This will finalize my work on this building.
 
To make a message that scrolls up across the main screen is not terribly hard, Here's an example from a mod I never released where I am making extra effects when a unit is killed in combat based on the result of a random number. Variable iVictoriousPlayer in this example would be the Player ID# for a player who killed off another unit. Events.GameplayAlertMessage(Message) is what makes a main-screen "float-up" message occur:
Code:
function FindFirstCityWithoutBuilding(pPlayer, iBuildingToSeek)
	for pCity in pPlayer:Cities() do
		if not pCity:IsHasBuilding(iBuildingToSeek) then
			return pCity
		end
	end
	return nil
end
function UnitKilled(iVictoriousPlayer, iDefeatedPlayer, iDestroyedUnitType)
	if (Players[iVictoriousPlayer]:GetCivilizationType() == iRequiredCivilization) then
		local iUseChance, iUseMatch = iStandardChance, iStandardMatch
		if (iDefeatedPlayer == 63) then
			iUseChance, iUseMatch = iBarbChance, iBarbMatch
		end
		if (math.random(iUseChance) == iUseMatch) then
			local pCity = FindFirstCityWithoutBuilding(Players[iVictoriousPlayer], iTriumphalArch)
			if pCity ~= nil then
				pCity:SetNumRealBuilding(iTriumphalArch, 1)
				local Message = "In function UnitKilled: A Triumphal Arch was added to the city of " .. tostring(pCity:GetName())
				print(Message)
				if (Players[iVictoriousPlayer]:IsHuman()) then
					Events.GameplayAlertMessage(Message)
				end
			else
				local Message = "In function UnitKilled: 30 Great General Progress was added"
				print(Message)
				if (Players[iVictoriousPlayer]:IsHuman()) then
					Events.GameplayAlertMessage(Message)
				end
				Players[iVictoriousPlayer]:ChangeCombatExperience(30)
			end
		end
	end
end
GameEvents.UnitKilledInCombat.Add(UnitKilled)
Note that there are a few variables that are defined elsewhere in the file that I did not copy for this snippet example.

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

To make text float over a specific tile is far more complicated.
 
Thanks again for this, LeeS - Events.GameplayAlertMessage(Message) was what I was looking for. I will play around with it and might use some of your other code (I am thinking of introcuding a random death event when near my building, probably followed by a sound effect).
 
Back
Top Bottom