. . . Posted March 7, 2019 Share Posted March 7, 2019 Hello, I need help trying to fix something cause I am too dumb to do it code Spoiler local function Fail_Craft(inst, data) local item = data.item if math.random() <= inst.craft_fail then inst.components.talker:Say("Oops!") inst.components.inventory:DropItem(item) if not item.components.lootdropper then item:AddComponent("lootdropper") end item.components.lootdropper:DropLoot() item:Remove() end end master_postinit inst:ListenForEvent("builditem", Fail_Craft) inst.craft_fail = 1 I'm helping a friend and they want their character to have a chance to fail a crafting recipe causing it to break and getting half the resources back (basically like when hammering stuff), but when you craft a recipe which gives more than 1 product it gives out this crash and I don't know how to fix it crash Spoiler [00:00:58]: [string "scripts/components/inventoryitem_replica.lu..."]:152: attempt to index field 'classified' (a nil value) LUA ERROR stack traceback: scripts/components/inventoryitem_replica.lua:152 in (method) SetPickupPos (Lua) <146-154> scripts/components/stackable.lua:6 in (field) ? (Lua) <3-8> scripts/class.lua:30 in () ? (Lua) <23-32> scripts/components/stackable.lua:55 in (method) SetStackSize (Lua) <53-57> scripts/components/builder.lua:468 in () ? (Lua) <388-509> any help getting it to work would be great Link to comment Share on other sites More sharing options...
Ultroman Posted March 8, 2019 Share Posted March 8, 2019 Take a look at the reporting line 468 in builder.lua, and read the comment above it. --We still need to give the player the original product that was spawned, so do that. self.inst.components.inventory:GiveItem(prod, nil, pt) What happens is: you remove the item from the world when the event is pushed on line 438, but you have no way of stopping the game code in builder.lua's DoBuild function from continuing after that, so the code continues with an item which has effectively been removed from the world, which causes a crash down the line. One way to fix this, would be to simply delay your code from running until next tick, where all the DoBuild code has finished, and the item has actually been given to the player. You can do this by wrapping the code in this: self.inst:DoTaskInTime(0, function(inst) -- Your code end) This will result in some trouble, though, if the item is stackable (see lines 445-448 in builder.lua). Then the instance of the item (data.item) that you're working with, won't be the correct instance if there was already a stack of it in the inventory. Then you have to find out where the stack of it is, how many are crafted per "build", and then remove that number of items from the stack. You should be able to find some function or code in inventory.lua or builder.lua which does this for you, though, since the builder-component can figure it out when you craft things. I think it might be line 418 in builder.lua: self:RemoveIngredients(materials, recname) As you can see in the code surrounding this line, builds can be buffered on the replica, which I don't really know how works or influences what you're doing. PERHAPS SOMEONE ELSE CAN TELL YOU ABOUT REPLICA PROBLEMS !??! Link to comment Share on other sites More sharing options...
YakumoYukari Posted March 10, 2019 Share Posted March 10, 2019 data.item from the event builditem could be the reference of items that are already in inventory as Ultroman said. I consider this as "Loss of information". That is, data.item is not what we are looking for. Further processing based on your code will require more information to be needed or processed. So I decided to create new prefabs instead of getting the reference of data.item. And move it to the player then lootdrop it. Also, This code works with recipes creating structures or walls. Here's the code. local FAIL_CHANCE = 1 local function Fail_Craft(inst) local _Dobuild = inst.components.builder.DoBuild function inst.components.builder.DoBuild(self, recname, ...) if math.random() <= FAIL_CHANCE then local recipe = GetValidRecipe(recname) local item = SpawnPrefab(recipe.product) or nil if item == nil then return end item.Transform:SetPosition(inst.Transform:GetWorldPosition()) if not item.components.lootdropper then item:AddComponent("lootdropper") end if item.components.stackable ~= nil then item.components.stackable:SetStackSize(recipe.numtogive) end inst.components.talker:Say("Oops!") item.components.lootdropper:DropLoot() item:Remove() return true -- to prevent action fail speech. else return _Dobuild(self, recname, ...) end end end master_postinit Fail_Craft(inst) And don't mind of FAIL_CHANCE part because my code is copy-pasted that I just tested. Link to comment Share on other sites More sharing options...
. . . Posted March 21, 2019 Author Share Posted March 21, 2019 (edited) @YakumoYukari Hello again, thanks for your help, but my friend seems to be having some glitches with this code and I don't know how to help them fix it so if you know how to fix it that would be great ! Basically, when you "fail" a recipe the items it cost to make the recipe aren't consumed, but the items the recipe should drop when it "breaks" still drop basically duping the items lol Edited March 21, 2019 by Warbucks Link to comment Share on other sites More sharing options...
Ultroman Posted March 22, 2019 Share Posted March 22, 2019 Yeah, there's nothing in the fail code there about consuming the items. You should be able to find the lines which consume the items in the original DoBuild function, and copy/paste them into the fail-if-statement, Link to comment Share on other sites More sharing options...
YakumoYukari Posted March 22, 2019 Share Posted March 22, 2019 (edited) I didn't even assume such a typical case. My bad. I added some code that consumes the recipe ingredient correctly. if self.buffered_builds[recname] ~= nil then -- is bufferable? self.buffered_builds[recname] = nil self.inst.replica.builder:SetIsBuildBuffered(recname, false) -- turn the buffer off else self:RemoveIngredients(self:GetIngredients(recname), recipe) -- or Remove its ingredients end it comes from DoBuild(). So the full code would be this. local FAIL_CHANCE = 1 local function Fail_Craft(inst) local _Dobuild = inst.components.builder.DoBuild function inst.components.builder.DoBuild(self, recname, ...) if math.random() <= FAIL_CHANCE then local recipe = GetValidRecipe(recname) if self.buffered_builds[recname] ~= nil then self.buffered_builds[recname] = nil self.inst.replica.builder:SetIsBuildBuffered(recname, false) else self:RemoveIngredients(self:GetIngredients(recname), recipe) end local item = SpawnPrefab(recipe.product) if item == nil then return end item.Transform:SetPosition(inst.Transform:GetWorldPosition()) if not item.components.lootdropper then item:AddComponent("lootdropper") end if item.components.stackable ~= nil then item.components.stackable:SetStackSize(recipe.numtogive) end inst.components.talker:Say("Oops!") item.components.lootdropper:DropLoot() item:Remove() return true -- to prevent action fail speech. else return _Dobuild(self, recname, ...) end end end Edited March 22, 2019 by YakumoYukari Link to comment Share on other sites More sharing options...
. . . Posted April 16, 2019 Author Share Posted April 16, 2019 (edited) @YakumoYukari Hey, sorry to be so annoying, but the code works fine! I was just wondering if you know a way to prevent crafting recipes which have placers from being able to break via this code since it makes somethings like crafting alchemy engines and base building really annoying, thanks! Edited April 20, 2019 by Warbucks Link to comment Share on other sites More sharing options...
YakumoYukari Posted April 20, 2019 Share Posted April 20, 2019 This code would work fine local FAIL_CHANCE = 1 local function Fail_Craft(inst) local _Dobuild = inst.components.builder.DoBuild function inst.components.builder.DoBuild(self, recname, pt, rotation, skin) if math.random() <= FAIL_CHANCE then local recipe = GetValidRecipe(recname) if self.buffered_builds[recname] ~= nil then -- is bufferable? return _Dobuild(self, recname, pt, rotation, skin) end self:RemoveIngredients(self:GetIngredients(recname), recipe) -- or Remove its ingredients local item = SpawnPrefab(recipe.product, recipe.chooseskin or skin, nil, self.inst.userid) or nil if item == nil then return end item.Transform:SetPosition(inst.Transform:GetWorldPosition()) if not item.components.lootdropper then item:AddComponent("lootdropper") end if item.components.stackable ~= nil then item.components.stackable:SetStackSize(recipe.numtogive) end self.inst:PushEvent("refreshcrafting") inst.components.talker:Say("Oops!") item.components.lootdropper:DropLoot() item:Remove() return true -- to prevent action fail speech. else return _Dobuild(self, recname, pt, rotation, skin) end end end 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