Jump to content

Recommended Posts

Hi, 

as the title suggets, i want to add the sound of rain hitting the umbrella to the "Rain Hat". However on reading some of the tutorials, i appears to me, that only existing sound events can be replaced. Since there doesn't seem to be an event for equipping the Rain Hat specifically, i wanted to ask if anyone here knows, if it's somehow possible to add that event to the game and add the soundfile to it. Obviously, that soundfile would only play while it's raining 

Thanks in advance

Edited by pR1sm

Welcome around here, @pR1sm!

local function updatesound(inst)
	local soundShouldPlay = GLOBAL.GetSeasonManager():IsRaining() and inst.components.equippable:IsEquipped()
	if soundShouldPlay ~= inst.SoundEmitter:PlayingSound("umbrellarainsound") then
		if soundShouldPlay then
			inst.SoundEmitter:PlaySound("dontstarve/rain/rain_on_umbrella", "umbrellarainsound") 
		else
			inst.SoundEmitter:KillSound("umbrellarainsound")
		end
	end
end  

local function rainhatpostinit(inst)
	inst.entity:AddSoundEmitter()

	local EquipOld = inst.components.equippable.Equip
	local UnequipOld = inst.components.equippable.Unequip
	
	inst.components.equippable.Equip = function(self, owner, slot)
		EquipOld(self, owner, slot)
		updatesound(self.inst)
	end
	inst.components.equippable.Unequip = function(self, owner, slot)
		UnequipOld(self, owner, slot)
		updatesound(self.inst)
	end
	
	inst:ListenForEvent("rainstop", function() updatesound(inst) end, GLOBAL.GetWorld())
	inst:ListenForEvent("rainstart", function() updatesound(inst) end, GLOBAL.GetWorld())
end

AddPrefabPostInit("rainhat", rainhatpostinit)

Tested it briefly in SW and it worked, should work in RoG too.

All headgear uses generic onequip and onunequip functions ( inst.components.equippable:SetOnEquip( onequip ) and inst.components.equippable:SetOnUnequip( onunequip ) in simple() in prefabs/hats.lua). Some hats with special behaviours then call SetOnEquip and SetOnUnequip again, overriding the generic function, like the Eyebrella does. You could just call SetOnEquip and SetOnUnequip, but then you'd have to define that default behaviour somewhere (or you wouldn't see the hat Rain Hat when worn) and it could potentially break other mods.

Thank you very much :)

 

Would SetOn(Un)Equip call rainhatpostinit and would i have to simply add that call at the bottom of my mainmod.lua? I'm very much new to LUA and script languages in general, sorry ^^

Edited by pR1sm

To change a prefab, you use AddPrefabPostInit. The first argument is the prefab, the second is the function that actually does something. If you wanted to use SetOnEquip and SetOnUnequip, your modmain.lua should look something like this (untested):

Spoiler



local function updatesound(inst)
	local soundShouldPlay = GLOBAL.GetSeasonManager():IsRaining() and inst.components.equippable:IsEquipped()
	if soundShouldPlay ~= inst.SoundEmitter:PlayingSound("umbrellarainsound") then
		if soundShouldPlay then
			inst.SoundEmitter:PlaySound("dontstarve/rain/rain_on_umbrella", "umbrellarainsound")
		else
			inst.SoundEmitter:KillSound("umbrellarainsound")
		end
	end
end

local function onequip(inst, owner)
	
	updatesound(inst)	-- this is what we're adding
	
	--everything from this point on is just copied from onequip in hats.lua
	local skin_build = inst:GetSkinBuild()
	if skin_build ~= nil then
		owner:PushEvent("equipskinneditem", inst:GetSkinName())
		owner.AnimState:OverrideItemSkinSymbol("swap_hat", skin_build, "swap_hat", inst.GUID, fname)
	else
		owner.AnimState:OverrideSymbol("swap_hat", fname, "swap_hat")
	end
	owner.AnimState:Show("HAT")
	owner.AnimState:Show("HAIR_HAT")
	owner.AnimState:Hide("HAIR_NOHAT")
	owner.AnimState:Hide("HAIR")
	
	if owner:HasTag("player") then
		owner.AnimState:Hide("HEAD")
		owner.AnimState:Show("HEAD_HAT")
	end
	
	if inst.components.fueled ~= nil then
		inst.components.fueled:StartConsuming()
	end
end

local function onunequip(inst, owner)
	
	updatesound(inst)	-- this is what we're adding
	
	--everything from this point on is just copied from onunequip in hats.lua
	local skin_build = inst:GetSkinBuild()
	if skin_build ~= nil then
		owner:PushEvent("unequipskinneditem", inst:GetSkinName())
	end
	
	owner.AnimState:ClearOverrideSymbol("swap_hat")
	owner.AnimState:Hide("HAT")
	owner.AnimState:Hide("HAIR_HAT")
	owner.AnimState:Show("HAIR_NOHAT")
	owner.AnimState:Show("HAIR")
	
	if owner:HasTag("player") then
		owner.AnimState:Show("HEAD")
		owner.AnimState:Hide("HEAD_HAT")
	end
	
	if inst.components.fueled ~= nil then
		inst.components.fueled:StopConsuming()
	end
end


local function rainhatpostinit(inst)
	inst.entity:AddSoundEmitter()
	
	inst.components.equippable:SetOnEquip(onequip)
	inst.components.equippable:SetOnUnequip(onunequip)
	
	inst:ListenForEvent("rainstop", function() updatesound(inst) end, GLOBAL.GetWorld())
	inst:ListenForEvent("rainstart", function() updatesound(inst) end, GLOBAL.GetWorld())
end

AddPrefabPostInit("rainhat", rainhatpostinit)


 

You'd need all the extra code to make the Rain Hat visible when worn. As you can see, it's much longer. Even if the bulk of the new code is just copy-pasted (with some changes), it's definitely more work. Don't work more than you have to, make the most of what's already there.

Awesome work!

So "AddPrefabPostInit" replaces all original functions of its first parameter with those provided in the modmain and subsequent lua-files?

I get an error however, game complains that the variable GetSeasonManager is not declared. Maybe a difference between RoG and SW after all, since your first code worked and "soundShouldPlay" was initialised in the same manner?

Edited by pR1sm

Eww, I made a mess. I copied copied the functions from hats.lua in DST. No skins in single player! It should look like this (tested!): 

Spoiler

local function updatesound(inst)
	local soundShouldPlay = GLOBAL.GetSeasonManager():IsRaining() and inst.components.equippable:IsEquipped()
	if soundShouldPlay ~= inst.SoundEmitter:PlayingSound("umbrellarainsound") then
		if soundShouldPlay then
			inst.SoundEmitter:PlaySound("dontstarve/rain/rain_on_umbrella", "umbrellarainsound")
		else
			inst.SoundEmitter:KillSound("umbrellarainsound")
		end
	end
end

local function onequip(inst, owner)
	
	updatesound(inst)	-- this is what we're adding
	
	--everything from this point on is just copied from onequip in hats.lua
	
	local build = "hat_rain"
	owner.AnimState:OverrideSymbol("swap_hat", build, "swap_hat")
	owner.AnimState:Show("HAT")
	owner.AnimState:Show("HAT_HAIR")
	owner.AnimState:Hide("HAIR_NOHAT")
	owner.AnimState:Hide("HAIR")
	
	if owner:HasTag("player") then
		owner.AnimState:Hide("HEAD")
		owner.AnimState:Show("HEAD_HAIR")
	end
	
	if inst.components.fueled then
		inst.components.fueled:StartConsuming()        
	end
end

local function onunequip(inst, owner)
	
	updatesound(inst)	-- this is what we're adding
	
	--everything from this point on is just copied from onunequip in hats.lua
	owner.AnimState:Hide("HAT")
	owner.AnimState:Hide("HAT_HAIR")
	owner.AnimState:Show("HAIR_NOHAT")
	owner.AnimState:Show("HAIR")
	
	if owner:HasTag("player") then
		owner.AnimState:Show("HEAD")
		owner.AnimState:Hide("HEAD_HAIR")
	end
	
	if inst.components.fueled then
		inst.components.fueled:StopConsuming()        
	end
end


local function rainhatpostinit(inst)
	inst.entity:AddSoundEmitter()
	
	inst.components.equippable:SetOnEquip(onequip)
	inst.components.equippable:SetOnUnequip(onunequip)
	
	inst:ListenForEvent("rainstop", function() updatesound(inst) end, GLOBAL.GetWorld())
	inst:ListenForEvent("rainstart", function() updatesound(inst) end, GLOBAL.GetWorld())
end

AddPrefabPostInit("rainhat", rainhatpostinit)

 

Works in RoG too. Were you perhaps trying it in DST? Weather is handled somewhat differently.

12 hours ago, pR1sm said:

So "AddPrefabPostInit" replaces all original functions of its first parameter with those provided in the modmain and subsequent lua-files?

No, AddPrefabPostInit does not replace anything in the original prefab. It allows you to add to the prefab definition, as if you were directly extending that definition. For example, in this case we are adding the sound emitter and the event listeners to the rainhat prefab. Everything else remains the same: it still uses the fueled, waterproofer and equippable components and the same graphics without us mentioning any of it in our mod. However, the SetOnEquip and SetOnUnequip methods to the equippable component each allow for one special function to be called when equipping/unequipping the item. Since we do want to have a special behaviour (checking whether the sound should play) and the prefab definition already has set one up (changing the animations), we are replacing the original one (animations) with what we finally want (both: check sound to play and change animations). Does that make sense?

Ah, i was indeed trying the modification in DST, i assumed RoG was pretty much a subset of DST with DST only adding multiplayer support. 

I've read up on some of the differences between DST and the basic game, so 

inst:ListenForEvent("rainstop", function() updatesound(inst) end, GLOBAL.GetWorld())
inst:ListenForEvent("rainstart", function() updatesound(inst) end, GLOBAL.GetWorld())

should look somewhat like this, right?

inst:WatchWorldState("israining", function() updatesound(inst) end, TheWorld)

Now the only other thing that should need chaning would be

local soundShouldPlay = GLOBAL.GetSeasonManager():IsRaining() and inst.components.equippable:IsEquipped()

..correct? Since DST doesnt use a seasonmanager, i figured it would be something like

local soundShouldPlay = GLOBAL.TheWorld.state:israining() and inst.components.equippable:IsEquipped()

However now the game complains about the israining call returning a boolean value (no idea why it would complain about that, after all we are evaluating wether its raining or not 0o), which at least told me that Global.TheWorld.state exists.

Been searching online for some documentation on the whole season matter in DST, but couldnt find anything <.<

Edited by pR1sm
39 minutes ago, alainmcd said:

...In DST, you'd only need AddPrefabPostInit("rainhat", function(inst) inst:AddTag("umbrella") end) in your modmain.lua.

well that and the seasonmanager, since that was the reason it didnt work for me to begin with in DST :p

Hmm, i tried your new version in shipwrecked, and it doesnt work, but it DOES work in RoG 0_o. the mod does load according to the console, but the sound remains the same in SW, hmm makes no sense...

Perhaps it has something to do with monsoon being a different season than spring?

Edited by pR1sm
27 minutes ago, pR1sm said:

that and the seasonmanager

No need, the single line I posted is all you need to add the Eyebrella sound to the Rain Hat.

30 minutes ago, pR1sm said:

i tried your new version in shipwrecked, and it doesnt work

shipwrecked_compatible = true? Works for me.

20 minutes ago, alainmcd said:

shipwrecked_compatible = true? Works for me.

it works in both cases for RoG, wether the save-file is made compatible with SW or not, but ironically it doesnt work for SW

EDIT. aaah, got it working by adding that line to the modinfo

Now also works in DST, thanks alot mate, really appreciate it :)

Any idea, where i can get a documenation on modding for dont starve without having to bother people on the forum?

Edited by pR1sm

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