Kemezryp Posted February 8, 2019 Share Posted February 8, 2019 So I'm creating a new character. I want her to have buffed sleeping. Changes I want to make are:bedroll_straw (+0,67 Sanity, -1 Hunger) >>> (+1 Sanity, -0,67 Hunger)bedroll_furry (+1 Sanity, +1 Health, -1 Hunger) >>> (+1,33 Sanity, +1,33 Health, -0,67 Hunger)tent (+1 Sanity, +2 Health. -1 Hunger) >>> (+1,33 Sanity, +2,33 Health, -0,67 Hunger)siestahut (+1 Sanity, +2 Health, -0.33 Hunger) >>> (+1,33 Sanity, +2,33 Health) (don't worry about the balance, she gets hungry more quickly than any other mortal) also if possible I want her to be able to sleep regardless of the time of day. Thanks for reading! Link to comment Share on other sites More sharing options...
Ultroman Posted February 8, 2019 Share Posted February 8, 2019 (edited) I just answered something like this in another thread. Just look at the sleep-part, as there is a lot going on in that thread. All you need to do in order to use that code, is say something like: if comp.inst.prefab == "bedroll_straw" then -- do the DoDelta call with the diference between the original value for bedroll_straw and the value you want it to give elseif comp.inst.prefab == "bedroll_furry" then ... I also explain in that thread why I think this is the best and least intrusive way to inject this kind of behavior. Edited February 9, 2019 by Ultroman Link to comment Share on other sites More sharing options...
Kemezryp Posted February 9, 2019 Author Share Posted February 9, 2019 (edited) Thanks for your reply! Alrgiht, so I wrote the code but it doesn't seem to take any effects. (it's modmain.lua) Spoiler AddComponentPostInit("sleepingbag", function(comp) local old_onsleep = comp.onsleep comp.onsleep = function(inst, sleeper) old_onsleep(inst, sleeper) -- call the original function if sleeper.prefab == "oshi" then if comp.inst.prefab == "bedroll_straw" then sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif comp.inst.prefab == "bedroll_furry" then sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif comp.inst.prefab == "tent" then sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif comp.inst.prefab == "bsiestahut" then sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) end end end end ) I tried writing character name as "oshi", "Oshi" and "OSHI" but neither of them works. EDIT: I tested 1 == 1 condition and it seems the problem lays somewhere else. Edited February 9, 2019 by Kemezryp Link to comment Share on other sites More sharing options...
Ultroman Posted February 9, 2019 Share Posted February 9, 2019 (edited) EDIT: THIS APPROACH DOESN'T WORK! Look at CarlZalph's reply further down for an explanation and some working code! I think I made a mistake...though it SHOULD amount to the same, try this instead (replaced "comp.inst" with "inst"): AddComponentPostInit("sleepingbag", function(comp) local old_onsleep = comp.onsleep comp.onsleep = function(inst, sleeper) old_onsleep(inst, sleeper) -- call the original function if sleeper.prefab == "oshi" then if inst.prefab == "bedroll_straw" then sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif inst.prefab == "bedroll_furry" then sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif inst.prefab == "tent" then sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif inst.prefab == "bsiestahut" then sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) end end end end ) If that doesn't work, it's time to break out the print-statements. If you're not sure how to use those to your advantage, look at the "Debugging" part of this post. Before even calling old_onsleep(inst, sleeper), print the value of sleeper.prefab and comp.inst.prefab and inst.prefab, to make sure you got everything right. Edited February 10, 2019 by Ultroman Link to comment Share on other sites More sharing options...
Kemezryp Posted February 9, 2019 Author Share Posted February 9, 2019 alright, I'm not sure if I did it correctly, but here's code: Spoiler AddComponentPostInit("sleepingbag", function(comp) print("step 1") local old_onsleep = comp.onsleep print("step 2") comp.onsleep = function(inst, sleeper) print("step 3") old_onsleep(inst, sleeper) -- call the original function print("step 4") if 1 == 1 then print("yes 1 is equal to 1") if inst.prefab == "bedroll_straw" then print("Bedroll Straw") sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif inst.prefab == "bedroll_furry" then print("Bedroll Furry") sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif inst.prefab == "tent" then print("Tent") sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif inst.prefab == "bsiestahut" then print("Siesta Hut") sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) end end end print("finished!") end ) I went to sleep to the Tent. Here are results from client_log.txt: (I think it should uptade itself more frequently? idk) client_log.txt Link to comment Share on other sites More sharing options...
Ultroman Posted February 9, 2019 Share Posted February 9, 2019 (edited) Not just that. Your "step 1" and "step 2" should happen every time a piece of sleeping-equipment is created on the server (even those existing in the world or on your character every time you load the game). Something is wrong. Where did you put this AddComponentPostInit call? It's supposed to be at the bottom of your modmain.lua Edited February 9, 2019 by Ultroman Link to comment Share on other sites More sharing options...
Kemezryp Posted February 9, 2019 Author Share Posted February 9, 2019 Yes, it is on the very bottom of the file. I retested the process. Yes, steps are repeated when I place the tent. Nothing happens when I go to sleep. I gave myself right to craft anything: [00:02:44]: Warning: GetPlayer() is deprecated. Please use ThePlayer instead. (@GetPlayer().components.builder:GiveAllRecipes():1 in ?) I created first tent: [00:03:05]: step 1 [00:03:05]: step 2 [00:03:05]: finished! I created another 2 tents: [00:05:23]: step 1 [00:05:23]: step 2 [00:05:23]: finished! [00:05:28]: step 1 [00:05:28]: step 2 [00:05:28]: finished! Link to comment Share on other sites More sharing options...
Ultroman Posted February 9, 2019 Share Posted February 9, 2019 As in nothing happens at all, or the stats change regularly? Link to comment Share on other sites More sharing options...
Kemezryp Posted February 9, 2019 Author Share Posted February 9, 2019 They change normally. Link to comment Share on other sites More sharing options...
Ultroman Posted February 9, 2019 Share Posted February 9, 2019 That is super-weird...now we know that AddComponentPostInit is being done, at least. We gotta figure out what's going on. After print("step 2") try adding something like this: print("Printing comp: ") print(comp) print("Printing comp.onsleep: ") print(comp.onsleep) It should either print nil, crash saying you've tried to concatenate nil, or write the current state of the function. Let's see what it says. Link to comment Share on other sites More sharing options...
Kemezryp Posted February 10, 2019 Author Share Posted February 10, 2019 [00:02:03]: step 1 [00:02:03]: step 2 [00:02:03]: Printing comp: [00:02:03]: table: 6877CDF8 [00:02:03]: Printing comp.onsleep: [00:02:03]: nil [00:02:03]: finished! There Link to comment Share on other sites More sharing options...
Ultroman Posted February 10, 2019 Share Posted February 10, 2019 That doesn't look right. Can you send me a zip of the mod? Link to comment Share on other sites More sharing options...
Ultroman Posted February 10, 2019 Share Posted February 10, 2019 (edited) EDIT: THIS APPROACH DOESN'T WORK! Look at CarlZalph's reply further down for an explanation and some working code! AHA! I know what's going on! It's because we're doing AddComponentPostInit. That function queues a function to be run after a component has been initialized, but since a component is always initialized first and has its functions changed later, what happens is this: a prefab has the sleepingbag-component added to it our function is called, and makes its changes the prefab makes changes its to the sleepingbag-component (overwriting our onsleep function) There are two ways we can fix this: We move this code to four AddPrefabPostInit calls (one for each sleeping equipment) and refactor it slightly. We simply delay the changing of our code a tiny bit into the future using DoTaskInTime, because DoTaskInTime waits at least until next game-tick, even if you tell it to wait for 0 time. Number 1 would be the obvious thing to do (and is also how I do it in my Sleepy Time mod, but if we do number 2, then we actually accommodate even the sleeping-equipment added by mods (or by Klei at a later date), since we go directly for the sleepingbag-component instead of individual prefabs. Let's try number 2, since it's also the quickest one to change our current code for: AddComponentPostInit("sleepingbag", function(comp) print("step 1") comp.inst:DoTaskInTime(0, function(comp) print("step 2") local old_onsleep = comp.onsleep comp.onsleep = function(inst, sleeper) print("step 3") old_onsleep(inst, sleeper) -- call the original function print("step 4") if sleeper.prefab == "oshi" then print("Sleeper is Oshi") if inst.prefab == "bedroll_straw" then print("Bedroll Straw") sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif inst.prefab == "bedroll_furry" then print("Bedroll Furry") sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif inst.prefab == "tent" then print("Tent") sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) elseif inst.prefab == "bsiestahut" then print("Siesta Hut") sleeper.components.health:DoDelta(2, true) sleeper.components.sanity:DoDelta(2, true) sleeper.components.hunger:DoDelta(2, true) end end end print("finished!") end ) end ) Edited February 10, 2019 by Ultroman Link to comment Share on other sites More sharing options...
CarlZalph Posted February 10, 2019 Share Posted February 10, 2019 (edited) The thing is that the onsleep/onwake functions are called once, and in the prefabs they're creating a timer from these callbacks. So it's a oneshot. The callbacks here shouldn't be created for these when the component postinit is done, since they're defined in the prefabs after the component is created. Then there's the thing that the callback for the timer for each prefab in the base game follow a nonstandard way on using variables for doing the deltas- some store the tuning variable for (hunger/health/sanity) while the others don't. To top it all off the timer variable when it's killed isn't being set to nil like it should, so can't even use a metatable hook for that. These all scream of 'old code from DS' which should have been rewritten if time permitted; even if they're functional they're not modder friendly. Luckily all of the default prefabs for the base game use the same variable name for the sleep timer called 'sleeptask', so it's not as bad as it could have been for a blanket hook. Last note you had a typo for the siestahut with a 'b' in front of it. Here's what I did to get this all functional and generic so you can apply it to anything really: local HookSleepingData = { wolfgang = { bedroll_straw = { sanity = 1.0, health = 1.0, -- This doesn't do anything due to prefab stopping it, this is here in case Klei changes that. hunger = -0.67, }, bedroll_furry = { sanity = 1.33, health = 1.33, hunger = -0.67, }, tent = { sanity = 1.33, health = 2.33, hunger = -0.67, }, siestahut = { sanity = 1.33, health = 2.33, hunger = -0.33, }, }, waxwell = { tent = { sanity = 400.0, health = 900.0, hunger = -50.0, }, }, } local HookThese = { "bedroll_straw", "bedroll_furry", "tent", "siestahut", } local HookDoDelta = function(hooks, sleeper, component, value) if value == nil then return end local tohook = sleeper.components[component] if tohook and tohook.DoDelta then local DoDelta_old = tohook.DoDelta tohook.DoDelta = function(self, amount, ...) -- You could do math here with the old value 'amount' and the new replacement 'value' if you wanted it to be additive or multiplicative. -- Right now it's just flat out replacement. return DoDelta_old(self, value, ...) end table.insert( hooks, { component = component, DoDelta_old = DoDelta_old, } ) end end local UnHookDoDelta = function(hooks, sleeper) for _, hook in ipairs(hooks) do sleeper.components[hook.component].DoDelta = hook.DoDelta_old end end local HookSleepingbags = function(prefab, sleeptask) local fn_old = sleeptask.fn sleeptask.fn = function(inst, sleeper, ...) local data = HookSleepingData[sleeper.prefab or "None"] if data and data[prefab] then local hooks = {} HookDoDelta(hooks, sleeper, "sanity", data[prefab].sanity) HookDoDelta(hooks, sleeper, "health", data[prefab].health) HookDoDelta(hooks, sleeper, "hunger", data[prefab].hunger) -- Temperature/Woodiness/custom component/etc could be done here. fn_old(inst, sleeper, ...) UnHookDoDelta(hooks, sleeper) else fn_old(inst, sleeper, ...) end end end for _, prefab in ipairs(HookThese) do AddPrefabPostInit( prefab, function(inst) if not GLOBAL.TheWorld.ismastersim then return end if inst.components.sleepingbag and inst.components.sleepingbag.onsleep then local onsleep_old = inst.components.sleepingbag.onsleep inst.components.sleepingbag.onsleep = function(...) onsleep_old(...) if inst.sleeptask -- Hopefully other 'sleepingbag' mod stuff uses this exact same variable name Klei uses, otherwise fix it here. then HookSleepingbags(prefab, inst.sleeptask) end end end end ) end Edited February 10, 2019 by CarlZalph Added case for if top defined isn't for default behaviour on hooks. Link to comment Share on other sites More sharing options...
Ultroman Posted February 10, 2019 Share Posted February 10, 2019 1 hour ago, CarlZalph said: .... Wow! That's crazy! And you're totally right, of course The reason why my approach works in my own mod, is because it replaces the onsleep functions entirely, and creates its own sleeptask. This is an awful lot of code just to do this simple thing, but from what you're saying it's necessary. Thanks for blowing my mind! Link to comment Share on other sites More sharing options...
Kemezryp Posted February 10, 2019 Author Share Posted February 10, 2019 Wow, this is some crazy piece of code! Everything works. I really appreciate your work. I would never manage to do something like that alone. Thank you both for helping me, have a great day! 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