Jump to content

Help on changing a component


Recommended Posts

I want to make mighty Wolfgang immune to bearger and gmoose's weapon dropping attacks. I figured the best way to do so was altering the DropItem function in the inventory component, so I went ahead and I got this error:

Quote

[00:02:40]: [string "../mods/MightyArms/modmain.lua"]:60: attempt to index field 'components' (a nil value)

The piece of code where the error appears:

local function NewDropItemFn(component)
	
	if not (GLOBAL.TheNet:GetIsServer() or GLOBAL.TheNet:IsDedicated()) then 
		return
    end

	local old_DropItem = component.DropItem
	
	component.inst:DoTaskInTime(0, function(inst)
		
		component.DropItem = function(item, wholestack, randomdir, pos)
	
			local owner = item.components.inventoryitem.owner

			if  owner ~= nil and owner == "wolfgang" and owner.strength == "mighty" then
				return
			end

			old_DropItem(item, wholestack, randomir, pos)
		
		end
	end)
end

AddComponentPostInit("inventory", NewDropItemFn)

The error appears specifically at "local owner".

So, the idea was to replace the current DropItem with some code that verified if the owner of the item was mighty Wolfgang; if no, it would go with the regular DropItem, if yes it would skip DropItem. But looking at "components" of "item" doesn't work? Is that normal when working with AddComponentPostInit? Is there a way around it or something?

Link to comment
Share on other sites

Looking at the original DropItem function, the very first thing they do is checking if the item is nil. So it is possible for the item to be nil. Of course nil does not have a components variable. So before you do what you want you should check for that:

local inventory = GLOBAL.require("components/inventory")

local old_DropItem = inventory.DropItem

function inventory.DropItem(item, wholestack, randomdir, pos)
    if item == nil or item.components.inventoryitem == nil then
        return
    end
    -- Your code here
    old_DropItem(item, wholestack, randomdir, pos)
end

 

Link to comment
Share on other sites

Yes, definitely always check for things being nil, unless you are 100% sure that they can never be nil.

Question: Isn't it kind of drastic to make it impossible for Wolfgang to drop an item? What happens if he dies? Does it still drop his items around the corpse?

I mean, I haven't looked at the code for any of this, but I'd guess that the least intrusive way would be to change whichever function Bearger and Gmoose use to make the characters drop their item.

Actually, I could imagine that frogs also steal items using DropItem.

I'm just thinking out loud here out of concern for unwanted side-effects. If you've checked all this out already, just ignore me :)

Link to comment
Share on other sites

On 2/28/2019 at 6:35 AM, BakaSchwarz said:

Looking at the original DropItem function, the very first thing they do is checking if the item is nil. So it is possible for the item to be nil. Of course nil does not have a components variable. So before you do what you want you should check for that:


local inventory = GLOBAL.require("components/inventory")

local old_DropItem = inventory.DropItem

function inventory.DropItem(item, wholestack, randomdir, pos)
    if item == nil or item.components.inventoryitem == nil then
        return
    end
    -- Your code here
    old_DropItem(item, wholestack, randomdir, pos)
end

 

Thanks a lot for your help! And I also learned a new way to change a component! But sadly it didn't work. The item is not nil (or has not been from every time I looked at the client log), but it does not have a "components" variable. The item is a table, or a table ID (this to be exact: item = table: 601B0AB0)? So maybe that is why it doesn't work. I will look into lua tables later on to see if maybe I can make it work. But if you (or anyone reading this) has an idea on how to do this (or why "components" doesn't "exist"): please be my guest!

Funnily enough, if I return in the case "item.components" is nil, it kind of works as I want it, as bearger is unable to drop the weapon from the player. Of course, since there is no other condition, it is also impossible to drop anything at will, and it applies to any and all characters.

@Ultroman Yeah, it is very drastic xD. I was thinking of adding, if it were possible, more conditions later on once I got it to "work" (look if Wolfgang is mighty, look if the player wants to drop the items, etc.). And doing it through inventory gives me the benefit that if Klei adds a new way of dropping items from the player, or adds a mob that makes you drop your items, or even if the player has a mod that has a mob with a functionality similar to bearger's, the mod should still work without my intervention (unless they drastically change how the function I used works). Sadly it seems I will have to change bearger and gmoose code to make this work :/. Still gotta see how tables work on lua though.

Link to comment
Share on other sites

I think you're missing something important about LUA. All classes are basically tables of variables. "item" looks like it's a table, because it is. It's a table of variables, and those variables can also be referring to functions. You can access variables and functions in LUA in two ways:

item["variablename"]

and the more conventional way, which is actually just syntactic sugar for the code above:

item.variablename

I'm not using the correct nomenclature, as I'm not entirely a LUA expert myself, but that's the gist of it, as far as I understand it (I hope I'm right :p).

An item should always have a "components" variable, as far as I understand it. Try doing something like this, to see what's going on:

if item.components == nil then
	print("item.components is nil")

	if item.prefab == nil then
		print("item.prefab is nil")
	else
		print("item.prefab: "..item.prefab)
	end

	if item.name == nil then
		print("item.name is nil")
	else
		print("item.name: "..item.name)
	end

	if item.inst == nil then
		print("item.inst is nil")
	else
		print("item.inst: "..item.inst)
		
		if item.inst.prefab == nil then
			print("item.inst.prefab is nil")
		else
			print("item.inst.prefab: "..item.inst.prefab)
		end
	end
end

You can copy/paste the first item.prefab if-structure and change it a bit, to check for more things which should be on an item. The longer one tries to see if for some reason you'd have to do "item.inst" to get the instance of the item, in order to access its components, but you really shouldn't have to. I don't know. If item is not nil, then it should have a components variable...

Anyway, as you're finding out, DropItem is used in many instances, and is probably not the function you should be toying with anyway. I understand that it'll make it future-proof and all that, but if it messes up the game (and I'm pretty sure it does, especially from what you're reporting), it's not worth it.

Link to comment
Share on other sites

I made it work!

For some reason the DropItem function was receiving a fifth parameter (which was the entity doing the drop action) that collided with the "item" parameter. I just added that fifth parameter and everything went on wheels!

In case anyone reading this wants to do something similar, here is the full code and what each part does:

local EQUIPSLOTS = GLOBAL.EQUIPSLOTS
local inventory = GLOBAL.require("components/inventory")		--this is needed to change the inventory component without the AddComponentPostInit (thanks to BakaSchwarz), you can also use AddComponentPostInit and it should work just fine

local old_DropItem = inventory.DropItem		--This is the original DropItem function that we will "change", we save it, because we are not going to change anything inside, just add

inventory.DropItem = function(self, item, wholestack, randomdir, pos)		--As you can see, it takes 5 parameter, the original only takes the last 4, but when you call it takes 5 (the first parameter is the owner of the inventory, but to reach the components you have to add ".inst", so if you called inst instead of self, you have to call it as inst.inst in order to reach the components variable)

	if item == nil or item.components.inventoryitem == nil then			--This is verifying if item is nil, again, thanks to BakaSchwarz and also Ultroman
		return
	end
	
	--This next line of code is a variable that verifies if the entity has mightygrip tag (I add it when Wolfgang becomes mighty, and remove when he becomes normal or wimpy), verifies that the item is the same as the item Wolfgang has in his hands equipslot, and verifies if Wolfgang has more than 0 health (it just happened that when he died, he had such a mighty grip he wouldn't let go of the weapon on death xD, so I added it just to make sense)
	--Here you might be thinking: why only the item Wolfgang has on his hands is affected? Well, I didn't know how to make it work with other items without the problem that mighty Wolfgang won't be able to drop anything at all, so I made it exclusive to anything equipped to the hands equipslot which was the main thing I wanted it to work for, yes, this makes him vulnerable to frogs dropping attack, but is not all that bad
	local IsGrabbedTight = self.inst:HasTag("mightygrip") and self.inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS) == item and self.inst.components.health.currenthealth > 0

	--here we verify that the owner is valid, that the variable IsGrabbedTight is true, and we verify that the item is not throwable like blowdarts or water balloons
	--yes, without that last one, mighty Wolfgang was completely unable to use blowdarts or water balloons, and yes, this items can be force dropped by bearger and gmoose, but is a sacrifice I am willing to take
	if item.components.inventoryitem.owner ~= nil and IsGrabbedTight and not item:HasTag("projectile") then
		return
	end
	
	--And here we return the original DropItem function with all 5 parameters
	--If you don't give it the 5 parameters, it will not work properly, despite the original DropItem function only having 4
	--If you don't return it all the characters will always say the error message that "they cannot do that", because it will be ending in this new DropItem function, without returning anything (so the game considers it a nil/error, despite actually dropping items successfully)
	return old_DropItem(self, item, wholestack, randomdir, pos)

end

Of course, I don't know if this works with wetness, still need to test for it. Even then I expect it to work normally

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