Jump to content

Recommended Posts

I'm currently in the middle of trying to create a custom item for my character which is essentially a craftable medpack that can be "repaired" / refilled with honey or some other item

So far I have managed to make a healing item that had the percent display from finiteuse code, and even the repair part thanks to another post on this forum (which is still a bit wonky but it does semi-work, just that it only repair the item to full instead of maybe 10-20% like I wanted)

But what I couldn't figure out is the more than one healing part, since no matter the percentage, the item is fully consumed and disappears immediately as opposed to going down to zero and simply not breaking

I figured this might've something to do with the regular "healer" component since I did look at the healer.lua and found that it says the following at the bottom:

Spoiler

if self.inst.components.stackable ~= nil and self.inst.components.stackable:IsStack() then
        self.inst.components.stackable:Get():Remove()
    else
        self.inst:Remove()
    end

Now, I must admit that I know practically nothing about how most of the code work, but I am assuming that meant that the item is consumed and removed from inventory once used.

Which, then lead me to try and create an alternative healing component that in theory shouldn't remove the item once it is used, although that's where I then ran into a problem where the healing option just doesn't appear at all, so there's a likely chance i messed up the copying process somewhere

But that makes me wonder if the whole new healer component thing was completely pointless from the start and I could've just used the original healer component if I had known how to set the finiteuse up properly, or if I was right to assume that I did indeed need to make a seperate healer component and the only way I can make this work was to figure out how to copy and edit the original healer component.

This here is my lua file for the item itself:
 

Spoiler

local function fn()
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddNetwork()

    MakeInventoryPhysics(inst)

    inst.AnimState:SetBank("bandage")
    inst.AnimState:SetBuild("bandage")
    inst.AnimState:PlayAnimation("idle")

    MakeInventoryFloatable(inst)

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end

    

 

    inst:AddComponent("inspectable")

    inst:AddComponent("inventoryitem")

    inst:AddComponent("repeathealer")
    inst.components.repeathealer:SetHealthAmount(15)


    MakeHauntableLaunch(inst)

    inst:AddComponent("finiteuses")
    inst.components.finiteuses:SetMaxUses(10)
    inst.components.finiteuses:SetUses(9)
    inst.components.finiteuses:SetConsumption(ACTIONS.REHEAL, 1)

      inst:AddComponent("repairable")
         inst.components.repairable.repairmaterial = "honey"

    return inst
end

return Prefab("traumakit", fn, assets)

 

20 hours ago, ArsFelenlis said:

I'm currently in the middle of trying to create a custom item for my character which is essentially a craftable medpack that can be "repaired" / refilled with honey or some other item

So far I have managed to make a healing item that had the percent display from finiteuse code, and even the repair part thanks to another post on this forum (which is still a bit wonky but it does semi-work, just that it only repair the item to full instead of maybe 10-20% like I wanted)

But what I couldn't figure out is the more than one healing part, since no matter the percentage, the item is fully consumed and disappears immediately as opposed to going down to zero and simply not breaking

I figured this might've something to do with the regular "healer" component since I did look at the healer.lua and found that it says the following at the bottom:

  Reveal hidden contents

if self.inst.components.stackable ~= nil and self.inst.components.stackable:IsStack() then
        self.inst.components.stackable:Get():Remove()
    else
        self.inst:Remove()
    end

Now, I must admit that I know practically nothing about how most of the code work, but I am assuming that meant that the item is consumed and removed from inventory once used.

Which, then lead me to try and create an alternative healing component that in theory shouldn't remove the item once it is used, although that's where I then ran into a problem where the healing option just doesn't appear at all, so there's a likely chance i messed up the copying process somewhere

But that makes me wonder if the whole new healer component thing was completely pointless from the start and I could've just used the original healer component if I had known how to set the finiteuse up properly, or if I was right to assume that I did indeed need to make a seperate healer component and the only way I can make this work was to figure out how to copy and edit the original healer component.

This here is my lua file for the item itself:
 

  Hide contents

local function fn()
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddNetwork()

    MakeInventoryPhysics(inst)

    inst.AnimState:SetBank("bandage")
    inst.AnimState:SetBuild("bandage")
    inst.AnimState:PlayAnimation("idle")

    MakeInventoryFloatable(inst)

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end

    

 

    inst:AddComponent("inspectable")

    inst:AddComponent("inventoryitem")

    inst:AddComponent("repeathealer")
    inst.components.repeathealer:SetHealthAmount(15)


    MakeHauntableLaunch(inst)

    inst:AddComponent("finiteuses")
    inst.components.finiteuses:SetMaxUses(10)
    inst.components.finiteuses:SetUses(9)
    inst.components.finiteuses:SetConsumption(ACTIONS.REHEAL, 1)

      inst:AddComponent("repairable")
         inst.components.repairable.repairmaterial = "honey"

    return inst
end

return Prefab("traumakit", fn, assets)

 

You will need to add AddComponentAction for your new component to make it work. Another way is to hook into the healer component, and you can also use this simple method
 

	inst:AddComponent("healer")
	inst.components.healer:SetHealthAmount(TUNING.HEALING_MEDLARGE)
	inst.components.healer.Heal = function(inst, target, doer, ...)
		if target.components.health then
			--[[Add health, finiteuses check or something you use to make sure it doesn't crash	]]
			target.components.health:DoDelta(1)
			return true
		end
	end

 

30 minutes ago, Haruhi Kawaii said:

You will need to add AddComponentAction for your new component to make it work. Another way is to hook into the healer component, and you can also use this simple method
 

	inst:AddComponent("healer")
	inst.components.healer:SetHealthAmount(TUNING.HEALING_MEDLARGE)
	inst.components.healer.Heal = function(inst, target, doer, ...)
		if target.components.health then
			--[[Add health, finiteuses check or something you use to make sure it doesn't crash	]]
			target.components.health:DoDelta(1)
			return true
		end
	end

 

I actually did manage to figure it out like less than an hour ago xD

Now I'm mostly just figuring out the custom repair part since it kinda broke along the way (Granted I aboslutely could just use an easy method and make it say repair when I hover item over it, but for some reason I decided to make a custom component for it as well so it'd say "Refill" when I'm trying to repair

Not sure how or what did it but I was finally able to create a proper working copy of the healer component without the remove upon consumption part

So now it uses the finiteuse component properly, albeit with it continuing to work after it's at zero, so I just added another function to take away the custom healer component once its at zero use and add it back once it got more than >=10 uses

And yes I already did had the AddComponent action, I just didn't copy it properly for some reason but it now works

And nope, I also learned that I simply just can't use the game's built in healer component since they specifically have a part where they remove the item upon use, so I did indeed need to make that new copy of the healer component while cutting that part of the code out

Thank you for your input anyway though!

 

On 2/16/2025 at 2:22 PM, ArsFelenlis said:

I actually did manage to figure it out like less than an hour ago xD

Now I'm mostly just figuring out the custom repair part since it kinda broke along the way (Granted I aboslutely could just use an easy method and make it say repair when I hover item over it, but for some reason I decided to make a custom component for it as well so it'd say "Refill" when I'm trying to repair

Not sure how or what did it but I was finally able to create a proper working copy of the healer component without the remove upon consumption part

So now it uses the finiteuse component properly, albeit with it continuing to work after it's at zero, so I just added another function to take away the custom healer component once its at zero use and add it back once it got more than >=10 uses

And yes I already did had the AddComponent action, I just didn't copy it properly for some reason but it now works

And nope, I also learned that I simply just can't use the game's built in healer component since they specifically have a part where they remove the item upon use, so I did indeed need to make that new copy of the healer component while cutting that part of the code out

Thank you for your input anyway though!

 

You could simply rewrite the function using AddComponentPostInit, adding a variable, and if it was true, then not delete the item after use, but reduce the number of uses, it's very simple and will not affect other mods if done correctly
 

AddComponentPostInit("healer", function(self, inst)
  function self:Heal(target, doer)
      local health = target.components.health

      if health == nil then
          return false
      end

      if self.canhealfn ~= nil then
          local valid, reason = self.canhealfn(self.inst, target, doer)

          if not valid then
              return false, reason
          end
      end

      if health.canheal then -- NOTES(JBK): Tag healerbuffs can make this heal function be invoked but we do not want to apply health to things that can not be healed.
          health:DoDelta(self.health, false, self.inst.prefab)
      end

      if self.onhealfn ~= nil then
          self.onhealfn(self.inst, target, doer)
      end

      if self.dont_remove then -- add this
          return true
      end

      if self.inst.components.stackable ~= nil and self.inst.components.stackable:IsStack() then
          self.inst.components.stackable:Get():Remove()
      else
          self.inst:Remove()
      end

      return true
  end
end


 

  • Like 1
On 2/21/2025 at 4:30 PM, GodIess said:

You could simply rewrite the function using AddComponentPostInit, adding a variable, and if it was true, then not delete the item after use, but reduce the number of uses, it's very simple and will not affect other mods if done correctly

I would recommend making a new fuelsource in modmain.lua and use the fueled compoennt. Seems to match your needs the most.

Spoiler
GLOBAL.FUELTYPE.HONEY = "HONEY" --++ alain
AddPrefabPostInit("honey", function(inst)
	if not GLOBAL.TheWorld.ismastersim then return end
	inst:AddComponent("fuel")
	inst.components.fuel.fueltype = GLOBAL.FUELTYPE.HONEY
	inst.components.fuel.fuelvalue = GLOBAL.TUNING.MED_LARGE_FUEL	-- to assign fuel strength see tuning.lua
end)

PrefabFiles = 
{
	"testitem",
}

Assets =
{
	Asset("ANIM", "anim/bandage.zip"),
}

Then in your itemtesting.lua (rename it to what you want it to be)

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

local function MakeHealer(inst)
	local icf = inst.components.fueled
	if not icf then print(inst, "error, no fueled component") return end -- Fueled is supposed to exist by now
  	-- suggest: PlayAnimation"idle"
	inst:AddComponent"healer"
	local ich = inst.components.healer
	ich:SetHealthAmount(TUNING.HEALING_MED)
	local Heal_old = ich.Heal
	local Remove_real = ich.inst.Remove 
	ich.Heal = function(...)
		ich.inst.Remove = function() return end -- No item removed on heal
		local result, reason = Heal_old(...)
		ich.inst.Remove = Remove_real
		return result, reason
	end
	ich:SetOnHealFn(function(inst, target, doer)
		icf:DoDelta(-TUNING.MED_FUEL)
		if not icf.accepting then
			icf.accepting = true
		end
		return true
	end)
	ich:SetCanHealFn(function(inst, target, doer)
		if not icf:IsEmpty() then
			return true
		else
			return false, "NO_HEALER_FUEL" -- Not sure if will be said
		end
	end)
end

local function MakeFueled(inst)
	inst:AddComponent"fueled"
	local icf = inst.components.fueled
	local USES = 3
	icf.fueltype = FUELTYPE.HONEY
	icf:InitializeFuelLevel(USES * TUNING.MED_FUEL)
	icf:SetDepletedFn(function()
		inst:RemoveComponent"healer"
		-- suggest: PlayAnimation"broken"
	end)
	icf:SetCanTakeFuelItemFn(function(inst, item, doer)
		return item.prefab == "honey" and not icf:IsFull() -- just in case
	end)
	local takesound = "dontstarve/creatures/monkey/poopsplat"
	inst.components.fueled:SetTakeFuelFn(function(inst) -- from amulet.lua
		if not inst.components.healer then
			MakeHealer(inst)
		end
		if icf:IsFull() then 
			icf.accepting = false
		end
		local owner = inst.components.inventoryitem.owner
		if owner == nil then
			inst.SoundEmitter:PlaySound(takesound)
		else
			inst.playfuelsound:push()
			if not TheNet:IsDedicated() then
				local parent = inst.entity:GetParent()
				local container = parent ~= nil and (parent.replica.inventory or parent.replica.container) or nil
				if container ~= nil and container:IsOpenedBy(ThePlayer) then
					TheFocalPoint.SoundEmitter:PlaySound(takesound)
				end
			end
		end
	end)
	local SetPercent_old = icf.SetPercent
	icf.SetPercent = function(self, ...) -- in case a scenario where this gets spawned with this function happens
		SetPercent_old(self, ...)
		if not self:IsFull() then
			self.accepting = true
		end	-- OnDepleted handles self:IsEmpty()
	end
end

local function fn()
	local inst = CreateEntity()

	inst.entity:AddTransform()
	inst.entity:AddAnimState()
    inst.entity:AddNetwork() 

    MakeInventoryPhysics(inst)

    inst.AnimState:SetBank("bandage")
    inst.AnimState:SetBuild("bandage")
    inst.AnimState:PlayAnimation("idle")

	inst.playfuelsound = net_event(inst.GUID, "traumakit.playfuelsound")

    MakeInventoryFloatable(inst)

	inst.entity:AddSoundEmitter() -- add sound emitter
    inst.entity:SetPristine() 

    if not TheWorld.ismastersim then return inst end

    inst:AddComponent("inspectable")
    inst:AddComponent("inventoryitem")
	inst.components.inventoryitem.imagename = "bandage" --not sure why it doesnt show up otherwise

	MakeFueled(inst)
	MakeHealer(inst)
	
    MakeHauntableLaunch(inst)

    return inst
end

return Prefab("testitem", fn, assets)

 

All I really did was patch together something similar to amulet.lua on your component and then overwrote the heal component like Godless did, but more precisely.

I think MakeForgeRepairable could be most accurately you're looking for but I was a bit intimidated because it's new/undocumented and wasn't sure about a lot of stuff with it. Didn't want to implement something poorly. But fueled does the same thing in this way but uses a different action name.

Edited by oregu
  • Like 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...