Blargh Posted November 15, 2018 Share Posted November 15, 2018 Hello, So I've finally figured out how to use AddPrefabPostInit correctly (mostly), but I'm having an issue when I attempt to make changes to the Tornado Stategraph. I'm just trying to make the tornado ignore structures and some plants when destroying stuff. I'm literally copy pasting the relevant function, and adding a few tags to the CANTTAG parameter on line 3. My code is as follows: local function destroystuff(inst) local x, y, z = inst.Transform:GetWorldPosition() local ents = TheSim:FindEntities(x, y, z, 3, nil, { "INLIMBO" ,"structure","witherable"}, TARGET_TAGS) for i, v in ipairs(ents) do --stuff might become invalid as we work or damage during iteration if v ~= inst.WINDSTAFF_CASTER and v:IsValid() then if v.components.health ~= nil and not v.components.health:IsDead() and v.components.combat ~= nil and v.components.combat:CanBeAttacked() and (TheNet:GetPVPEnabled() or not (inst.WINDSTAFF_CASTER_ISPLAYER and v:HasTag("player"))) then local damage = inst.WINDSTAFF_CASTER_ISPLAYER and v:HasTag("player") and TUNING.TORNADO_DAMAGE * TUNING.PVP_DAMAGE_MOD or TUNING.TORNADO_DAMAGE v.components.combat:GetAttacked(inst, damage, nil, "wind") if v:IsValid() and inst.WINDSTAFF_CASTER ~= nil and inst.WINDSTAFF_CASTER:IsValid() and v.components.combat ~= nil and not (v.components.health ~= nil and v.components.health:IsDead()) and not (v.components.follower ~= nil and v.components.follower.keepleaderonattacked and v.components.follower:GetLeader() == inst.WINDSTAFF_CASTER) then v.components.combat:SuggestTarget(inst.WINDSTAFF_CASTER) end elseif v.components.workable ~= nil and v.components.workable:CanBeWorked() and v.components.workable:GetWorkAction() and WORK_ACTIONS[v.components.workable:GetWorkAction().id] then SpawnPrefab("collapse_small").Transform:SetPosition(v.Transform:GetWorldPosition()) v.components.workable:WorkedBy(inst, 2) --v.components.workable:Destroy(inst) end end end print("Tornado SG updated") end AddStateGraphPostInit("SGtornado", function(inst) inst.destroystuff = destroystuff end) And my error log is: [00:00:55]: [string "../mods/BlarghTestClean/modmain.lua"]:246: attempt to call global 'AddStateGraphPostInit' (a nil value) LUA ERROR stack traceback: ../mods/BlarghTestClean/modmain.lua(246,1) in main chunk =[C] in function 'xpcall' scripts/util.lua(711,1) in function 'RunInEnvironment' scripts/mods.lua(513,1) in function 'InitializeModMain' scripts/mods.lua(487,1) in function 'LoadMods' scripts/main.lua(302,1) in function 'ModSafeStartup' scripts/main.lua(375,1) =[C] in function 'SetPersistentString' scripts/mainfunctions.lua(26,1) in function 'SavePersistentString' scripts/modindex.lua(80,1) =[C] in function 'GetPersistentString' scripts/modindex.lua(67,1) in function 'BeginStartupSequence' scripts/main.lua(374,1) in function 'callback' scripts/modindex.lua(545,1) =[C] in function 'GetPersistentString' scripts/modindex.lua(519,1) in function 'Load' scripts/main.lua(373,1) in main chunk [00:00:55]: [string "scripts/mainfunctions.lua"]:1079: variable 'global_error_widget' is not declared LUA ERROR stack traceback: =[C] in function 'error' scripts/strict.lua(23,1) scripts/mainfunctions.lua(1079,1) =[C] in function 'GetPersistentString' scripts/quagmire_recipebook.lua(54,1) in function 'Load' scripts/main.lua(320,1) in function 'ModSafeStartup' scripts/main.lua(375,1) =[C] in function 'SetPersistentString' scripts/mainfunctions.lua(26,1) in function 'SavePersistentString' scripts/modindex.lua(80,1) =[C] in function 'GetPersistentString' scripts/modindex.lua(67,1) in function 'BeginStartupSequence' scripts/main.lua(374,1) in function 'callback' scripts/modindex.lua(545,1) =[C] in function 'GetPersistentString' scripts/modindex.lua(519,1) in function 'Load' scripts/main.lua(373,1) in main chunk I'm guessing this is because the Tornado doesn't exist when the AddStategraphPostInit is being run? Or maybe my method of making the modification is just wrong. Any advice would be appreciated. Thanks! Link to comment Share on other sites More sharing options...
Ultroman Posted November 15, 2018 Share Posted November 15, 2018 (edited) It's giving you a hint. You don't have direct access to global functions like AddStateGraphPostInit in a modmain.lua file. You need to do: GLOBAL.AddStategraphPostInit(whatever) Edited November 16, 2018 by Ultroman Link to comment Share on other sites More sharing options...
Blargh Posted November 16, 2018 Author Share Posted November 16, 2018 Heh, the hint it gave me turned out to be a bit misleading! It didn't feel like a scope issue, it was actually a case issue. The G in AddStategraphPostInit shouldn't have been capitalized, and it worked without the GLOBAL. Well, it didn't actually work, it just didn't give me an error. So the mod runs, but it doesn't seem to be doing anything. I'm guessing i'm not calling the function correctly, because the "Tornado SG updated" line isn't getting printed to console. Looks like i haven't quite figured out the Add__GraphPostInit functions yet! I actually wound up trying it another way as well, but that ended up with the same results. No errors, but not working as intended. Anyone mind taking a second glance? Thanks! Link to comment Share on other sites More sharing options...
Ultroman Posted November 16, 2018 Share Posted November 16, 2018 (edited) Well, you're just setting the variable destroystuff on the inst, to be the function. That won't do anything. Instead, do this: AddStategraphPostInit("SGtornado", destroystuff) You're adding your function to the list/table of (postinit) functions for it to run on the given stategraph, after constructing the stategraph. In this case the "SGTornado" stategraph. That's all EDIT: Ehm...wait...I think I'm missing something. What you did SHOULD work. Let me just look at the game code for a minute. Got it. Give me a minute to do a write-up. Where are you looking for the print() to show up? Edited November 16, 2018 by Ultroman Link to comment Share on other sites More sharing options...
Blargh Posted November 16, 2018 Author Share Posted November 16, 2018 I checked the console, and client_log for the Print(). Part of me is expecting it to pop up after casting the spell, but I'm not quite seeing anything. Link to comment Share on other sites More sharing options...
Ultroman Posted November 16, 2018 Share Posted November 16, 2018 (edited) The print() will only show up in the server_log (or is it the client_log?), and not the in-game console. What spell are you talking about? Try this very intrusive code, to see if what you expect happens. local function destroystuffyay(inst) local x, y, z = inst.Transform:GetWorldPosition() local ents = TheSim:FindEntities(x, y, z, 3, nil, { "INLIMBO" ,"structure","witherable"}, TARGET_TAGS) for i, v in ipairs(ents) do --stuff might become invalid as we work or damage during iteration if v ~= inst.WINDSTAFF_CASTER and v:IsValid() then if v.components.health ~= nil and not v.components.health:IsDead() and v.components.combat ~= nil and v.components.combat:CanBeAttacked() and (TheNet:GetPVPEnabled() or not (inst.WINDSTAFF_CASTER_ISPLAYER and v:HasTag("player"))) then local damage = inst.WINDSTAFF_CASTER_ISPLAYER and v:HasTag("player") and TUNING.TORNADO_DAMAGE * TUNING.PVP_DAMAGE_MOD or TUNING.TORNADO_DAMAGE v.components.combat:GetAttacked(inst, damage, nil, "wind") if v:IsValid() and inst.WINDSTAFF_CASTER ~= nil and inst.WINDSTAFF_CASTER:IsValid() and v.components.combat ~= nil and not (v.components.health ~= nil and v.components.health:IsDead()) and not (v.components.follower ~= nil and v.components.follower.keepleaderonattacked and v.components.follower:GetLeader() == inst.WINDSTAFF_CASTER) then v.components.combat:SuggestTarget(inst.WINDSTAFF_CASTER) end elseif v.components.workable ~= nil and v.components.workable:CanBeWorked() and v.components.workable:GetWorkAction() and WORK_ACTIONS[v.components.workable:GetWorkAction().id] then SpawnPrefab("collapse_small").Transform:SetPosition(v.Transform:GetWorldPosition()) v.components.workable:WorkedBy(inst, 2) --v.components.workable:Destroy(inst) end end end print("Tornado SG updated") end local function alterSGTornado(sg) sg.states["idle"].onenter = function(inst) inst.Physics:Stop() inst.AnimState:PushAnimation("tornado_loop", false) destroystuffyay(inst) end sg.states["walk"].onenter = function(inst) inst.components.locomotor:WalkForward() inst.AnimState:PushAnimation("tornado_loop", false) destroystuffyay(inst) end sg.states["walk"].timeline = { TimeEvent(5*FRAMES, destroystuffyay), } sg.states["run_start"].timeline = { TimeEvent(5*FRAMES, destroystuffyay), } sg.states["run"].timeline = { TimeEvent(5*FRAMES, destroystuffyay), } sg.states["run_stop"].timeline = { TimeEvent(5*FRAMES, destroystuffyay), } end AddStategraphPostInit("SGtornado", alterSGTornado) As I said, this is very intrusive, in that it directly overrides those state functions and arrays. It has to, in order to properly change them. It REALLY would be best, if what you had originally (after the capital G) just worked. It should...I think. Unless it "saves" the destroystuff function to the inside of the state functions, and doesn't just read/use it from the local function (which you correctly changed). Edited November 16, 2018 by Ultroman Link to comment Share on other sites More sharing options...
Blargh Posted November 16, 2018 Author Share Posted November 16, 2018 Hmm, that didn't seem to work, neither the Print() is appearing, nor is the tornado ignoring the things it should be ignoring. But I believe I follow what you're saying. I'll try modifying the individual Stategraphs for Idle and Walk (and maybe even "run_start", "run", and "run_stop". It looks like the function gets called there too), with AddStategraphState. I'm really just glad that my initial approach was correct (in theory and minus a few typos ). Thanks again. Link to comment Share on other sites More sharing options...
Ultroman Posted November 16, 2018 Share Posted November 16, 2018 If you follow the modding API, this is supposed to work. This snippet is from the modding API documentation. local newIdleTimeout = function(inst) if math.random() < 0.5 then inst.sg:GoToState("funnyidle") else inst.sg:GoToState("spin_around") end end local function SGWilsonPostInit(sg) -- note! This overwrites the old timeout behavior! If possible you should -- always try appending your behaviour instead. sg.states["idle"].ontimeout = newIdleTimeout end AddStategraphPostInit("wilson", SGWilsonPostInit) What are you using to test the tornado? Because just spawning a tornado is a different prefab (fans.lua) than the one spawned by the Windstaff (staff_tornado.lua). The one from the Windstaff uses the stategraph, and the one you spawn when doing c_spawn("tornado") does not. Well, apparently that's not entirely right, either. Man, they've really messed up the naming of these things... Link to comment Share on other sites More sharing options...
Ultroman Posted November 16, 2018 Share Posted November 16, 2018 Like you, I can't get it to do anything Link to comment Share on other sites More sharing options...
Ultroman Posted November 16, 2018 Share Posted November 16, 2018 (edited) This works. We're not supposed to alter "SGtornado" stategraph, but "tornado" stategraph. I also needed to add GLOBAL to a bunch of things, and copy the WORK_ACTIONS and TARGET_TAGS stuff, but it works. local WORK_ACTIONS = { CHOP = true, DIG = true, HAMMER = true, MINE = true, } local TARGET_TAGS = { "_combat" } for k, v in pairs(WORK_ACTIONS) do table.insert(TARGET_TAGS, k.."_workable") end local function destroystuff(inst) local x, y, z = inst.Transform:GetWorldPosition() local ents = TheSim:FindEntities(x, y, z, 3, nil, { "INLIMBO" ,"structure","witherable"}, TARGET_TAGS) for i, v in ipairs(ents) do --stuff might become invalid as we work or damage during iteration if v ~= inst.WINDSTAFF_CASTER and v:IsValid() then if v.components.health ~= nil and not v.components.health:IsDead() and v.components.combat ~= nil and v.components.combat:CanBeAttacked() and (GLOBAL.TheNet:GetPVPEnabled() or not (inst.WINDSTAFF_CASTER_ISPLAYER and v:HasTag("player"))) then local damage = inst.WINDSTAFF_CASTER_ISPLAYER and v:HasTag("player") and GLOBAL.TUNING.TORNADO_DAMAGE * GLOBAL.TUNING.PVP_DAMAGE_MOD or GLOBAL.TUNING.TORNADO_DAMAGE v.components.combat:GetAttacked(inst, damage, nil, "wind") if v:IsValid() and inst.WINDSTAFF_CASTER ~= nil and inst.WINDSTAFF_CASTER:IsValid() and v.components.combat ~= nil and not (v.components.health ~= nil and v.components.health:IsDead()) and not (v.components.follower ~= nil and v.components.follower.keepleaderonattacked and v.components.follower:GetLeader() == inst.WINDSTAFF_CASTER) then v.components.combat:SuggestTarget(inst.WINDSTAFF_CASTER) end elseif v.components.workable ~= nil and v.components.workable:CanBeWorked() and v.components.workable:GetWorkAction() and WORK_ACTIONS[v.components.workable:GetWorkAction().id] then GLOBAL.SpawnPrefab("collapse_small").Transform:SetPosition(v.Transform:GetWorldPosition()) v.components.workable:WorkedBy(inst, 2) --v.components.workable:Destroy(inst) end end end print("Tornado SG updated") end local function alterSGTornado(sg) sg.states["idle"].onenter = function(inst) inst.Physics:Stop() inst.AnimState:PushAnimation("tornado_loop", false) destroystuff(inst) end sg.states["walk"].onenter = function(inst) inst.components.locomotor:WalkForward() inst.AnimState:PushAnimation("tornado_loop", false) destroystuff(inst) end sg.states["walk"].timeline = { GLOBAL.TimeEvent(5*GLOBAL.FRAMES, destroystuff), } sg.states["run_start"].timeline = { GLOBAL.TimeEvent(5*GLOBAL.FRAMES, destroystuff), } sg.states["run"].timeline = { GLOBAL.TimeEvent(5*GLOBAL.FRAMES, destroystuff), } sg.states["run_stop"].timeline = { GLOBAL.TimeEvent(5*GLOBAL.FRAMES, destroystuff), } end AddStategraphPostInit("tornado", alterSGTornado) EDIT: Just edited to add GLOBAL to the TUNING variables. Edited November 16, 2018 by Ultroman Link to comment Share on other sites More sharing options...
Blargh Posted November 17, 2018 Author Share Posted November 17, 2018 Yep, That did the trick. Thanks! 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