Jump to content

Recommended Posts

Heyo, so I'm working on a character and was having some trouble implementing a few things. First off Is it possible to give an inherent physical defense multiplier? I know there's inst.components.health:SetAbsorptionAmount() But that applies to all sources of damage. Additionally is it possible to give a physical poison immunity like the seashell suit and horned helmet to a character? If anybody can help, let me know, thanks!

Well, there's no descriptor on the damage about whether or not it is physical or meta-physical (joke, shadow creatures) or fire. If we look at how the code works, we can see that there are only a few things which can impact how much damage an attacker deals to you. This is the game code:

if self.inst.components.health ~= nil and damage ~= nil and damageredirecttarget == nil then
	if self.inst.components.inventory ~= nil then
		damage = self.inst.components.inventory:ApplyDamage(damage, attacker, weapon)
	end
	damage = damage * self.externaldamagetakenmultipliers:Get()
	if damage > 0 and not self.inst.components.health:IsInvincible() then
		--Bonus damage only applies after unabsorbed damage gets through your armor
		if attacker ~= nil and attacker.components.combat ~= nil and attacker.components.combat.bonusdamagefn ~= nil then
			damage = damage + attacker.components.combat.bonusdamagefn(attacker, self.inst, damage, weapon) or 0
		end

		local cause = attacker == self.inst and weapon or attacker
		--V2C: guess we should try not to crash old mods that overwrote the health component
		damageresolved = self.inst.components.health:DoDelta(-damage, nil, cause ~= nil and (cause.nameoverride or cause.prefab) or "NIL", nil, cause)
		damageresolved = damageresolved ~= nil and -damageresolved or damage
		if self.inst.components.health:IsDead() then
			if attacker ~= nil then
				attacker:PushEvent("killed", { victim = self.inst })
			end
			if self.onkilledbyother ~= nil then
				self.onkilledbyother(self.inst, attacker)
			end
		end
	else
		blocked = true
	end
end

The things affecting the taken damage are:

  • The ApplyDamage function on your inventory, which apply the absorption rates and resistances on your equipped items.
  • Any externaldamagemultipliers in your combat component, which affect any damage taken
  • Bonus damage of the attacker
  • The DoDelta function of your health component, which in turn uses its SetVal function whose job it is to pass on the cause of death if the damage was a killing blow and enforce e.g. a minimum health restriction keeping this particular character from dying and in general clamp the health value being set.

That's it. I don't see any other way of doing what you want to do, than to roll your own, or hook into something e.g. how armors figure out whether they should absorb the damage, and relay that through the inventory when it checks the equipped items.

The inventory has its ApplyDamage function, and it does this:

function Inventory:ApplyDamage(damage, attacker, weapon)
    --check resistance and specialised armor
    local absorbers = {}
    for k, v in pairs(self.equipslots) do
        if v.components.resistance ~= nil and
            v.components.resistance:HasResistance(attacker, weapon) and
            v.components.resistance:ShouldResistDamage() then
            v.components.resistance:ResistDamage(damage)
            return 0
        elseif v.components.armor ~= nil then
            absorbers[v.components.armor] = v.components.armor:GetAbsorption(attacker, weapon)
        end
    end
blablabla more code

If any of the equipped items have the resistance component, and that component has been set up to resist this particular attacker or weapon, they make the damage 0 instantly! Otherwise, they gather up the absorber items in a dictionary with the component as key and the absorption value calculated by the GetAbsorption function on the armor component.

GetAbsorption is pretty simple:

function Armor:GetAbsorption(attacker, weapon)
    return self:CanResist(attacker, weapon) and self.absorb_percent or nil
end

The armor component has this function, CanResist, which GetAbsorption calls, and if that function returns true, then GetAbsorption returns the armor component's absorption value e.g. 0.6 (60%).

function Armor:CanResist(attacker, weapon)
    if self.tags == nil then
        return true
    elseif attacker ~= nil then
        for i, v in ipairs(self.tags) do
            if attacker:HasTag(v) or (weapon ~= nil and weapon:HasTag(v)) then
                return true
            end
        end
    end
    return false
end

CanResist basically just says, that if the attacker or the weapon have any of the tags that are on the armor (self.tags), then the armor resists them. If the armor has the "sharp" tag, then its absorption applies to this damage. Notice that it returns true as soon as a matching tag is found.

So, what I would do, is that I would contemplate exactly which weapons or attackers I would want my damage reduction perk to apply to, and then try to write something similar. Then you can override combat:GetAttacked and alter the damage with your own code, before the damage is even assessed by the original code.

I couldn't help it, so I wrote it:

-- This first part goes in your character LUA, ABOVE master_postinit() or fn()
local attackerPrefabsToResist = {"crawlinghorror", "terrorbeak", "crawlingnightmare", "nightmarebeak"}
local weaponTagsToResist = { "sharp", "blunt" }
local weaponPrefabsToResist = {"spear", "nightsword"}

local function contains(mytable, value)
	if mytable and value then
		for _, v in ipairs(mytable) do
			if v == value then
				return true
			end
		end
	end
	return false
end

local function tableContainsOneOf(mytable, values)
	if mytable and values then
		for _, v in ipairs(values) do
			if contains(mytable, v) then
				return true
			end
		end
	end
	return false
end

local function myDamageResistance(attacker, damage, weapon)
	if (attacker and contains(attackerPrefabsToResist, attacker.prefab))
		or (weapon and (contains(weaponPrefabsToResist, weapon.prefab) or tableContainsOneOf(weaponTagsToResist, weapon.tags)))
		then
			-- subtract some fixed value
			damage = damage - 2
			-- or apply a multiplier
			damage = damage * (1 - multiplier) -- where multiplier is e.g. 0.4 to apply 40% damage reduction
			if damage < 0 then
				damage = 0
			end
		end
	end
end

-- The following goes IN your character LUA master_postinit() or fn()
local orig_GetAttacked = inst.components.combat.GetAttacked
inst.components.combat:GetAttacked = function(attacker, damage, weapon, stimuli, ...)
	damage = myDamageResistance(attacker, damage, weapon)
	orig_GetAttacked(self, attacker, damage, weapon, stimuli, ...)
end

 

You can write it in another way, if you want to apply different modifiers per weapon tag or weapon prefab or attacker prefab, but this will take care of your original question, with some work with defining the lists.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
  • Create New...