Jump to content

Underwater Salvageable


Recommended Posts

Hello

Is there some easy way to make heavy ocean objects be picked up by players directly? I know it doesn't make sense but I'd need that for a mod.

I've tried removing the NOBLOCK / NOCLICK / NOTARGET tags and such but it doesn't do anything. I'm not really a fan of the wincher claw mechanic especially when crabking respawns directly ontop of the celestial altar it dropped a couple days earlier.

Link to comment
Share on other sites

If I'm not mistaken, the salvageables are a sort of container for the actual item that gets fetched by the winch. In addition to removing the NOBLOCK, NOCLICK, NOTARGET, you still have to give it a reason to be picked up by the cursor.

A rough and quick way of doing it could be giving it the "inventoryitem" component (might have some side effects, since the salvageables have an inventory component).

Edited by penguin0616
  • Like 2
Link to comment
Share on other sites

It gives you the correct item, it's just the underwater_salvageable prefab that you are getting, which has no inventoryitem image or strings.

local function pickup(inst,pickupguy)
	local item = inst.components.inventory:GetItemInSlot(1)
	if item ~= nil then
		if item.components.equippable ~= nil then
			pickupguy.components.inventory:Equip(GLOBAL.SpawnPrefab(item.prefab))
		else
			pickupguy.components.inventory:GiveItem(GLOBAL.SpawnPrefab(item.prefab))
		end
	end
  	inst:DoTaskInTime(0, inst.Remove)
end


local function underwater_salvageable(inst)

	inst:RemoveTag("notarget")
	inst:RemoveTag("NOCLICK")
    inst:RemoveTag("NOBLOCK")

	if not GLOBAL.TheWorld.ismastersim then
        return inst
    end
	
	if inst.components.inventoryitem == nil then
    	inst:AddComponent("inventoryitem")
	end
    inst.components.inventoryitem:SetOnPickupFn(pickup)

end

AddPrefabPostInit("underwater_salvageable", underwater_salvageable)

Adding something like this to your modmain will give you the item of the underwater_salvageable and remove it from your inventory after picking it up.

Edited by Monti18
Applied changes to make the function work better
  • Like 1
  • Potato Cup 1
Link to comment
Share on other sites

16 hours ago, Monti18 said:

It gives you the correct item, it's just the underwater_salvageable prefab that you are getting, which has no inventoryitem image or strings.


local function pickup(inst,pickupguy)
	local item = inst.components.inventory:GetItemInSlot(1)
	if item ~= nil then
		pickupguy.components.inventory:GiveItem(GLOBAL.SpawnPrefab(item.prefab))
	end
  	inst:DoTaskInTime(0, inst.Remove)
end


local function underwater_salvageable(inst)

	inst:RemoveTag("notarget")
	inst:RemoveTag("NOCLICK")
    inst:RemoveTag("NOBLOCK")

	if not TheWorld.ismastersim then
        return inst
    end

    inst:AddComponent("inventoryitem")
    inst.components.inventoryitem:SetOnPickupFn(pickup)

    inst.components.inventory.ignorescangoincontainer = false
end

AddPrefabPostInit("underwater_salvageable", underwater_salvageable)

Adding something like this to your modmain will give you the item of the underwater_salvageable and remove it from your inventory after picking it up.

This code causes a crash on server load unfortunately

Link to comment
Share on other sites

I think this is because I forgot to write there GLOBAL.TheWorld.ismastersim.

You need to have a look at the server log to find the error, at least for me this exact piece of code is working when using GLOBAL.TheWorld.ismastersim .

Edited by Monti18
  • Like 2
Link to comment
Share on other sites

11 hours ago, Monti18 said:

I think this is because I forgot to write there GLOBAL.TheWorld.ismastersim.

You need to have a look at the server log to find the error, at least for me this exact piece of code is working when using GLOBAL.TheWorld.ismastersim .

Most of the mechanic seems to function however the item is teleported to the nearest shore and not put in the character's hands

Link to comment
Share on other sites

Ah this seems to be because these are heavy objects, try this:

local function pickup(inst,pickupguy)
	local item = inst.components.inventory:GetItemInSlot(1)
	if item ~= nil then
		pickupguy.components.inventory:Equip(GLOBAL.SpawnPrefab(item.prefab))
	end
  	inst:DoTaskInTime(0, inst.Remove)
end

I updated the function in my first post to reflect the changes, i also made a check for the component inventoryitem as I had problems with duplicate replicas and deleted the line with ignorescangoincontainer as it made no sense. When I tried it like this, I equipped the seashells upon picking it up.

Edited by Monti18
  • Like 1
  • Potato Cup 1
Link to comment
Share on other sites

works great now!

now i'm just trying to figure out how to replace "MISSING NAME" with something more tangible.

inst:SetPrefabNameOverride("Unidentified Depth")

inst:AddComponent("inspectable")
inst.components.inspectable.nameoverride = "Unidentified Depth"

neither of these seem to work though hmm

Link to comment
Share on other sites

5 hours ago, Monti18 said:

Good to hear!

You do it the same way as adding a name to a custom item:


GLOBAL.STRINGS.NAMES.UNDERWATER_SALVAGEABLE = "Unidentified Depth"

 

Thank you. Already I've been using your help as a base to do other things on my own ;)

I have one last problem with the underwater salvageable. Sea chests do not produce loot when picked up manually this way. Is there some obvious fix i'm missing?

  • Like 1
Link to comment
Share on other sites

That's good, it's always the best to learn it to it yourself :)

Are the chests also an underwater salvageable? I'm sorry, I haven't really played much of th sea part of the game :D

But as far as I can see, these chests don't have an inventory component, that's why you won't get anything out of them. But they have the container component, you can just make a loop of all container slots and give yourself the item as I did it for the other salvageables.

Try it and see if you can do it, otherwise let me know.

  • Like 1
Link to comment
Share on other sites

2 minutes ago, Monti18 said:

That's good, it's always the best to learn it to it yourself :)

Are the chests also an underwater salvageable? I'm sorry, I haven't really played much of th sea part of the game :D

But as far as I can see, these chests don't have an inventory component, that's why you won't get anything out of them. But they have the container component, you can just make a loop of all container slots and give yourself the item as I did it for the other salvageables.

Try it and see if you can do it, otherwise let me know.

Sea chests roll a random set of various treasures, it's not really static

https://dontstarve.fandom.com/wiki/Sunken_Chest

If I had to take a guess, the loot is decided during the winch event which would be annoying considering we've bypassed exactly that

Link to comment
Share on other sites

Ah ok, I had a quick look at them, the treasure is generated when the chest is generated by the message bottle.

But that doesn't matter if it's then or with the winch, as we delete the chest and give ourselves a new one.

You need to save the items of the previous chest and insert them into the new chest.

From the function the variable item is your chest. You make a loop and save each item with and their stacksize if they have one in a table, and apply these items to the new item you spawn. You can do this by saving the new item as 

local new_item = GLOBAL.SpawnPrefab(item.prefab)

and then apply the items to new_item.

  • Like 1
Link to comment
Share on other sites

Oh sorry, it's a function that runs multiple times, see here from lua manual: https://www.lua.org/pil/4.3.4.html

It's something like this:

local items = {}

for k,v in ipairs(item.components.container.slots) do
	items[k] = v
end

Here k is the key and v the value of the table.

If you run this, you iterate through all slots of the container of your item, and each item is saved in the local table items.

 

  • Like 1
Link to comment
Share on other sites

Tables and loops are very useful for modding, so try to get comfortable with them.

i put a small explanation in the spoiler:

Spoiler

When you have a table, you can enter values into the table. These are v. v is just the variable name, you can call it whatever you want, but it gives back the value of the table. The key is the place where the value is stored in the table. If you don't choose a key, they start at 1 and go up by 1 for each next value.

So for example:


local table = {
  	"first",
 	 "second",
  	"third",
  }

Here we have a table named "table". The first value is "first". As we chose no key, the key is 1. That means, table[1] = "first".

The same goes for the other two, table[2] = "second" and table[3] = "third".


local table2 = {
  	one = "first",
 	two = "second",
  	three = "third",
  }

Here we choose the key, that means that table["one"] = "first", whereas table[1] is nil, as there is no value for 1.

When using pairs or ipairs, you cycle through each key of the table that is written in parentheses after pairs/ipairs.

If you run this function:


for k,v in ipairs(table) do
	print(k,v)
end

you will get this result:


1	first
2	second
3	third

The differences between pairs/ipairs is can be seen here: https://stackoverflow.com/questions/55108794/what-is-the-difference-of-pairs-vs-ipairs-in-lua

Try to get a feeling for it, it will make it much easier to write code as you don't need to write some things multiple times.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

10 hours ago, Monti18 said:

Tables and loops are very useful for modding, so try to get comfortable with them.

i put a small explanation in the spoiler:

  Reveal hidden contents

When you have a table, you can enter values into the table. These are v. v is just the variable name, you can call it whatever you want, but it gives back the value of the table. The key is the place where the value is stored in the table. If you don't choose a key, they start at 1 and go up by 1 for each next value.

So for example:



local table = {
  	"first",
 	 "second",
  	"third",
  }

Here we have a table named "table". The first value is "first". As we chose no key, the key is 1. That means, table[1] = "first".

The same goes for the other two, table[2] = "second" and table[3] = "third".



local table2 = {
  	one = "first",
 	two = "second",
  	three = "third",
  }

Here we choose the key, that means that table["one"] = "first", whereas table[1] is nil, as there is no value for 1.

When using pairs or ipairs, you cycle through each key of the table that is written in parentheses after pairs/ipairs.

If you run this function:



for k,v in ipairs(table) do
	print(k,v)
end

you will get this result:



1	first
2	second
3	third

The differences between pairs/ipairs is can be seen here: https://stackoverflow.com/questions/55108794/what-is-the-difference-of-pairs-vs-ipairs-in-lua

Try to get a feeling for it, it will make it much easier to write code as you don't need to write some things multiple times.

well I've spent hours crashing over and over again and I'm no closer to retaining the loot and transferring it in the "new" chest. it's frustrating because it's probably simple

Link to comment
Share on other sites

I know how you feel, I've been there often enough :D

But don't worry, it's not the easiest, especialy if you don't have experience with loops. Here is the solution for you:

local function pickup(inst,pickupguy)
	local item = inst.components.inventory:GetItemInSlot(1)
	if item ~= nil then
		if item.components.equippable ~= nil then
			local new_item = GLOBAL.SpawnPrefab(item.prefab)
			if item.components.container ~= nil and new_item.components.container ~= nil then --we check if the item is a container
				for k,v in ipairs(item.components.container.slots) do -- we do a loop through all slots of the old chest
					new_item.components.container.slots[k] = GLOBAL.SpawnPrefab(v.prefab) --we copy the items from the old chest to the new chest
					if v.components.stackable ~= nil then --we check if the item is stackable and if yes, we copy the stacksize of the old item to the new item
						local slot_item = new_item.components.container.slots[k]
						if slot_item and slot_item.components.stackable then
							slot_item.components.stackable.stacksize = v.components.stackable.stacksize
						end
					end
				end
			end
			pickupguy.components.inventory:Equip(new_item)
		else
			pickupguy.components.inventory:GiveItem(GLOBAL.SpawnPrefab(item.prefab))
		end
	end
  	inst:DoTaskInTime(0, inst.Remove)
end

When you copy item, it's important to use SpawnPrefab, as otherwise you just reference the old item and don't make a new one, I also always forget this.

If you have problems to understand something in this code, let me know!

  • Like 2
Link to comment
Share on other sites

On 6/3/2021 at 5:43 AM, Monti18 said:

I know how you feel, I've been there often enough :D

But don't worry, it's not the easiest, especialy if you don't have experience with loops. Here is the solution for you:


local function pickup(inst,pickupguy)
	local item = inst.components.inventory:GetItemInSlot(1)
	if item ~= nil then
		if item.components.equippable ~= nil then
			local new_item = GLOBAL.SpawnPrefab(item.prefab)
			if item.components.container ~= nil and new_item.components.container ~= nil then --we check if the item is a container
				for k,v in ipairs(item.components.container.slots) do -- we do a loop through all slots of the old chest
					new_item.components.container.slots[k] = GLOBAL.SpawnPrefab(v.prefab) --we copy the items from the old chest to the new chest
					if v.components.stackable ~= nil then --we check if the item is stackable and if yes, we copy the stacksize of the old item to the new item
						local slot_item = new_item.components.container.slots[k]
						if slot_item and slot_item.components.stackable then
							slot_item.components.stackable.stacksize = v.components.stackable.stacksize
						end
					end
				end
			end
			pickupguy.components.inventory:Equip(new_item)
		else
			pickupguy.components.inventory:GiveItem(GLOBAL.SpawnPrefab(item.prefab))
		end
	end
  	inst:DoTaskInTime(0, inst.Remove)
end

When you copy item, it's important to use SpawnPrefab, as otherwise you just reference the old item and don't make a new one, I also always forget this.

If you have problems to understand something in this code, let me know!

The code crashes around 25% of the time when I hammer a "clone chest". it says

[00:44:26]: [string "scripts/components/inventoryitem.lua"]:10: attempt to index field 'inventoryitem' (a nil value)

this doesn't seem to be from my modmain so I don't know what to do

Link to comment
Share on other sites

The game wants to index the owner of the item with the inventoryitem component, but on this item there is no iventoryitem component left. 

Could you post the entire log, I need to see for which item it happens and what the other variables are at the moment of the crash.

  • Like 1
Link to comment
Share on other sites

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
 Share

×
  • Create New...