zetake Posted July 17, 2019 Author Share Posted July 17, 2019 I have "dropitem" done same way as "itemget". And "itemlose" trigger when it's putted to mouse. The problem is that I get these events only from container component... Link to comment Share on other sites More sharing options...
zetake Posted July 17, 2019 Author Share Posted July 17, 2019 I honestly don't have f...ing idea WHY/HOW, BUT it works. OMG (Even with stack items, when you take half[AGAIN IDK WHY/HOW]) inst:ListenForEvent("itemget", function(inst, data) local item = data and data.item local perishable = item.components.perishable or nil if perishable then item.components.perishable:StartISRPerishing() end local slot = data and data.slot inst:ListenForEvent("itemlose", function(inst, data2) if slot == data2.slot then if perishable then item.components.perishable:StartPerishing() end end end) end) On drop from container should work with ListenForEvent("dropitem", but not sure about saving or after some time, or if this bug/slow game... Require testing... Anyway thanks for help. P.S: Unorthodox thinking: 100 point for me. XD Link to comment Share on other sites More sharing options...
Ultroman Posted July 17, 2019 Share Posted July 17, 2019 With that code, you're going to end up with duplicate listeners for the "itemlose" event. You need to remove the listener again when it's called, which sadly requires you to also pass the function you hooked up to the listener, so we have to store it in a local variable. Also, "slot" needs to be stored, as well, since simply making it local for the inventory would break everything if you put in one item and take out another. You'd have to do something like this: local myFunction = function(inst, slot) if slot == nil then return end local tempfunc = function(inst, data2) if slot == data2.slot then if perishable then item.components.perishable:StartPerishing() inst:RemoveEventCallback("itemlose", tempfunc) end end end inst:ListenForEvent("itemlose", tempfunc) end inst:ListenForEvent("itemget", function(inst, data) local item = data and data.item local perishable = item.components.perishable or nil if perishable then item.components.perishable:StartISRPerishing() end slot = data and data.slot myFunction(inst, slot) end) I'm not 100% sure this'll work, and there are definitely still some edge-cases you need to take care of. There are many ways in which an item can be put into, or taken out of, an inventory. There are also the overflow containers to test with, and putting things into a backpack sitting on the ground. Sooo many things. Break out your be print-statements printing everything, and set up a slew of test-cases and go through them one by one, checking that your print-statements and the game say and do as you expect. It's pretty important, because it's difficult to tell while playing the game whether the mod is doing what it's supposed to. At some point, someone may message you "hey, my food suddenly lasts forever" or "My game now lags every time I click an item in your container." Link to comment Share on other sites More sharing options...
zetake Posted July 17, 2019 Author Share Posted July 17, 2019 My script is just better? This part if slot == data2.slot then prevents to do fn on all same items in container... And this one inst:ListenForEvent("itemlose", function(inst, data2) shouldn't be ended if you take only half of a stack... #EDIT: This part of yours if slot == nil then return end is pointless, because there will be always slot when you put item to slot in container... logic, right? Only problem is to not duplicate listener "itemlose" up to max container slots... Is there fn to detect if lister is already running? Or I need to save item and slot from "itemget" somewhere... Link to comment Share on other sites More sharing options...
Ultroman Posted July 17, 2019 Share Posted July 17, 2019 True. But the problem with duplicate event listeners is still there. You have to take care of that. It's a memory/performance leak, not to mention that it'll call StartPerishing() once for every event listener. You can test this by putting a print-statement in your "itemlose" function, and then continuously putting the same stack into your container and taking it out again. You should get duplicate prints the more times you do it. Link to comment Share on other sites More sharing options...
zetake Posted July 17, 2019 Author Share Posted July 17, 2019 Yes, every "itemget" was creating new "itemlose" event... Oh, well. I just overrided Container:RemoveItemBySlot(slot) and Container:RemoveItem(item, wholestack). Now it returns item... and works perfect? Probably... This override is better? Sure... Link to comment Share on other sites More sharing options...
Ultroman Posted July 17, 2019 Share Posted July 17, 2019 As long as you chained the original functions, instead of copying and overwriting them, then it's probably fine (barring any problems with the way you've done this whole thing). Link to comment Share on other sites More sharing options...
zetake Posted July 17, 2019 Author Share Posted July 17, 2019 I didn't, I don't see how I could accomplish this... Spoiler AddClassPostConstruct("components/container", function(self, inst) function self:RemoveItemBySlot(slot) if slot and self.slots[slot] then local item = self.slots[slot] if item then self.slots[slot] = nil if item.components.inventoryitem then item.components.inventoryitem:OnRemoved() end self.inst:PushEvent("itemlose", {item = item, slot = slot}) -- changed here end item.prevcontainer = self item.prevslot = slot return item end end function self:RemoveItem(item, wholestack) local dec_stack = not wholestack and item and item.components.stackable and item.components.stackable:IsStack() and item.components.stackable:StackSize() > 1 local slot = self:GetItemSlot(item) if dec_stack then local dec = item.components.stackable:Get() dec.prevslot = slot dec.prevcontainer = self return dec else for k,v in pairs(self.slots) do if v == item then self.slots[k] = nil self.inst:PushEvent("itemlose", {item = item, slot = k, boatequipslot = nil}) -- changed here if item.components.inventoryitem then item.components.inventoryitem:OnRemoved() end item.prevslot = slot item.prevcontainer = self return item end end if self.hasboatequipslots then for k,v in pairs(self.boatequipslots) do if v == item then --print("found item equipped!!!") --self.boatequipslots[k] = nil self.inst:PushEvent("itemlose", {item = item, slot = nil, boatequipslot = k}) -- changed here if item.components.inventoryitem then item.components.inventoryitem:OnRemoved() end self:Unequip(k) item.prevslot = slot item.prevcontainer = self return item end end end end return item end end) #EDIT: Maybe with detecting if item is in my container, but what's the point? Will someone edit this part too? Link to comment Share on other sites More sharing options...
Ultroman Posted July 17, 2019 Share Posted July 17, 2019 You don't need AddClassPostConstruct for any of this. You have your own container prefab, right? Within its fn() function, you can easily overwrite or chain the public functions in its components. And I don't see why you need to change the "itemlose" event pushes. The functions always return the item. This is how you chain/extend the functions. Put this in your container prefab's fn() function. local oldRemoveItem = inst.components.container.RemoveItem inst.components.container.RemoveItem = function(self, item, wholestack) local returnedItem = oldRemoveItem(self, item, wholestack) -- do whatever you want with the item, which MIGHT have been removed from the container. end local oldRemoveItemBySlot = inst.components.container.RemoveItemBySlot inst.components.container.RemoveItemBySlot = function(self, slot) local returnedItem = oldRemoveItemBySlot(self, slot) -- do whatever you want with the item, which MIGHT have been removed from the container. end Link to comment Share on other sites More sharing options...
zetake Posted July 18, 2019 Author Share Posted July 18, 2019 Because "itemlose" doesn't push item for event listener... (only slot) Well I knew this "chain/extend", but I wasn't aware I can use it like that... Ok now works wonderfull. Spoiler Fns Spoiler local function removeitembyslot(self, slot) if slot and self.slots[slot] then local item = self.slots[slot] if item then self.slots[slot] = nil if item.components.inventoryitem then item.components.inventoryitem:OnRemoved() end self.inst:PushEvent("itemlose", {item = item, slot = slot}) end item.prevcontainer = self item.prevslot = slot return item end end local function removeitem(self, item, wholestack) local dec_stack = not wholestack and item and item.components.stackable and item.components.stackable:IsStack() and item.components.stackable:StackSize() > 1 local slot = self:GetItemSlot(item) if dec_stack then local dec = item.components.stackable:Get() dec.prevslot = slot dec.prevcontainer = self return dec else for k,v in pairs(self.slots) do if v == item then self.slots[k] = nil self.inst:PushEvent("itemlose", {item = item, slot = k, boatequipslot = nil}) if item.components.inventoryitem then item.components.inventoryitem:OnRemoved() end item.prevslot = slot item.prevcontainer = self return item end end if self.hasboatequipslots then for k,v in pairs(self.boatequipslots) do if v == item then self.inst:PushEvent("itemlose", {item = item, slot = nil, boatequipslot = k}) if item.components.inventoryitem then item.components.inventoryitem:OnRemoved() end self:Unequip(k) item.prevslot = slot item.prevcontainer = self return item end end end end return item end Fn() Spoiler inst:ListenForEvent("itemget", function(inst, data) local item = data and data.item local perishable = item.components.perishable if perishable then perishable:StartISRPerishing() end end) inst:ListenForEvent("itemlose", function(inst, data) local item = data and data.item local perishable = item.components.perishable if perishable then perishable:StartPerishing() end end) inst:ListenForEvent("dropitem", function(inst, data) local item = data and data.item local perishable = item.components.perishable if perishable then perishable:StartPerishing() end end) local container = inst.components.container container.RemoveItemBySlot = removeitembyslot container.RemoveItem = removeitem and Custom Perish Spoiler local FRAMES = GLOBAL.FRAMES local TUNING = GLOBAL.TUNING local function Update(inst, dt) if inst.components.perishable then local modifier = 300 local old_val = inst.components.perishable.perishremainingtime local delta = dt or (10 + math.random()*FRAMES*8) inst.components.perishable.perishremainingtime = inst.components.perishable.perishremainingtime - delta*modifier if math.floor(old_val*100) ~= math.floor(inst.components.perishable.perishremainingtime*100) then inst:PushEvent("perishchange", {percent = inst.components.perishable:GetPercent()}) end --trigger the next callback if inst.components.perishable.perishremainingtime <= 0 then inst.components.perishable:Perish() end end end AddClassPostConstruct("components/perishable", function(self, inst) function self:StartISRPerishing() if self.updatetask then self.updatetask:Cancel() self.updatetask = nil end local dt = 10 + math.random()*FRAMES*8--math.max( 4, math.min( self.perishtime / 100, 10)) + ( math.random()* FRAMES * 8) if dt > 0 then self.updatetask = self.inst:DoPeriodicTask(dt, Update, math.random()*2, dt) else Update(self.inst, 0) end end end) Thanks a lot.. But I wonder what happens if function Perishable:LongUpdate(dt) if self.updatetask then Update(self.inst, dt or 0) end end kicks in, if kicks in? When LongUpdate is used??? And what means dt in it? Link to comment Share on other sites More sharing options...
Ultroman Posted July 18, 2019 Share Posted July 18, 2019 Yes, that is a problem. Right now, if someone uses LongUpdate() on the server, it'll use the original Update() function. Since it IS a public function in the perishable component, you CAN extend this, as well, check if there is an updatetask, also check whether the owner of the perishable item is your container (self.inst.components.inventoryitem.owner.prefab, obviously with a bunch of checks to make sure the component and owner is there), and if it is your container, then do your Update function instead and return immediately, and if not, call the original function. dt is the deltatime, the time between last frame and this frame. This was one of the many reasons I suggested you just make your mod put a modifier on the deltatime (dt) in both LongUpdate() AND StartPerishing(), which both get dt as a parameter. It would be super easy, and aaalmost what you want. Link to comment Share on other sites More sharing options...
zetake Posted July 18, 2019 Author Share Posted July 18, 2019 Something like this? local LongUpdate = self.LongUpdate function self:LongUpdate(dt) local owner = inst.components.inventoryitem and inst.components.inventoryitem.owner or nil if owner:HasTag("isr") then if self.updatetask then Update(self.inst, dt or 0) end else LongUpdate(self, inst, dt) end end But, it didn't work for custom perishing, because it didn't get/refresh owner... And I'm doing mod only for Don't Starve as it is now... Link to comment Share on other sites More sharing options...
Ultroman Posted July 18, 2019 Share Posted July 18, 2019 No, you're still overwriting functions. -- I do not know exactly where you are in the code right now, but for you to -- be able to use "self" in this way, the code has to either be inside a -- AddClassPostConstruct or a new version of the class, or a public function -- which is passed a self as a hidden parameter. -- Otherwise, you have to get the component on the instance you are changing. -- I've rewritten the code, so you can stick it in an AddComponentPostInit function. -- Store the old function. local oldLongUpdate = inst.components.inventoryitem.LongUpdate -- Overwrite the original function. Since it's originally declared with :, we need to add self as the first parameter. inst.components.inventoryitem.LongUpdate = function(self, dt) local owner = self.inst.components.inventoryitem and self.inst.components.inventoryitem.owner or nil -- You have to check for owner being nil before using it. if owner ~= nil and owner:HasTag("isr") then if self.updatetask then Update(self.inst, dt or 0) end else -- Call the original function. (how did you figure that it should have "inst" in there?) oldLongUpdate(self, dt) end end Link to comment Share on other sites More sharing options...
zetake Posted July 18, 2019 Author Share Posted July 18, 2019 I don't how this works, but shouldn't it be inst.components.inventoryitem.OnLongUpdate? Link to comment Share on other sites More sharing options...
Ultroman Posted July 18, 2019 Share Posted July 18, 2019 Sorry, we're messing with so many different classes, I got a bit confused. That code should be applied to all perishables, so put this in your modmain.lua -- I have rewritten the code, so you can stick it in an AddComponentPostInit function. AddComponentPostInit("perishable", function(comp) -- Store the old function. local oldLongUpdate = comp.LongUpdate -- Overwrite the original function. Since it's originally declared with :, we need to add self as the first parameter. comp.LongUpdate = function(self, dt) local owner = self.inst.components.inventoryitem and self.inst.components.inventoryitem.owner or nil -- You have to check for owner being nil before using it. if owner ~= nil and owner:HasTag("isr") then if self.updatetask then Update(self.inst, dt or 0) end else -- Call the original function. oldLongUpdate(self, dt) end end end) Link to comment Share on other sites More sharing options...
zetake Posted August 2, 2019 Author Share Posted August 2, 2019 Thanks to you, I finded better way to do this. Spoiler local function OnGetPerish(inst, data) local item = data and data.item if item then local perish = item.components.perishable if perish then if perish.LongUpdateCopy == nil then perish.LongUpdateCopy = perish.LongUpdate end perish.LongUpdate = LongUpdateISP if perish.StartPerishingCopy == nil then perish.StartPerishingCopy = perish.StartPerishing end perish.StartPerishing = StartISP perish:StartPerishing() end end end local function OnLosePerish(inst, data) local item = data and data.item if item then local perish = item.components.perishable if perish then perish.LongUpdate = perish.LongUpdateCopy perish.StartPerishing = perish.StartPerishingCopy perish:StartPerishing() end end end So, it's solved, or do you know better way how to get components function out of nowhere? Link to comment Share on other sites More sharing options...
Ultroman Posted August 2, 2019 Share Posted August 2, 2019 Looks good Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.
Please be aware that the content of this thread may be outdated and no longer applicable.