meltorefas Posted July 15, 2018 Share Posted July 15, 2018 (edited) I am trying to modify a single line in the timeline of a state on a character, postinit. I didn't see any way in modutils to do that, so I tried just using AddStateGraphPostInit to replace the entire state with a version that is identical except that one line. However, I have run into the following issue. The State contains a reference to CommonHandlers. When trying to load with the mod enabled, it crashes with a log error "attempt to index global 'CommonHandlers' (a nil value)". Pretty basic error when running a postinit from modmain, so I changed it to GLOBAL.CommonHandlers. Except now it crashes with the log error "variable 'CommonHandlers' is not declared". I have no idea what to do from this point, and haven't found anything via searching. If anyone knows how to fix this, or a way to just replace the one line/part I need without replacing the entire state, I would really appreciate it. Thanks! EDIT: I also tried using inst:SetStateGraph in a prefab postinit to make the player use my own custom version of the stategraph that has that line changed. However, it simply didn't work; the game did not use the custom stategraph (everything else in the prefab postinit works fine though). I've used SetStateGraph for mobs, but figured it just doesn't work for players. If there's a way to do this, that would also be an acceptable solution for me. Edited July 15, 2018 by meltorefas Link to comment Share on other sites More sharing options...
K1NGT1GER609 Posted July 15, 2018 Share Posted July 15, 2018 Not much to work with so I'll take a stab in the dark,do you have all these lines: local require = GLOBAL.require local State = GLOBAL.State local Action = GLOBAL.Action local ActionHandler = GLOBAL.ActionHandler local TimeEvent = GLOBAL.TimeEvent local EventHandler = GLOBAL.EventHandler local ACTIONS = GLOBAL.ACTIONS local FRAMES = GLOBAL.FRAMES before the stategraph code? Link to comment Share on other sites More sharing options...
meltorefas Posted July 21, 2018 Author Share Posted July 21, 2018 (edited) Okay, sorry for the long delay in replying. I was super tired when I posted this and... forgot I had done it. >.> Was taking a break from DST to play something else, but when I came back and tried to tackle this problem again I finally remembered. Also, sorry for not posting literally any code. I should probably have waited until after I slept, lol. Thanks for taking a crack at it anyways! So, your solution unfortunately didn't work, but it DID let me remove all the GLOBAL. from before the various global functions, which was nice (and blindingly obvious in retrospect, I feel silly). But yeah, still crashes with one of the two CommonHandlers related errors from above (depending on whether I include a GLOBAL. or not). Here is the actual (relevant) *code* this time, though: Spoiler PrefabFiles = { } Assets = { } local require = GLOBAL.require local State = GLOBAL.State local Action = GLOBAL.Action local ActionHandler = GLOBAL.ActionHandler local TimeEvent = GLOBAL.TimeEvent local EventHandler = GLOBAL.EventHandler local ACTIONS = GLOBAL.ACTIONS local FRAMES = GLOBAL.FRAMES local DEGREES = GLOBAL.DEGREES local Vector3 = GLOBAL.Vector3 local CommonHandlers = GLOBAL.CommonHandlers --trying it up here local new_BeeQeen_Spawn = State{ name = "special_atk2_nr", tags = { "spawnguards", "busy", "nosleep", "nofreeze" }, onenter = function(inst) local oldnum = inst.components.commander:GetNumSoldiers() local soldiers = inst.components.commander:GetAllSoldiers() local numbees = #soldiers if numbees >= 8 or inst.specialatk2 == false then inst.sg:GoToState("idle") else FaceTarget(inst) inst.components.locomotor:StopMoving() inst.AnimState:PlayAnimation("spawn") inst.SoundEmitter:PlaySound("dontstarve/creatures/together/bee_queen/spawn") end end, timeline = { TimeEvent(16 * FRAMES, function(inst) inst.sg.mem.wantstospawnguards = nil inst.components.timer:StartTimer("spawnguards_cd", inst.spawnguards_cd) local oldnum = inst.components.commander:GetNumSoldiers() local x, y, z = inst.Transform:GetWorldPosition() local rot = inst.Transform:GetRotation() local num = math.random(TUNING.BEEQUEEN_MIN_GUARDS_PER_SPAWN, TUNING.BEEQUEEN_MAX_GUARDS_PER_SPAWN) if num + oldnum > TUNING.BEEQUEEN_TOTAL_GUARDS then num = math.max(TUNING.BEEQUEEN_MIN_GUARDS_PER_SPAWN, TUNING.BEEQUEEN_TOTAL_GUARDS - oldnum) end local drot = 360 / num for i = 1, num do local minion = inst.components.petleash:SpawnPetAt(x, y, z, "beeguard") local angle = rot + i * drot local radius = minion.Physics:GetRadius() minion.Transform:SetRotation(angle) if inst.isshiny ~= 0 then minion.isshiny = inst.isshiny minion.AnimState:SetBuild("bee_guard_shiny_build_0"..minion.isshiny) end angle = -angle * DEGREES minion.Transform:SetPosition(x + radius * math.cos(angle), 0, z + radius * math.sin(angle)) minion:OnSpawnedGuard(inst) end inst.specialatk2 = false if oldnum > 0 then local soldiers = inst.components.commander:GetAllSoldiers() num = #soldiers drot = 360 / num for i = 1, num do local angle = -(rot + i * drot) * DEGREES local xoffs = TUNING.BEEGUARD_GUARD_RANGE * math.cos(angle) local zoffs = TUNING.BEEGUARD_GUARD_RANGE * math.sin(angle) local mindistsq = math.huge local closest = 1 for i2, v in ipairs(soldiers) do local offset = v.components.knownlocations:GetLocation("queenoffset") if offset ~= nil then local distsq = distsq(xoffs, zoffs, offset.x, offset.z) if distsq < mindistsq then mindistsq = distsq closest = i2 end end end table.remove(soldiers, closest).components.knownlocations:RememberLocation("queenoffset", Vector3(xoffs, 0, zoffs), false) end end end), CommonHandlers.OnNoSleepTimeEvent(32 * FRAMES, function(inst) inst.sg:RemoveStateTag("busy") inst.sg:RemoveStateTag("nosleep") inst.sg:RemoveStateTag("nofreeze") end), }, onexit = function(inst) inst:DoTaskInTime(10, function(inst) inst.specialatk2 = true end) end, events = { EventHandler("animqueueover", function(inst) inst.sg:GoToState("idle") end ), }, } AddStategraphPostInit("SGbeequeenp", new_BeeQeen_Spawn) This is a copy of the one of the states from the stategraph of the playable Bee Queen from Leonardo Coxington's Playable Pets: New Reign. I changed one line in the spawn function (and it works, I tested it in the actual mod stategraph). I don't know if this is the proper method for replacing a state post init, but this is what I found from searching. I don't mind figuring it out as I go, I am just stuck on this CommonHandlers issue. Thanks again for any help. Edited July 21, 2018 by meltorefas Link to comment Share on other sites More sharing options...
K1NGT1GER609 Posted July 21, 2018 Share Posted July 21, 2018 Hmm have you tried deleteing: CommonHandlers.OnNoSleepTimeEvent(32 * FRAMES, function(inst) end), Its my only guess as I can't find it in leonardo's mod (maybe its outdated?) and I'm not finding similar lines of code that have commonhandlers in the modmain. Link to comment Share on other sites More sharing options...
meltorefas Posted July 22, 2018 Author Share Posted July 22, 2018 CommonHandlers is in the stategraphs, not the modmains. Which I am guessing is the problem I am running into. The whole thing I am trying to do is replace a function within an existing stategraph, post init. So I am using an AddStategraphPostInit function from my mod's modmain to try and do that. I can't exactly delete the reference to CommonHandlers since it is needed for that state to work properly. I found that CommonHandlers is declared inside stategraphs/commonstates.lua, and is used by a lot of creatures. I just can't figure out how to access it. I tried adding require("stategraphs/commonstates") to my modmain, which is what the NPC stategraphs have, but it didn't work. I tried creating using a local variable, SGCommon = require("stategraphs/commonstates"), and changing it to SGCommon.CommonHandlers, but that gives me an error "attempt to index local 'SGCommon' (a boolean value)". In the meantime I figured out why inst:SetStategraph wasn't working... the playable pets characters have functions that reset their stategraphs (among other things) to their own locally stored values, thus overwriting any change I make. Contacting the mod author to see if he'll expose those local variables, heh. I would still love to know how to properly mod a stategraph post init to replace a state, though. Still can't find much on it. Link to comment Share on other sites More sharing options...
ksaab Posted July 22, 2018 Share Posted July 22, 2018 require "stategraphs/commonstates" local CommonHandlers = GLOBAL.CommonHandlers It will work only it this order. You need to use require because GLOBAL.CommonHadlers doesn't exist when the modmain is called (I think it will be defined by the game when the first stategraph will be initiated). Anyway, it is not your main problem. You are trying to add a custom state, not to reinit existing: AddStategraphPostInit("SGbeequeenp", new_BeeQeen_Spawn) Must be used in another way. For example: local function SGWilsonPostInit(sg) local _onenter = sg.states["run_start"].onenter sg.states["run_start"].onenter = function(inst) _onenter(inst) if not (inst.sg.statemem.riding or inst.sg.statemem.heavy) and SlowEnoughSandWalk(inst) then inst.sg.statemem.sandstorm = true inst.AnimState:PushAnimation("sand_walk_pre") end end local _onenter2 = sg.states["run"].onenter sg.states["run"].onenter = function(inst) if not (inst.components.rider:IsRiding() or inst.components.inventory:IsHeavyLifting()) and SlowEnoughSandWalk(inst) then inst.components.locomotor:RunForward() if not inst.AnimState:IsCurrentAnimation("sand_walk") then inst.AnimState:PlayAnimation("sand_walk", true) end inst.sg:SetTimeout(inst.AnimState:GetCurrentAnimationLength()) else _onenter2(inst) end end end AddStategraphPostInit("wilson", SGWilsonPostInit) If you want to add a custom state, use AddStategraphState("stategraph_name", state_var) Link to comment Share on other sites More sharing options...
meltorefas Posted July 25, 2018 Author Share Posted July 25, 2018 On 7/22/2018 at 3:58 AM, ksaab said: require "stategraphs/commonstates" local CommonHandlers = GLOBAL.CommonHandlers It will work only it this order. You need to use require because GLOBAL.CommonHadlers doesn't exist when the modmain is called (I think it will be defined by the game when the first stategraph will be initiated). This is good to know, thanks! On 7/22/2018 at 3:58 AM, ksaab said: Anyway, it is not your main problem. You are trying to add a custom state, not to reinit existing: I don't think I understand what you mean, because I am actually trying to replace/overwrite an existing state, not add a new one; the specific state is called from elsewhere in the mod, and I wanted to overwrite one of the lines that would thus be executed. If there is some other/better way to do that, that's fine, but I am not sure I understand what you're saying otherwise, because adding a new state wouldn't help me change what happens when the existing state is called, would it? Anyways, the mod author is letting me contribute to the code and also move the functions I want to change out of the stategraph and into the prefab, which will make things much easier (though redundant). I would still love to know how to actually replace a state, though. Link to comment Share on other sites More sharing options...
ksaab Posted July 25, 2018 Share Posted July 25, 2018 (edited) AddStategraphPostInit("stategraph_name", some_function) When the stategraph will be initiated, the some_function will be called with stategraph itself as the first argument. It gives you an access to all state's variables: local function some_function(sg) --sg is the statgraph "stategraph_name" local _onenter = sg.states["some_state"].onenter --saving old onenter function as a local var sg.states["some_state"].onenter = function(inst) --replacing it with a new one _onenter(inst) --call an original onenter if needed inst.SoundEmitter:PlaySound("some_sound") --new action for the state end end This code plays a sound when a creature comes to this state in addition to the original code. Or you can completely replace state with this: local function some_function(sg) --sg is the statgraph "stategraph_name" sg.states["some_state"] = State{ your state params } --or sg.states["some_state"] = state_var --if you've created a state before end Now look at your code: AddStategraphPostInit("SGbeequeenp", new_BeeQeen_Spawn) new_BeeQeen_Spawn is a state-type object (table generally). The game will try to call it as a function and... it will crash, because it's a table. AddStategraphState("stategraph_name", state_var) This adds a new state to the stategraph and the state_var is a state-type object. May be if you add a state with an existing name (state name, not a variable name), it will overwrite an original state. I'm not sure, but it should. Edited July 25, 2018 by ksaab 1 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