Jump to content

Recommended Posts

Hello! I'm trying to make a body armor that will absorb 0% of all damage dealt to the player (but the armor's durability will lower based on the damage dealt by the enemy) and will make the player lose 20 health (with the additional damage dealt by the enemy).

Why do I want a painful armor like that? Well I made it so that equipping said armor will make the player deal 50% more damage, but the cost is that the player should use this if they're confident they can kite well. (This code for the extra damage has already been done)

Can anyone help me?

Thanks for reading,

-mathem99

Edited by mathem99

Unfortunately there isn't an easy way to do this built in to the armor component. The way I would do this is listen for the armor's wearer's Health Delta's and the event armordamaged, and damage the armor by the amount of health lost if the armor should have been hit, as well as damaging the wearer for 20. As long as the armor itself is set to 0% protection this should work fine theoretically.

 

I'm at work at the moment but I can help with this code if someone else hasn't already by the time I'm home.

Edited by TheSkylarr
  • Big Ups 1

Alright, so scratch what I commented before. The better way to do this would be to set your armor to be 100% absorption. This may sound wrong, but hear out. We can use the armordamaged event from the armor component to see how much damage to armor takes. Since the armor is taking 100% of the damage instead of the player, we can take the amount of damage the armor just took, subtract another 20, then hit the player for that much with a health delta.

That should leave the player damaged for 20 + the amount they would have been damaged anyway, on top of damaging the armor for only the normal hit automatically.

So, in your constructor make sure to set your absorption to 100% like so when adding your armor component. The constructor is the part of your item that adds components, tags, and sets other variables for when one of your item is spawned in the world.

    inst:AddComponent("armor") -- Only add the armor component once, if you already have it you can omit this line
    inst.components.armor:InitCondition(TUNING.ARMORWOOD, 1) -- The 1 here is the absorption, be sure change TUNING.ARMORWOOD to whatever durability variable or number you'll be using for your own armor.

Unfortunately, since the absorption is technically 100% some HUD mods may display the "incorrect" value, of 100% armor. I'm not personally sure how to fix this, but players should know how this armor works in advance hopefully and ignore the fake 100% absorption.

Now that the armor is the taking all the damage, we'll listen for the armor getting damaged in the OnEquip and OnUnequip functions.

This should go in OnEquip

inst:ListenForEvent("armordamaged", OnDamaged, inst) -- Listens for the armor getting damaged, then when it is, it calls the function OnDamaged, and passes inst to it as a variable

And this in OnUnequip

inst:RemoveEventCallback("armordamaged", OnDamaged, inst) -- Removes the event listener we added in OnEquip. We only want the player getting damaged while they're wearing the armor.

Finally, we'll need to add this function above both OnEquip and OnUnequip to handle the damage penalty

local function OnDamaged(inst, data)
    local owner = inst.components.inventoryitem:GetGrandOwner() -- Sets the varaible owner as the armors owner/wearer

    if owner and owner.components.health and owner.components.combat then -- This ensures we won't crash by checking if the things we're accessing exist
        owner.components.health:DoDelta(-data - 20) -- data is the variable for how much damage the armor took, 
                                                    -- So we make it negative, then take away an additional 20 for your damage penalty and apply it to the players health
        owner.components.combat:GetAttacked() -- This ensures the player still goes into hitstun, and the damage feels normal.
    end
end

Hope this helps! Don't hesitate to ask any questions, no matter how stupid you may think it is. We all need to learn somehow.

Edited by TheSkylarr
  • Big Ups 1
5 hours ago, TheSkylarr said:

- SNIP-

I didn't mention it before but this mod is being worked for DS and your code worked like a charm! Thank-you!!

If it's possible and you have the time I'd like to ask your help for another matter with another armor my mod introduces. This armor (armor_ghost) is supposed to update its Absorption amount based on the player's sanity (if owner's sanity is 100% the armor gives 60% protection, if sanity=75% then 75% protection, if sanity=0.5 then 80% protection and last if sanity=0.25 then 95% protection)

What I've tried to do is this:

Spoiler

local assets=
{
	Asset("ANIM", "anim/armor_ghost.zip"),
}

local function UpdateAbsorption(inst, owner)
	if owner ~= nil and owner.components.sanity >= 1 then
		inst.components.armor:SetAbsorption(.6)
	elseif owner ~= nil and owner.components.sanity >= 0.75 then
		inst.components.armor:SetAbsorption(.75)
	elseif owner ~= nil and owner.components.sanity >= 0.5 then
		inst.components.armor:SetAbsorption(.8)
	elseif owner ~= nil and owner.components.sanity >= 0.25 then
		inst.components.armor:SetAbsorption(.95)
	end
end

local function OnBlocked(owner) 
    UpdateAbsorption(inst)
    owner.SoundEmitter:PlaySound("dontstarve/wilson/hit_nightarmour")

	if math.random() <= TUNING.ARMORGHOST_ATKCHANCE then
		local fx = SpawnPrefab("bramblefx")
	local x,y,z = owner.Transform:GetWorldPosition()
		fx.Transform:SetPosition(x,y,z)

		local ents = TheSim:FindEntities(x,y,z, 4,nil,{"INLIMBO"})
		if #ents > 0 then			
			for i,ent in pairs(ents)do
				if ent.components.combat and ent ~= owner then
					local src = owner
					if ent.components.follower and ent.components.follower.leader == owner then
						src = nil
					end
					ent.components.combat:GetAttacked(src, TUNING.ARMORGOST_ATKDMG, nil, "thorns")
				end
			end
		end
	end
end

local function OnLoad(inst, data)
    UpdateAbsorption(inst)
end

local function onequip(inst, owner) 
    UpdateAbsorption(inst)
    owner.AnimState:OverrideSymbol("swap_body", "armor_ghost", "swap_body")
    inst:ListenForEvent("blocked", OnBlocked, owner)
end

local function onunequip(inst, owner) 
    UpdateAbsorption(inst)
    owner.AnimState:ClearOverrideSymbol("swap_body")
    inst:RemoveEventCallback("blocked", OnBlocked, owner)
end

local function OnTakeDamage(inst)
	UpdateAbsorption(inst)
end

local function fn(Sim)
	local inst = CreateEntity()
    
	inst.entity:AddTransform()
	inst.entity:AddAnimState()

    MakeInventoryPhysics(inst)
--  MakeInventoryFloatable(inst, "idle_water", "idle_loop")
    
    inst.AnimState:SetBank("hf_armors")
    inst.AnimState:SetBuild("armor_hf_ghost")
    inst.AnimState:PlayAnimation("idle_loop")

    inst:AddComponent("inspectable")
    
    inst:AddComponent("inventoryitem")
    inst.components.inventoryitem.imagename = "armor_ghost"
    inst.components.inventoryitem.atlasname = "images/inventoryimages/armor_ghost.xml"
    inst.components.inventoryitem.foleysound = "dontstarve_DLC003/common/crafted/vortex_armour/foley"
    
    inst:AddComponent("armor")
	inst.components.armor:InitCondition(TUNING.ARMOR_GHOST)
	inst.components.armor:SetAbsorption(TUNING.ARMOR_GHOST_ABSORPTION)
	inst.components.armor.ontakedamage = OnTakeDamage
    
    inst:AddComponent("equippable")
    inst.components.equippable.equipslot = EQUIPSLOTS.BODY
    
    inst.components.equippable:SetOnEquip( onequip )
    inst.components.equippable:SetOnUnequip( onunequip )

	UpdateAbsorption(inst)

    return inst
end

return Prefab( "common/inventory/armor_ghost", fn, assets) 

 

Also the TUNING values I've set on another file:

local TUNING = GLOBAL.TUNING

TUNING.ARMOR_GHOST = 2000
TUNING.ARMOR_GHOST_ABSORPTION = .7
TUNING.ARMORGHOST_ATKCHANCE = .33
TUNING.ARMORGOST_ATKDMG = 34

I'm still working on it, maybe I can find some solution on some other in-game item, right now the game crashes and the log says:

armor_ghost.lua:7: attempt to index local 'owner' (a nil value)

I hope you can help. Thanks again for helping me with the original problem (I will credit you on the mod page once it gets released, unless it's against your wishes to use your name.)

EDIT UPDATE 1:

I had no idea how to make it work with the player's health, so I decided to make the absorption change based on the armor's own health! And it seems to have worked. This is the code I came up with:

local function UpdateAbsorption(inst, owner)
	if inst.components.armor.condition >= TUNING.ARMOR_GHOST*0.75 then
		inst.components.armor:SetAbsorption(0.75)
	elseif inst.components.armor.condition >= TUNING.ARMOR_GHOST*0.5 and inst.components.armor.condition < TUNING.ARMOR_GHOST*0.75 then
		inst.components.armor:SetAbsorption(0.85)
	elseif inst.components.armor.condition > TUNING.ARMOR_GHOST*0.25 and inst.components.armor.condition < TUNING.ARMOR_GHOST*0.5 then
		inst.components.armor:SetAbsorption(0.90)
	elseif inst.components.armor.condition <= TUNING.ARMOR_GHOST*.25 then
		inst.components.armor:SetAbsorption(0.95)
	end  
end

local function OnLoad(inst, data)
   UpdateAbsorption(inst) -- When the Player loads back in the armor will check its durability and absorption
end
      
local function OnTakeDamage(inst)
  UpdateAbsorption(inst) -- Everytime the armor gets hit it will recheck its condition and then apply the appropriate absorption
end

 

Edited by mathem99
5 hours ago, mathem99 said:

I didn't mention it before but this mod is being worked for DS and your code worked like a charm! Thank-you!!

If it's possible and you have the time I'd like to ask your help for another matter with another armor my mod introduces. This armor (armor_ghost) is supposed to update its Absorption amount based on the player's sanity (if owner's sanity is 100% the armor gives 60% protection, if sanity=75% then 75% protection, if sanity=0.5 then 80% protection and last if sanity=0.25 then 95% protection)

What I've tried to do is this:

  Reveal hidden contents


local assets=
{
	Asset("ANIM", "anim/armor_ghost.zip"),
}

local function UpdateAbsorption(inst, owner)
	if owner ~= nil and owner.components.sanity >= 1 then
		inst.components.armor:SetAbsorption(.6)
	elseif owner ~= nil and owner.components.sanity >= 0.75 then
		inst.components.armor:SetAbsorption(.75)
	elseif owner ~= nil and owner.components.sanity >= 0.5 then
		inst.components.armor:SetAbsorption(.8)
	elseif owner ~= nil and owner.components.sanity >= 0.25 then
		inst.components.armor:SetAbsorption(.95)
	end
end

local function OnBlocked(owner) 
    UpdateAbsorption(inst)
    owner.SoundEmitter:PlaySound("dontstarve/wilson/hit_nightarmour")

	if math.random() <= TUNING.ARMORGHOST_ATKCHANCE then
		local fx = SpawnPrefab("bramblefx")
	local x,y,z = owner.Transform:GetWorldPosition()
		fx.Transform:SetPosition(x,y,z)

		local ents = TheSim:FindEntities(x,y,z, 4,nil,{"INLIMBO"})
		if #ents > 0 then			
			for i,ent in pairs(ents)do
				if ent.components.combat and ent ~= owner then
					local src = owner
					if ent.components.follower and ent.components.follower.leader == owner then
						src = nil
					end
					ent.components.combat:GetAttacked(src, TUNING.ARMORGOST_ATKDMG, nil, "thorns")
				end
			end
		end
	end
end

local function OnLoad(inst, data)
    UpdateAbsorption(inst)
end

local function onequip(inst, owner) 
    UpdateAbsorption(inst)
    owner.AnimState:OverrideSymbol("swap_body", "armor_ghost", "swap_body")
    inst:ListenForEvent("blocked", OnBlocked, owner)
end

local function onunequip(inst, owner) 
    UpdateAbsorption(inst)
    owner.AnimState:ClearOverrideSymbol("swap_body")
    inst:RemoveEventCallback("blocked", OnBlocked, owner)
end

local function OnTakeDamage(inst)
	UpdateAbsorption(inst)
end

local function fn(Sim)
	local inst = CreateEntity()
    
	inst.entity:AddTransform()
	inst.entity:AddAnimState()

    MakeInventoryPhysics(inst)
--  MakeInventoryFloatable(inst, "idle_water", "idle_loop")
    
    inst.AnimState:SetBank("hf_armors")
    inst.AnimState:SetBuild("armor_hf_ghost")
    inst.AnimState:PlayAnimation("idle_loop")

    inst:AddComponent("inspectable")
    
    inst:AddComponent("inventoryitem")
    inst.components.inventoryitem.imagename = "armor_ghost"
    inst.components.inventoryitem.atlasname = "images/inventoryimages/armor_ghost.xml"
    inst.components.inventoryitem.foleysound = "dontstarve_DLC003/common/crafted/vortex_armour/foley"
    
    inst:AddComponent("armor")
	inst.components.armor:InitCondition(TUNING.ARMOR_GHOST)
	inst.components.armor:SetAbsorption(TUNING.ARMOR_GHOST_ABSORPTION)
	inst.components.armor.ontakedamage = OnTakeDamage
    
    inst:AddComponent("equippable")
    inst.components.equippable.equipslot = EQUIPSLOTS.BODY
    
    inst.components.equippable:SetOnEquip( onequip )
    inst.components.equippable:SetOnUnequip( onunequip )

	UpdateAbsorption(inst)

    return inst
end

return Prefab( "common/inventory/armor_ghost", fn, assets) 

 

Also the TUNING values I've set on another file:


local TUNING = GLOBAL.TUNING

TUNING.ARMOR_GHOST = 2000
TUNING.ARMOR_GHOST_ABSORPTION = .7
TUNING.ARMORGHOST_ATKCHANCE = .33
TUNING.ARMORGOST_ATKDMG = 34

I'm still working on it, maybe I can find some solution on some other in-game item, right now the game crashes and the log says:


armor_ghost.lua:7: attempt to index local 'owner' (a nil value)

I hope you can help. Thanks again for helping me with the original problem (I will credit you on the mod page once it gets released, unless it's against your wishes to use your name.)

EDIT UPDATE 1:

I had no idea how to make it work with the player's health, so I decided to make the absorption change based on the armor's own health! And it seems to have worked. This is the code I came up with:


local function UpdateAbsorption(inst, owner)
	if inst.components.armor.condition >= TUNING.ARMOR_GHOST*0.75 then
		inst.components.armor:SetAbsorption(0.75)
	elseif inst.components.armor.condition >= TUNING.ARMOR_GHOST*0.5 and inst.components.armor.condition < TUNING.ARMOR_GHOST*0.75 then
		inst.components.armor:SetAbsorption(0.85)
	elseif inst.components.armor.condition > TUNING.ARMOR_GHOST*0.25 and inst.components.armor.condition < TUNING.ARMOR_GHOST*0.5 then
		inst.components.armor:SetAbsorption(0.90)
	elseif inst.components.armor.condition <= TUNING.ARMOR_GHOST*.25 then
		inst.components.armor:SetAbsorption(0.95)
	end  
end

local function OnLoad(inst, data)
   UpdateAbsorption(inst) -- When the Player loads back in the armor will check its durability and absorption
end
      
local function OnTakeDamage(inst)
  UpdateAbsorption(inst) -- Everytime the armor gets hit it will recheck its condition and then apply the appropriate absorption
end

 

Im glad you got it working, though I'm confused whether you wanted it to change based on Sanity or Health?

Also, if you'd like i can help code a more elegant solution for either one, there can be rare cases where it gets applied incorrectly, like if the players health changes while wearing the armor for a non-combat reason.

  • Thanks 1
53 minutes ago, TheSkylarr said:

Im glad you got it working, though I'm confused whether you wanted it to change based on Sanity or Health?

Also, if you'd like i can help code a more elegant solution for either one, there can be rare cases where it gets applied incorrectly, like if the players health changes while wearing the armor for a non-combat reason.

I made the Armor's Absorption be based on the Armor's Durability (Health) instead of the Player's Sanity, it's much easier and I thought game-play wise it looks a lot more fun. As of right now the code I'm using to change the armor's absorption is this:

local function UpdateAbsorption(inst, owner)
	if inst.components.armor.condition >= TUNING.ARMOR_GHOST*0.75 then
        inst.components.armor:SetAbsorption(0.75)
	elseif inst.components.armor.condition >= TUNING.ARMOR_GHOST*0.5 and inst.components.armor.condition < TUNING.ARMOR_GHOST*0.75 then
		inst.components.armor:SetAbsorption(0.85)
	elseif inst.components.armor.condition > TUNING.ARMOR_GHOST*0.25 and inst.components.armor.condition < TUNING.ARMOR_GHOST*0.5 then
		inst.components.armor:SetAbsorption(0.90)
	elseif inst.components.armor.condition <= TUNING.ARMOR_GHOST*.25 then
		inst.components.armor:SetAbsorption(0.95)
    end  
end

And I haven't met any problems yet. (I made the armor go through this function automatically when you equip the armor, when you take damage and when you load in the game)

Edited by mathem99
2 hours ago, mathem99 said:

I made the Armor's Absorption be based on the Armor's Durability (Health) instead of the Player's Sanity, it's much easier and I thought game-play wise it looks a lot more fun. As of right now the code I'm using to change the armor's absorption is this:


local function UpdateAbsorption(inst, owner)
	if inst.components.armor.condition >= TUNING.ARMOR_GHOST*0.75 then
        inst.components.armor:SetAbsorption(0.75)
	elseif inst.components.armor.condition >= TUNING.ARMOR_GHOST*0.5 and inst.components.armor.condition < TUNING.ARMOR_GHOST*0.75 then
		inst.components.armor:SetAbsorption(0.85)
	elseif inst.components.armor.condition > TUNING.ARMOR_GHOST*0.25 and inst.components.armor.condition < TUNING.ARMOR_GHOST*0.5 then
		inst.components.armor:SetAbsorption(0.90)
	elseif inst.components.armor.condition <= TUNING.ARMOR_GHOST*.25 then
		inst.components.armor:SetAbsorption(0.95)
    end  
end

And I haven't met any problems yet. (I made the armor go through this function automatically when you equip the armor, when you take damage and when you load in the game)

Oh i see! I suppose i misunderstood you before, I thought you were basing it off the players health, in the case of the armors remaining durability your code should be full proof!

If you need any other help don't be afraid to post here again, or if you have discord, add me, I'm Skylarr#9203, and I can give you more direct help.

Edited by TheSkylarr
  • Thanks 1

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...