SakkeEU Posted September 22, 2020 Share Posted September 22, 2020 Hi, i'm trying to make the game not crash when it calls my unmodified copy-pasted version of the its OnGetItem and DigestFood functions. This is the code: local function DigestFood(inst, food) --nothing changed end local function OnGetItem(inst, giver, item) --nothing changed end AddPrefabPostInit("birdcage", function(inst) inst.components.trader.onaccept = OnGetItem end) but this is the result: Quote [string "../mods/monster_meat_nerf/modmain.lua"]:96: attempt to index field 'trader' (a nil value) I tried to print the content of the components table but nothing came out, so I'm clearly missing something. The ultimate goal of the mod is to make the bird die after eating too much monster meat in a single day. This is my first time coding in lua and it shows I guess, help please? Link to comment Share on other sites More sharing options...
Serpens Posted September 22, 2020 Share Posted September 22, 2020 (edited) that line with trader you posted has no problem. I tested a modmain with only that code: AddPrefabPostInit("birdcage", function(inst) inst.components.trader.onaccept = function() end end) and it worked fine (since funciton is empty, nothing happend onaccept) So you messed up the birdcage somewhere else in your code. After you found the problem, you should save the old function, unless you want a completely different one. So do sth like this: AddPrefabPostInit("birdcage", function(inst) local old_onaccept = inst.components.trader.onaccept inst.components.trader.onaccept = function(inst, giver, item,...) if item.prefab=="monstermeat" then --kill bird elseif old_onaccept~=nil then return old_onaccept(inst,giver,item,...) -- call the old unchanged function otherwise. the "..." will make sure that this still works if the devs add a fourth param to the funciton. end end end) Edited September 22, 2020 by Serpens Link to comment Share on other sites More sharing options...
SakkeEU Posted September 23, 2020 Author Share Posted September 23, 2020 (edited) I tried to run your sample code Spoiler AddPrefabPostInit("birdcage", function(inst) inst.components.trader.onaccept = function() end end) in a new mod folder but the game kept crashing anyway, so I made a clean install and everything started working again. Thank you for the tips! Edited September 23, 2020 by SakkeEU Link to comment Share on other sites More sharing options...
SakkeEU Posted September 25, 2020 Author Share Posted September 25, 2020 (edited) Hello again, I have a another couple of questions about birds and birdcages: To make sure the bird dies after eating too much monster meat I added a new component to the birds prefabs: local birds_prefabs = {"crow","robin","robin_winter","canary","quagmire_pigeon","puffin"} for _,v in pairs(birds_prefabs) do AddPrefabPostInit(v, function(inst) inst:AddComponent("weakstomach") end) end Everything works fine but to make sure that if a new bird is added the game doesn't crash I wrote this: local function OnGetItem(inst, giver, item,...) local bird = inst.components.occupiable and inst.components.occupiable:GetOccupant() --add weakstomach to this bird prefab and this bird entity if its not defined if bird.components.weakstomach == nil then AddPrefabPostInit(bird.prefab, function(inst) inst:AddComponent("weakstomach") end) -- #1 bird:AddComponent("weakstomach") -- #2 end --check if monstermeat is eaten if item.prefab == "cookedmonstermeat" or item.prefab == "monstermeat_dried" then if bird.components.weakstomach:GetWS() > 0 then -- get weakstomach counter bird.components.weakstomach:DecWS() --decrese ws counter end end --if enough monstermeat is eaten, the bird dies if bird.components.weakstomach:GetWS() == 0 and (item.prefab == "cookedmonstermeat" or item.prefab == "monstermeat_dried") then BirdDeath(inst, bird) --return old function, the bird survives elseif old_onaccept~=nil then return old_onaccept(inst,giver,item,...) end end #1 should add the component to the new prefab while #2 adds it to the current entity. #2 works fine but #1 doesn't, birds that are not present in the birds_prefabs always spawn without the custom component. It's not a big deal, the mod works but it doesn't feel right to leave it this way. Another problem I found: if I keep feeding the bird while he's still "digesting" the first monstermeat it doesn't die and no loot is dropped, resulting in wasted monstermeats. I tried something like this: local function OnGetItem(inst, giver, item,...) inst.components.trader:SetAbleToAcceptTest(function(...) return false end) inst.components.trader:Disable() -- --same same -- elseif old_onaccept~=nil then old_onaccept(inst,giver,item,...) end inst.components.trader:SetAbleToAcceptTest(nil) inst.components.trader:Enable() end But nothing changes. Help again, please? Edited September 25, 2020 by SakkeEU Link to comment Share on other sites More sharing options...
Serpens Posted September 25, 2020 Share Posted September 25, 2020 I did not yet look deeper ito the birds feeding mechanism, but AddPrefabPostInit should only be used at the very beginning of the game, so when the script is first executed. You dont use it inside a funciton like GetItem. This is because AddPrefabPostInit, like the name suggests, changes a prefab, not an entity. So if you do AddPrefabPostInit in your modmain outside of any function, it will affect all entities that have this prefab, as soon as they spawn (so neither #1 nor #2 should be needed). It should be enough to add this component only once within AddPrefabPostInit and nowhere else. But I don't know exactly how the birdcage and the birds within interact. Similar for your SetAbleToAcceptTest, you should define it elsewhere, eg in AddPRefabPostInit from the birdcage. And set it to a function that checks eg. a Tag or a value within bird or your component. Link to comment Share on other sites More sharing options...
SakkeEU Posted September 25, 2020 Author Share Posted September 25, 2020 13 minutes ago, Serpens said: I did not yet look deeper ito the birds feeding mechanism, but AddPrefabPostInit should only be used at the very beginning of the game, so when the script is first executed. You dont use it inside a funciton like GetItem. This is because AddPrefabPostInit, like the name suggests, changes a prefab, not an entity. So if you do AddPrefabPostInit in your modmain outside of any function, it will affect all entities that have this prefab, as soon as they spawn (so neither #1 nor #2 should be needed). It should be enough to add this component only once within AddPrefabPostInit and nowhere else. But I don't know exactly how the birdcage and the birds within interact. The goal of #1 was to make the mod compatible with other mods that add new bird prefabs by adding the new component to the new prefabs once and for all, #2 works on the entity in the cage but it's kinda cheap. I really don't know how to check if there are custom bird prefabs without my component outside of any function. 39 minutes ago, Serpens said: Similar for your SetAbleToAcceptTest, you should define it elsewhere, eg in AddPRefabPostInit from the birdcage. And set it to a function that checks eg. a Tag or a value within bird or your component. The problem with SetAbleToAcceptTest is that it has priority over important stuff and needs to be set to nil again after the check is run (or that's how I read the following snippet of code), I don't know if I can do this from an AddPrefabPostInit. This is from trader.lua: --This only comes into play after passing AbleToAccept, --and does not trigger action fail or reason. function Trader:SetAcceptTest(fn) self.test = fn end --This can be used to override AbleToAccept test to --trigger custom action fail with reason. function Trader:SetAbleToAcceptTest(fn) self.abletoaccepttest = fn end -- Able to accept refers to physical ability, i.e. am I in combat, or sleeping, or dead function Trader:AbleToAccept(item, giver) if not self.enabled or item == nil then return false elseif self.abletoaccepttest ~= nil then return self.abletoaccepttest(self.inst, item, giver) elseif self.inst.components.health ~= nil and self.inst.components.health:IsDead() then return false, "DEAD" elseif self.inst.components.sleeper ~= nil and self.inst.components.sleeper:IsAsleep() then return false, "SLEEPING" elseif self.inst.sg ~= nil and self.inst.sg:HasStateTag("busy") then return false, "BUSY" end return true end Link to comment Share on other sites More sharing options...
Serpens Posted September 25, 2020 Share Posted September 25, 2020 (edited) ? Use AddPrefabPostInitAny, which will run for every prefab. Then check inst:HasTag("bird") to add your component for all birds. This will also catch mod birds, if they have that tag (and I guess all of modded birds will have it). I don't know the difference about self.test and self.abletoaccepttest , but use the one that suits your needs best. As always you can remember the old fn, if there was one. And of course you can set one of this test funtions to a function that returns false, if there is a bird and that bird has your component and according to your component has eaten too much meat. And return the old fn otherwise (if~=nil) or true. There is no reason to change that function more than once, you only set it up and make sure that the function can react variable to the situation (by checking your component within the function) Edited September 25, 2020 by Serpens 1 Link to comment Share on other sites More sharing options...
SakkeEU Posted September 25, 2020 Author Share Posted September 25, 2020 25 minutes ago, Serpens said: Use AddPrefabPostInitAny, which will run for every prefab. Then check inst:HasTag("bird") to add your component for all birds. This will also catch mod birds, if they have that tag (and I guess all of modded birds will have it). Oh I didn't know of this function, thank you very much! 27 minutes ago, Serpens said: I don't know the difference about self.test and self.abletoaccepttest , but use the one that suits your needs best. As always you can remember the old fn, if there was one. And of course you can set one of this test funtions to a function that returns false, if there is a bird and that bird has your component and according to your component has eaten too much meat. And return the old fn otherwise (if~=nil) or true. There is no reason to change that function more than once, you only set it up and make sure that the function can react variable to the situation (by checking your component within the function) Ok, I'll see what I can do thank you! Link to comment Share on other sites More sharing options...
Serpens Posted September 25, 2020 Share Posted September 25, 2020 53 minutes ago, SakkeEU said: Ok, I'll see what I can do thank you! sry, my mistake. of course you are right that if the function self.abletoaccepttest is not nil, then the other stuff below, like sleeping/dead and so on is not checked. Without looking further into the code I would say this means you should use another function, maybe the self.test function instead, if this works better. And about the birds: You can also attach your component to the birdcage instead and count how often there was monstermeat. And reset this counter if the bird changed. This should be easier, although a user could exploit it with help of 2 birdcages and switching the birds all the time But this is up to you. Link to comment Share on other sites More sharing options...
SakkeEU Posted September 25, 2020 Author Share Posted September 25, 2020 (edited) 1 hour ago, Serpens said: And about the birds: You can also attach your component to the birdcage instead and count how often there was monstermeat. And reset this counter if the bird changed. This should be easier, although a user could exploit it with help of 2 birdcages and switching the birds all the time But this is up to you. I tried to make it this way before realizing how easy it was to exploit and changing route. It's actually very ugly to implement because the occupiable component of the cage is removed and readded when the bird dies, so it needs a lot of copy-pasting. I'll make it work with the birds in one way or another! : ) Edited September 25, 2020 by SakkeEU Link to comment 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