Corrosive Posted March 22, 2015 Share Posted March 22, 2015 (edited) [disregard this post-- brain error occured] @ScallyCat, @Kzisor,Here was the code provided(I have not changed this):local function OnEquip(inst, owner, fname_override) inst.components.equippable:_OnEquip(inst, owner, fname_override) if owner:HasTag("monster") then owner:RemoveTag("monster") endend local function onUnequip(inst, owner) inst.components.equippable:_OnUnequip(inst, owner) owner:AddTag("monster") if inst.components.leader then inst.components.leader:RemoveFollowersByTag("pig") endend local function TestHat(inst, owner) inst.components.equippable._OnEquip = inst.compoennts.equippable.onequipfn inst.components.equippable:SetOnEquip(OnEquip) inst.components.equippable._OnUnequip = inst.components.equippable.onunequipfn inst.components.equippable:SetOnUnequip(onUnequip) end Note that when you call the OnEquip and OnUnequip methods, you are using the colon( : ) syntax.inst.components.equippable:_OnEquip(inst, owner, fname_override)Colon syntax is used with methods when you want to pass self as the first parameter. It's syntactic sugar(i.e. "it means the exact same thing") as this: inst.components.equippable._OnEquip(inst.components.equippable, inst, owner, fname_override)Note that that second form uses a period ( . ) instead of the colon.Knowing this, you need to allow for self to be the first parameter in your replacement functions: local function OnEquip(self, inst, owner, fname_override) inst.components.equippable:_OnEquip(inst, owner, fname_override) if owner:HasTag("monster") then owner:RemoveTag("monster") endendlocal function onUnequip(self inst, owner) inst.components.equippable:_OnUnequip(inst, owner) owner:AddTag("monster") if inst.components.leader then inst.components.leader:RemoveFollowersByTag("pig") endendWhat was happening before was that since the "self" parameter was not defined in your replacement function, all of the arguments were being shifted over 1 parameter similar to like this: function somefunction(inst, owner, fnameoverride) ..blah blah..endsomefunction(self, inst, owner, fname_override)As you can see, self is getting passed to the inst parameter, inst is getting passed to the owner parameter, and owner is getting passed to the fname_override parameter. The fname_override argument is getting dropped, because there are no more parameters to assign it to. Hence the game receiving a table when it expected a string. Edited March 23, 2015 by Corrosive Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624087 Share on other sites More sharing options...
Kzisor Posted March 22, 2015 Share Posted March 22, 2015 @Corrosive, I applaud your efforts of trying to debug this, you are actually incorrect in your solution to the problem. However, you did bring up a great point about the colon and period syntax and as such I've looked back over the code in the equippable component and have determined the required change to make it work. local function OnEquip(inst, owner, fname_override) inst.components.equippable._OnEquip(inst, owner, fname_override) if owner:HasTag("monster") then owner:RemoveTag("monster") endend local function onUnequip(inst, owner) inst.components.equippable._OnUnequip(inst, owner) owner:AddTag("monster") if inst.components.leader then inst.components.leader:RemoveFollowersByTag("pig") endend local function TestHat(inst, owner) inst.components.equippable._OnEquip = inst.components.equippable.onequipfn inst.components.equippable:SetOnEquip(OnEquip) inst.components.equippable._OnUnequip = inst.components.equippable.onunequipfn inst.components.equippable:SetOnUnequip(onUnequip) end By simply changing it from a colon to a period will ensure that it gets pushed appropriately. The reason is because we are dealing with a local function inside the original code therefore our code need not pass the self parameter. Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624106 Share on other sites More sharing options...
Corrosive Posted March 23, 2015 Share Posted March 23, 2015 (edited) By simply changing it from a colon to a period will ensure that it gets pushed appropriately. The reason is because we are dealing with a local function inside the original code therefore our code need not pass the self parameter. You're right-- although the reason is more that Klei treats methods with setters more like functions rather than methods. Which makes more sense conceptually. And also presumably since you can't have the same component on an entity more than once, inst will always be specific to the correct component.function Equippable:Equip(owner, slot) self.isequipped = true if self.inst.components.burnable then self.inst.components.burnable:StopSmoldering() end if self.onequipfn then self.onequipfn(self.inst, owner) end self.inst:PushEvent("equipped", {owner=owner, slot=slot})endIf equippable.lua called the stored function using the colon syntax there, you'd need to deal with a self parameter on overrides. Edited March 23, 2015 by Corrosive Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624115 Share on other sites More sharing options...
Kzisor Posted March 23, 2015 Share Posted March 23, 2015 @Corrosive, I think you are still incorrect with your explanation. Functions and methods are the exact same thing in programming terminology. In lua a function is simply another variable, whether it be stored as a global, in a table, as a local or not at all it's still just another variable. The calling method plays no part in whether or not the function will handle the self parameter; it simply dictates if we want to auto forward the self parameter to the function. It's up to the original function itself to handle the self parameter in order to process the data properly. Example:This line of initialization code cannot be called by using a colon syntax because it originally is not handling the self parameter.local function onequip(inst, owner, fname_override) It would not matter whether the equippable.lua component was calling it using colon syntax or if our code was calling it using the colon syntax. The function itself is not handling the self parameter therefore in both instances you would receive the error. If the equippable.lua component was originally calling it via the colon syntax or if the function was a global function (Equippable:OnEquip) of the component my original code would have worked correctly. Also to clarify, we should not actually need to handle the self parameter in any case not even in our override function. The self parameter should be handled automatically when you make a function which is accessible globally from a class or component, e.g., function Equippable:SetOnEquip(fn). This is the reason why you can access self outside of the component file when overriding these functions; because it's automatically handled by the compiler. Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624183 Share on other sites More sharing options...
Corrosive Posted March 23, 2015 Share Posted March 23, 2015 @Kzisor, I'm aware that functions are first-class values in Lua. However, the Lua reference manual explicitly defines methods in Lua as using the colon syntax: The colon syntax is used for defining methods, that is, functions that have an implicit extra parameter self. Thus, the statementfunction t.a.b.c:f (params) body endis syntactic sugar fort.a.b.c.f = function (self, params) body end Anyhoo-- This is the reason why you can access self outside of the component file when overriding these functions; because it's automatically handled by the compiler. Not... quite... the interpreter isn't giving you access to anything you don't already have access to. It's just passing whatever table is the parent of the method. Ex:> tab_a = { val = "Table A" }> function tab_a:fn() print(self.val) end> tab_b = { val = "Table B" }> function tab_b:fn() print(self.val) end>> tab_a:fn()Table A> tab_b:fn()Table B> tab_a.fn = tab_b.fn>> tab_a:fn()Table A> tab_b:fn()Table BAlso to clarify, we should not actually need to handle the self parameter in any case not even in our override function. If the stored onequipfn was called via colon syntax, e.g.:self:onequipfn(self.inst, owner)...it would send self as the first parameter to your stored function, in which case you would absolutely need to have a parameter for it. It doesn't matter if you are using the self argument or not, it still gets passed. Since you are declaring the function and assigning it via a setter, you aren't able to use colon syntax. You could alternatively bypass the setter and use colon syntax: function Equippable:onequipfn(inst, owner) or you could even create a dummy table and use it to allow you to add the self parameter via colon syntax: t = {}function t:fn(inst, owner)Equippable:SetOnEquip(t.fn)All this is moot, though, since that's not how Klei rolls. Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624204 Share on other sites More sharing options...
Kzisor Posted March 23, 2015 Share Posted March 23, 2015 If the stored onequipfn was called via colon syntax, e.g.:self:onequipfn(self.inst, owner)...it would send self as the first parameter to your stored function, in which case you would absolutely need to have a parameter for it. It doesn't matter if you are using the self argument or not, it still gets passed. Since you are declaring the function and assigning it via a setter, you aren't able to use colon syntax. Actually this is inaccurate information. Example: inst._GiveItem = inst.GiveItem function inst:GiveItem( inst, slot, screen_src_pos, skipsound ) if not self:CanTakeItemInSlot(inst, slot) then return false end return self:_GiveItem(inst, slot, screen_src_pos, skipsound ) end I understand that you know Lua, but your explanation of it is inaccurate. Using a setter has nothing to do with whether or not you can use the colon syntax, it's completely up to how the original code was initialized. In the working example it's initialized already containing the colon syntax, therefore it's automatically handling the self parameter. We could even override that function as:inst._GiveItem = inst.GiveItem function inst.GiveItem( self, inst, slot, screen_src_pos, skipsound ) if not self:CanTakeItemInSlot(inst, slot) then return false end return self:_GiveItem( inst, slot, screen_src_pos, skipsound ) end This code would still function correctly. As would this code:inst._GiveItem = inst.GiveItem function inst.GiveItem( self, inst, slot, screen_src_pos, skipsound ) if not self:CanTakeItemInSlot(inst, slot) then return false end return self._GiveItem( self, inst, slot, screen_src_pos, skipsound ) end All that using a setter accomplishes is it allows you to set a function to be called at a later date. The initialization of the setter calling method dictates how we propagate the actual setter function value. All this is not rendered moot because of Klei, it's all very relevant because Klei uses both types of calling conventions in their code. Either way, the problem should be fixed. Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624247 Share on other sites More sharing options...
Corrosive Posted March 23, 2015 Share Posted March 23, 2015 @Kzisor, I think you misunderstood what I meant. I did not mean that you can't achieve a working effect by using a setter. I meant that you have nowhere to put a colon when using a setter. We could even override that function as: ... ...I know, that was literally how I erroneously recommended to do it in the first place! Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624321 Share on other sites More sharing options...
Kzisor Posted March 23, 2015 Share Posted March 23, 2015 I did not mean that you can't achieve a working effect by using a setter. I meant that you have nowhere to put a colon when using a setter. As I said before this is incorrect information. In a general sense you can in fact use the colon syntax with a setter, however, in this particular instance you are correct we cannot. It's better to be precise with what you're talking about so that you do not misinform people of how Lua actually works. If you do not believe that we cannot use a colon with setters, simply look at the examples I posted above, all three of them are working examples. Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624332 Share on other sites More sharing options...
ScallyCat Posted March 23, 2015 Author Share Posted March 23, 2015 As I said before this is incorrect information. In a general sense you can in fact use the colon syntax with a setter, however, in this particular instance you are correct we cannot. It's better to be precise with what you're talking about so that you do not misinform people of how Lua actually works. If you do not believe that we cannot use a colon with setters, simply look at the examples I posted above, all three of them are working examples. @Kzisor, I think you misunderstood what I meant. I did not mean that you can't achieve a working effect by using a setter. I meant that you have nowhere to put a colon when using a setter. ...I know, that was literally how I erroneously recommended to do it in the first place!Wow you guys make my head spin! Seriously though, thanks so much to the both of you Everything seems to be working perfectly now! This was definitely a neat little learning experience I'll be sure to post my mod here on the forums upon it's completion. (Also nice Mir(?) picture) Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624334 Share on other sites More sharing options...
Corrosive Posted March 23, 2015 Share Posted March 23, 2015 I've been phrasing my arguments to apply to our single-case scenario. If you do not believe that we cannot use a colon with setters, simply look at the examples I posted above, all three of them are working examples. or you could even create a dummy table and use it to allow you to add the self parameter via colon syntax:t = {}function t:fn(inst, owner)Equippable:SetOnEquip(t.fn) somehow I managed to leave out the word end on line 2 there, but you get the point. Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624343 Share on other sites More sharing options...
Kzisor Posted March 23, 2015 Share Posted March 23, 2015 (edited) somehow I managed to leave out the word end on line 2 there, but you get the point. That code would not work the way you vision it to work. The problem was not with the code we are setting in our TestHat function but, with the code we are using to call the original code in our OnEquip function which in this instance is inst.components.equippable._OnEquip. Edited March 23, 2015 by Kzisor Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624367 Share on other sites More sharing options...
Blueberrys Posted March 23, 2015 Share Posted March 23, 2015 (edited) For anyone who comes across this and wants to understand what's going on, here's a simplified (but long) summary. One argument is about the places you can or can't use colons.Say we have this module:local t = {}function t:some_fn() -- self is automatically available due to the colon print(self)endYou can do this:t.some_fn2 = t.some_fnYou can not do any of these:t:some_fn2 = t:some_fn2t.some_fn2 = t:some_fn2t:some_fn2 = t.some_fn2 Another point is about the fundamental use of colons.Now we have this module (same from above):local t = {}function t:some_fn() -- self "should" refer to (an instance of) t -- but in practice, anything can be passed as the first parameter -- and it will be used as "self" print(self)endThese will give you the same results:t:some_fn() -- prints table t's idt.some_fn(t) -- prints table t's idBut this will be different:local new_t = {}t.some_fn(new_t) -- prints table new_t's idNote that using a colon to call a function automatically passes the parent table as the first parameter.t:some_fn() -- passes "t" as the first parameterMore on this here. And finally, another point is about modifying a module to call your function how you want (passing the variables you need), instead of how it was previously designed to be called.You have this module:local t = {}-- Ideally, this should be set outside the module, but we'll keep it here for simplicityt.custom_fn = function() print("fn 2")endfunction t:some_fn() -- again, self is available due to the colon return self.custom_fnendYou can do this:-- We want a number to be passed in custom_fn-- So first we must be able to receive itt.custom_fn = function(num) print("Our number is:", num)end-- Now we want some_fn to pass the number-- First preserve the old some_fnlocal old_some_fn = t.some_fn-- Then replace some_fn with a new functionfunction t:some_fn() -- Call the custom_fn, passing a number self.custom_fn(123) -- We must pass self explicitly because old_some_fn -- doesn't have a parent so a colon can not be used. -- Also note that the old some_fn already calls custom_fn -- so it will be called twice. return old_some_fn(self)end Which means you can also do this:t.custom_fn_2 = function(num) print("Our number is:", num)end-- Preserve the old some_fnlocal old_some_fn = t.some_fnfunction t:some_fn() -- Call our new function, passing a number self.custom_fn_2(123) return old_some_fn(self)end Edited March 23, 2015 by Blueberrys Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624387 Share on other sites More sharing options...
Corrosive Posted March 23, 2015 Share Posted March 23, 2015 Thank you Blueberrys, you understood what my late night ramblings meant to convey @Kzisor, Kz man, I swear you are going to make me cry. Every response you seem to slightly shift the focus. My original statement was that if the code calling the stored onequipfn, i.e. if self.onequipfn then self.onequipfn(self.inst, owner) end...were actually written thus: if self.onequipfn then self:onequipfn(self.inst, owner) endThe function that you create which you are storing in the onequipfn field would need to account for self being passed to the first parameter. I don't care how you create it, colon syntax or not-- it would need to account for that extra parameter or else your arguments are going to be shifted over by 1. Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624392 Share on other sites More sharing options...
Kzisor Posted March 24, 2015 Share Posted March 24, 2015 Thank you Blueberrys, you understood what my late night ramblings meant to convey @Kzisor, Kz man, I swear you are going to make me cry. Every response you seem to slightly shift the focus. My original statement was that if the code calling the stored onequipfn, i.e. if self.onequipfn then self.onequipfn(self.inst, owner) end...were actually written thus: if self.onequipfn then self:onequipfn(self.inst, owner) endThe function that you create which you are storing in the onequipfn field would need to account for self being passed to the first parameter. I don't care how you create it, colon syntax or not-- it would need to account for that extra parameter or else your arguments are going to be shifted over by 1. The problem is what you're pointing out has zero (0) to do with the actual problem which was occurring. That is what I was trying to point out and is why I think we have a misunderstanding. The problem was in this line right here:inst.components.equippable:_OnEquip(inst, owner, fname_override) Because I was automatically sending the self parameter with the colon syntax to the old function is where we ran into issues. The old function was never meant to handle the self parameter. What you were saying is that I should account for that in my function by adding self to our onequip function, which would actually cause more errors because the original code never sends us the self parameter. Corrosives Original Code:local function OnEquip(self, inst, owner, fname_override) inst.components.equippable:_OnEquip(inst, owner, fname_override) if owner:HasTag("monster") then owner:RemoveTag("monster") endend local function onUnequip(self inst, owner) inst.components.equippable:_OnUnequip(inst, owner) owner:AddTag("monster") if inst.components.leader then inst.components.leader:RemoveFollowersByTag("pig") endend If we were to used your original code, we would have still had the issue because all you did was add self as a parameter to our local function. Our local function overrides the old function, but still calls it by simply moving it to a different variable (_OnEquip). I'm okay with actually chalking it up to a misunderstanding because I think that is clearly what happened. Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624432 Share on other sites More sharing options...
Corrosive Posted March 24, 2015 Share Posted March 24, 2015 (edited) @Kzisor, I think you missed first two words of my first response in which I stated "you're right." I was trying (poorly) to explain what had gone wrong in my mind*, not your explanation. *Edit: Which was that equippable doesn't call the stored custom function using colon syntax. Edited March 24, 2015 by Corrosive Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624506 Share on other sites More sharing options...
Kzisor Posted March 24, 2015 Share Posted March 24, 2015 @Corrosive, yeah see, a complete misunderstanding. Sometimes communication is difficult when talking about code so it's understandable that we would definitely have a misunderstanding about it. It's no skin off my bones, I just hope that if other people read this they will understand what is actually happening and that it helps them to become a better modder. After all that is what we both want. Link to comment https://forums.kleientertainment.com/forums/topic/52127-testing-hats/page/2/#findComment-624590 Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now