RedHairedHero Posted July 24, 2017 Share Posted July 24, 2017 (edited) Hello, I'm currently running into the following error when attempting to switch my characters build, bank, and stategraph then attempt to move. [00:01:28]: [string "../mods/Whisper/scripts/stategraphs/SGmolep..."]:551: attempt to call method 'SetUnderPhysics' (a nil value) LUA ERROR stack traceback: ../mods/Whisper/scripts/stategraphs/SGmolep.lua:551 in (field) onenter (Lua) <550-557> scripts/stategraph.lua:501 in (method) GoToState (Lua) <450-511> ../mods/Whisper/scripts/stategraphs/SGmolep.lua:185 in (field) fn (Lua) <161-189> scripts/stategraph.lua:400 in (method) HandleEvents (Lua) <391-411> scripts/stategraph.lua:145 in (method) Update (Lua) <109-148> scripts/update.lua:218 in () ? (Lua) <149-228> The calls are being made from stategraph SGmolep.lua when I attempt to move after changing. The stategraph should be pulling from my characters prefab, but for whatever reason it's not, this only occurs when caves are on. local function SetUnderPhysics(inst) if inst.isunder ~= true then inst.isunder = true inst.Physics:SetCollisionGroup(COLLISION.CHARACTERS) inst.Physics:ClearCollisionMask() inst.Physics:CollidesWith(COLLISION.WORLD) inst.Physics:CollidesWith(COLLISION.OBSTACLES) end end If anyone could help with this it would be appreciated or if you know an easier way to swap my characters build with a mole and only perform it's walk animation it would be appreciated. Thank you, Red Edited August 1, 2017 by RedHairedHero Link to comment Share on other sites More sharing options...
ZupaleX Posted July 25, 2017 Share Posted July 25, 2017 I think we need more information. Like where is this function you posted located? Link to comment Share on other sites More sharing options...
RedHairedHero Posted July 25, 2017 Author Share Posted July 25, 2017 (edited) Spoiler local MakePlayerCharacter = require "prefabs/player_common" local assets = { Asset("SCRIPT", "scripts/prefabs/player_common.lua"), Asset( "ANIM", "anim/whisper.zip" ), Asset( "ANIM", "anim/whisper_furry.zip" ), Asset( "ANIM", "anim/whisper_dark.zip" ), Asset( "ANIM", "anim/whisper_dark_furry.zip" ), Asset("ANIM", "anim/mole_build.zip"), Asset("ANIM", "anim/mole_basic.zip"), Asset("ANIM", "anim/mole_move_fx.zip"), Asset("ANIM", "anim/player_jump.zip"), Asset("SOUND", "sound/mole.fsb"), } local prefabs = { "mole_move_fx", "molep" } -- Custom starting items local start_inv = { "carrot", "carrot", "carrot", "carrot", } -- When the character is revived from human local function onbecamehuman(inst) -- Set speed when reviving from ghost (optional) inst.components.locomotor:SetExternalSpeedMultiplier(inst, "whisper_speed_mod", 1 + .25) end local function onbecameghost(inst) -- Remove speed modifier when becoming a ghost inst.CanExamine = nil inst:RemoveTag("underground") inst:AddTag("aboveground") inst.components.locomotor:RemoveExternalSpeedMultiplier(inst, "whisper_speed_mod") end -- When loading or spawning the character local function onload(inst) inst:ListenForEvent("ms_respawnedfromghost", onbecamehuman) inst:ListenForEvent("ms_becameghost", onbecameghost) if inst:HasTag("playerghost") then onbecameghost(inst) else onbecamehuman(inst) end end local function oneat(inst, food) local extrahunger = 0 if food.prefab == "carrot" or food.prefab == "carrot_cooked" then if food.components.perishable:IsStale() then extrahunger = 4 else if food.components.perishable:IsSpoiled() then extrahunger = 2 else --if it's fresh extrahunger = 6 end end end if food and food.components.edible and food.prefab == "carrot" then inst.components.hunger:DoDelta(extrahunger); end if food and food.components.edible and food.prefab == "carrot_cooked" then inst.components.hunger:DoDelta(extrahunger); end end local function OnResetBeard(inst) inst:RemoveTag("FURRY") if inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark") else inst.AnimState:SetBuild("whisper") end end local function OnGrowBeard(inst) inst:AddTag("FURRY") if inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark_furry") else inst.AnimState:SetBuild("whisper_furry") end inst.components.beard.bits = 4 end local function CannotExamine(inst) return false end local function SetUnderPhysics(inst) if inst.isunder ~= true then inst.isunder = true inst.Physics:SetCollisionGroup(COLLISION.CHARACTERS) inst.Physics:ClearCollisionMask() inst.Physics:CollidesWith(COLLISION.WORLD) inst.Physics:CollidesWith(COLLISION.OBSTACLES) end end local function SetAbovePhysics(inst) if inst.isunder ~= false then inst.isunder = false ChangeToCharacterPhysics(inst) end end local function burrow (inst) if inst:HasTag("aboveground") then inst:RemoveTag("aboveground") inst:AddTag("underground") inst.AnimState:SetBank("mole") inst.AnimState:SetBuild("mole_build") inst:SetStateGraph("SGmolep") inst.CanExamine = CannotExamine inst.components.locomotor.fasteronroad = false --print("underground") else if inst:HasTag("underground") then inst:RemoveTag("underground") inst:AddTag("aboveground") inst.CanExamine = nil inst.components.locomotor.fasteronroad = true --print("aboveground") end end end local function transform (inst) inst.AnimState:OverrideSymbol("shadow_hands", "shadow_hands", "shadow_hands") if not inst:HasTag("DARK") and not inst:HasTag("notransform") and not inst:HasTag("underground") then--if the user is normal, not underground, and has enough sanity, change to dark inst:AddTag("DARK") inst.components.beard.prize = "beardhair" else if inst:HasTag("DARK") then--if user is dark, change to normal inst:RemoveTag("DARK") inst.components.beard.prize = "manrabbit_tail" end end if inst:HasTag("FURRY") and inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark_furry") else if not inst:HasTag("FURRY") and inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark") else if inst:HasTag("FURRY") and not inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_furry") else inst.AnimState:SetBuild("whisper") end end end if inst:HasTag("DARK") then inst.components.health:SetAbsorptionAmount(0.25) inst.components.combat.damagemultiplier = 1.25 inst.components.sanity.dapperness = (TUNING.CRAZINESS_SMALL)--SMALL Night Armour, MED is Dark Sword inst.components.locomotor:SetExternalSpeedMultiplier(inst, "whisper_speed_mod", 1 ) inst.soundsname = "wickerbottom" else inst.components.health:SetAbsorptionAmount(-0.25) inst.components.combat.damagemultiplier = 0.75 inst.components.sanity.dapperness = 0 inst.components.locomotor:SetExternalSpeedMultiplier(inst, "whisper_speed_mod", 1 + .25) inst.soundsname = "willow" end end local function OnKeyPressed(inst, data) if data.inst == ThePlayer then if data.key == KEY_B and not inst:HasTag("DARK") then if inst:HasTag("aboveground") then inst.AnimState:PlayAnimation("jump") inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/jump") inst:DoTaskInTime(inst.AnimState:GetCurrentAnimationLength(), burrow) else if inst:HasTag("underground") then inst:Hide() ChangeToCharacterPhysics(inst) inst.AnimState:SetBank("wilson") if inst:HasTag("FURRY") then inst.AnimState:SetBuild("whisper_furry") else inst.AnimState:SetBuild("whisper") end inst.SoundEmitter:KillSound("move") inst:SetStateGraph("SGwilson") inst:Show() inst.AnimState:PlayAnimation("jumpout") inst.SoundEmitter:PlaySound("dontstarve/creatures/mandrake/plant_dirt") burrow (inst) end end --print("KEY_B has been pressed.") end if data.key == KEY_T then inst.AnimState:OverrideSymbol("shadow_hands", "shadow_skinchangefx", "shadow_hands") inst.AnimState:OverrideSymbol("shadow_ball", "shadow_skinchangefx", "shadow_ball") inst.AnimState:OverrideSymbol("splode", "shadow_skinchangefx", "splode") inst.AnimState:PlayAnimation("skin_change") inst.SoundEmitter:PlaySound("dontstarve/creatures/werepig/transformToPig") inst:DoTaskInTime(inst.AnimState:GetCurrentAnimationLength(), transform) --print("KEY_T has been pressed.") end end end local function controltransform (inst, data) if not inst:HasTag("playerghost") then if inst.components.sanity.current <= 30 and inst:HasTag("DARK") then --If in dark mode and sanity falls below 30 force the player back into normal mode SendRPCToServer(RPC.DoWidgetButtonAction, ACTIONS.DARKSIDE.code, inst, ACTIONS.DARKSIDE.mod_name) inst:AddTag("notransform") else if inst.components.sanity.current <= 30 then --If the players sanity is below or equal to 30 prevent them from transforming into dark mode inst:AddTag("notransform") else if inst.components.sanity.current > 30 then --If sanity is above 30 allow transformation inst:RemoveTag("notransform") end end end end end local function sanityfn(inst) local delta = 0 if TheWorld.state.isday then delta = TUNING.SANITY_NIGHT_MID end return delta end -- This initializes for both the server and client. Tags can be added here. local common_postinit = function(inst) inst:AddComponent("keyhandler") inst:ListenForEvent("keypressed", OnKeyPressed) inst.MiniMapEntity:SetIcon( "whisper.tex" ) inst:AddTag("aboveground") inst:AddTag("bearded") inst.SetUnderPhysics = SetUnderPhysics inst.SetAbovePhysics = SetAbovePhysics MakeCharacterPhysics(inst, 99999, 0.5) inst.Physics:SetMass(99999) SetUnderPhysics(inst) end -- This initializes for the server only. Components are added here. local master_postinit = function(inst) inst.soundsname = "willow" inst.components.sanity.custom_rate_fn = sanityfn inst.components.eater:SetDiet({ FOODTYPE.VEGGIE }, { FOODTYPE.VEGGIE }) inst:AddComponent("beard") inst.components.beard.onreset = OnResetBeard inst.components.beard:AddCallback( 7 , OnGrowBeard ) inst.components.sanity.night_drain_mult = 0 inst:AddComponent("lootdropper") if inst:HasTag("DARK") then inst.components.beard.prize = "beardhair" else inst.components.beard.prize = "manrabbit_tail" end -- Listener for eating inst.components.eater:SetOnEatFn(oneat) -- Stats inst.components.health:SetMaxHealth(200) inst.components.hunger:SetMax(150) inst.components.sanity:SetMax(150) -- Damage multiplier (optional) inst.components.health:SetAbsorptionAmount(-0.25) inst.components.combat.damagemultiplier = 0.75 -- Hunger rate (optional) if inst:HasTag("aboveground") then inst.components.hunger.hungerrate = 1 * TUNING.WILSON_HUNGER_RATE else if inst:HasTag("underground") then inst.components.hunger.hungerrate = 3 * TUNING.WILSON_HUNGER_RATE end end inst:ListenForEvent("sanitydelta", controltransform) inst.OnLoad = onload inst.OnNewSpawn = onload return inst end return MakePlayerCharacter("whisper", prefabs, assets, common_postinit, master_postinit, start_inv) Spoiler require("stategraphs/commonstates") local WALK_SPEED = 4 local RUN_SPEED = 7 --[[ local MOLE_PEEK_INTERVAL = 20 local MOLE_PEEK_VARIANCE = 5 function TableConcat(t1,t2) for i=1,#t2 do t1[#t1+1] = t2 end return t1 end local actionhandlers = { ActionHandler(ACTIONS.GOHOME, "action"), --ActionHandler(ACTIONS.ATTACK, "attack"), ActionHandler(ACTIONS.PICKUP, "steal_pre_under"), ActionHandler(ACTIONS.PICK, "steal_pre_under"), ActionHandler(ACTIONS.DROP, "action"), ActionHandler(ACTIONS.HARVEST, "action"), ActionHandler(ACTIONS.SLEEPIN, "home"), ActionHandler(ACTIONS.EAT, "action"), ActionHandler(ACTIONS.HEAL, "action"), ActionHandler(ACTIONS.GIVE, "action"), ActionHandler(ACTIONS.GIVETOPLAYER, "action"), ActionHandler(ACTIONS.GIVEALLTOPLAYER, "action"), ActionHandler(ACTIONS.JUMPIN, "action"), ActionHandler(ACTIONS.MIGRATE, "action"), ActionHandler(ACTIONS.COMBINESTACK, "action"), ActionHandler(ACTIONS.DEPLOY, "make_molehill"), --ActionHandler(ACTIONS.PICK, "eat"), ActionHandler(ACTIONS.ACTIVATE, "action"), } local mobCraftActions = { ActionHandler(ACTIONS.GOHOME, "gohome"), --ActionHandler(ACTIONS.ATTACK, "attack"), ActionHandler(ACTIONS.PICKUP, "steal_pre_under"), ActionHandler(ACTIONS.PICK, "steal_pre_under"), ActionHandler(ACTIONS.DROP, "action"), ActionHandler(ACTIONS.ACTIVATE, "action"), ActionHandler(ACTIONS.SLEEPIN, "home"), ActionHandler(ACTIONS.EAT, "action"), ActionHandler(ACTIONS.HEAL, "action"), ActionHandler(ACTIONS.FAN, "action"), ActionHandler(ACTIONS.DIG, "action"), ActionHandler(ACTIONS.CHOP, "action"), ActionHandler(ACTIONS.MINE, "action"), ActionHandler(ACTIONS.GIVE, "action"), ActionHandler(ACTIONS.GIVEALLTOPLAYER, "action"), ActionHandler(ACTIONS.COOK, "action"), ActionHandler(ACTIONS.FILL, "action"), ActionHandler(ACTIONS.DRY, "action"), ActionHandler(ACTIONS.ADDFUEL, "action"), ActionHandler(ACTIONS.ADDWETFUEL, "action"), ActionHandler(ACTIONS.LIGHT, "action"), ActionHandler(ACTIONS.BAIT, "action"), ActionHandler(ACTIONS.BUILD, "action"), ActionHandler(ACTIONS.PLANT, "action"), ActionHandler(ACTIONS.REPAIR, "action"), ActionHandler(ACTIONS.HARVEST, "action"), ActionHandler(ACTIONS.STORE, "action"), ActionHandler(ACTIONS.RUMMAGE, "action"), ActionHandler(ACTIONS.DEPLOY, "action"), ActionHandler(ACTIONS.HAMMER, "action"), ActionHandler(ACTIONS.FERTILIZE, "action"), ActionHandler(ACTIONS.MURDER, "action"), ActionHandler(ACTIONS.UNLOCK, "action"), ActionHandler(ACTIONS.TURNOFF, "action"), ActionHandler(ACTIONS.TURNON, "action"), ActionHandler(ACTIONS.SEW, "action"), ActionHandler(ACTIONS.COMBINESTACK, "action"), ActionHandler(ACTIONS.UPGRADE, "action"), ActionHandler(ACTIONS.WRITE, "action"), ActionHandler(ACTIONS.FEEDPLAYER, "action"), ActionHandler(ACTIONS.TERRAFORM, "action"), ActionHandler(ACTIONS.NET, "action"), ActionHandler(ACTIONS.CHECKTRAP, "action"), ActionHandler(ACTIONS.SHAVE, "action"), ActionHandler(ACTIONS.FISH, "action"), ActionHandler(ACTIONS.REEL, "action"), ActionHandler(ACTIONS.CATCH, "action"), ActionHandler(ACTIONS.TEACH, "action"), ActionHandler(ACTIONS.MANUALEXTINGUISH, "action"), ActionHandler(ACTIONS.RESETMINE, "action"), ActionHandler(ACTIONS.BLINK, "action"), --ActionHandler(ACTIONS.CHANGEIN, "changeskin"), ActionHandler(ACTIONS.SMOTHER, "action"), ActionHandler(ACTIONS.CASTSPELL, "action"), } local extraActions = { --ActionHandler(ACTIONS.SLEEPIN, "sleep"), ActionHandler(ACTIONS.TRAVEL, "taunt"), --ActionHandler(ACTIONS.LOOKAT, "taunt"), } if MOBCRAFTCU == "Enable" then extraActions = mobCraftActions end actionhandlers = TableConcat(actionhandlers, extraActions) --]] local function SetSleeperAwakeState(inst) if inst.components.grue ~= nil then inst.components.grue:RemoveImmunity("sleeping") end if inst.components.talker ~= nil then inst.components.talker:StopIgnoringAll("sleeping") end if inst.components.firebug ~= nil then inst.components.firebug:Enable() end if inst.components.playercontroller ~= nil then inst.components.playercontroller:EnableMapControls(true) inst.components.playercontroller:Enable(true) end inst:OnWakeUp() inst.components.inventory:Show() inst:ShowActions(true) --inst.sg:GoToState("taunt") end local function SetSleeperSleepState(inst) if inst.components.grue ~= nil then inst.components.grue:AddImmunity("sleeping") end if inst.components.talker ~= nil then inst.components.talker:IgnoreAll("sleeping") end if inst.components.firebug ~= nil then inst.components.firebug:Disable() end if inst.components.playercontroller ~= nil then inst.components.playercontroller:EnableMapControls(false) inst.components.playercontroller:Enable(false) end inst:OnSleepIn() inst.components.inventory:Hide() inst:PushEvent("ms_closepopups") inst:ShowActions(false) end local events= { --EventHandler("attacked", function(inst) if not inst.components.health:IsDead() and not inst.sg:HasStateTag("attack") then inst.sg:GoToState("hit") end end), --EventHandler("doattack", function(inst, data) inst.sg:GoToState("attack", data.target) end), EventHandler("death", function(inst) inst.sg:GoToState("death") end), --CommonHandlers.OnSleep(), --CommonHandlers.OnFreeze(), --CommonHandlers.OnLocomote(true,false), EventHandler("locomote", function(inst, data) if inst.sg:HasStateTag("busy") or inst.sg:HasStateTag("sleeping") and not inst.sg:HasStateTag("home") then return end local is_moving = inst.sg:HasStateTag("moving") local should_move = inst.components.locomotor:WantsToMoveForward() if inst.sg:HasStateTag("home") or inst.sg:HasStateTag("home_waking") then -- wakeup on locomote if inst.sleepingbag ~= nil and inst.sg:HasStateTag("sleeping") and inst.sg:HasStateTag("home") then inst.sleepingbag.components.sleepingbag:DoWakeUp() inst.sleepingbag = nil inst:Show() --inst.AnimState:PushAnimation("pig_pickup") SetSleeperAwakeState(inst) --inst.AnimState:PushAnimation("pig_pickup") --inst.sg:GoToState("idle") inst.AnimState:PlayAnimation("idle_under") inst.sg:GoToState("idle", true) end elseif is_moving and not should_move then inst.sg:GoToState("run_stop") elseif not is_moving and should_move then inst.sg:GoToState("run_start") elseif data.force_idle_state and not (is_moving or should_move or inst.sg:HasStateTag("idle")) then inst.sg:GoToState("idle") end end), --[[ EventHandler("ms_opengift", function(inst) if not inst.sg:HasStateTag("busy") then inst.sg:GoToState("opengift") end end), --]] } local function SpawnMoveFx(inst) SpawnPrefab("mole_move_fx").Transform:SetPosition(inst.Transform:GetWorldPosition()) end local function PlayStunnedSound(inst) if not inst.SoundEmitter:PlayingSound("stunned") then inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/sleep", "stunned") end end local function KillStunnedSound(inst) inst.SoundEmitter:KillSound("stunned") end local states= { State{ name = "death", tags = {"busy"}, onenter = function(inst) inst.SoundEmitter:KillSound("move") inst.SoundEmitter:KillSound("sniff") --inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/death") inst.AnimState:PlayAnimation("death") inst.Physics:Stop() RemovePhysicsColliders(inst) --inst.components.lootdropper:DropLoot(Vector3(inst.Transform:GetWorldPosition())) inst.components.inventory:DropEverything(true) if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() end end, timeline = { }, events = { EventHandler("animover", function(inst) if inst.AnimState:AnimDone() then if MOBGHOSTCU == "Enable" then inst:PushEvent(inst.ghostenabled and "makeplayerghost" or "playerdied", { skeleton = false }) else TheWorld:PushEvent("ms_playerdespawnanddelete", inst) end --inst:PushEvent(inst.ghostenabled and "makeplayerghost" or "playerdied", { skeleton = false }) end end), }, }, --[[ State{ name = "opengift", tags = { "busy", "pausepredict" }, onenter = function(inst) inst.components.locomotor:Stop() inst.components.locomotor:Clear() inst:ClearBufferedAction() --if IsNearDanger(inst) then --inst.sg.statemem.isdanger = true --inst.sg:GoToState("idle") --if inst.components.talker ~= nil then -- inst.components.talker:Say(GetString(inst, "ANNOUNCE_NODANGERGIFT")) --end --return -- end inst.SoundEmitter:PlaySound("dontstarve/common/player_receives_gift") --inst.AnimState:PlayAnimation("taunt") --inst.AnimState:PushAnimation("taunt", true) -- NOTE: the previously used ripping paper anim is called "giift_loop" if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() inst.components.playercontroller:EnableMapControls(false) inst.components.playercontroller:Enable(false) end inst.components.inventory:Hide() inst:PushEvent("ms_closepopups") inst:ShowActions(false) inst:ShowGiftItemPopUp(true) if inst.components.giftreceiver ~= nil then inst.components.giftreceiver:OnStartOpenGift() end end, timeline = { -- Timing of the gift box opening animation on giftitempopup.lua TimeEvent(155 * FRAMES, function(inst) -- inst.AnimState:PlayAnimation("gift_open_pre") --inst.AnimState:PushAnimation("taunt", true) end), }, events = { EventHandler("firedamage", function(inst) --inst.AnimState:PlayAnimation("taunt") inst.sg:GoToState("idle", true) if inst.components.talker ~= nil then inst.components.talker:Say(GetString(inst, "ANNOUNCE_NODANGERGIFT")) end end), EventHandler("ms_doneopengift", function(inst, data) inst.sg:GoToState("idle", true) end), }, onexit = function(inst) if inst.sg.statemem.isdanger then return elseif not inst.sg.statemem.isopeningwardrobe then if inst.components.playercontroller ~= nil then inst.components.playercontroller:EnableMapControls(true) inst.components.playercontroller:Enable(true) end inst.components.inventory:Show() inst:ShowActions(true) end inst:ShowGiftItemPopUp(false) end, }, State { name = "enter", tags = { "busy" }, onenter = function(inst) inst.Physics:Stop() inst:SetAbovePhysics() inst.AnimState:PlayAnimation("enter") inst.SoundEmitter:KillSound("move") end, events = { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end), }, }, State { name = "peek", tags = { "busy" }, onenter = function(inst) inst.Physics:Stop() inst:SetAbovePhysics() inst.SoundEmitter:KillSound("move") inst.AnimState:PlayAnimation("enter") inst.peek_interval = GetRandomWithVariance(MOLE_PEEK_INTERVAL, MOLE_PEEK_VARIANCE) inst.last_above_time = GetTime() inst:PerformBufferedAction() end, timeline = { TimeEvent(1*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/emerge") end), TimeEvent(3*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/emerge_voice") end), }, events = { EventHandler("animover", function(inst) inst.sg:GoToState("exit") end), }, }, State { name = "steal_pre_under", tags = { "busy" }, onenter = function(inst, data) inst.Physics:Stop() inst:SetAbovePhysics() inst.SoundEmitter:KillSound("move") inst.AnimState:PlayAnimation("enter") inst.AnimState:PushAnimation("idle", false) end, timeline = { TimeEvent(1*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/emerge") end), TimeEvent(3*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/emerge_voice") end), TimeEvent(26*FRAMES, function(inst) if not inst.SoundEmitter:PlayingSound("sniff") then inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/sniff", "sniff") end end), TimeEvent(77*FRAMES, function(inst) inst.SoundEmitter:KillSound("sniff") end), }, events = { EventHandler("animqueueover", function(inst) inst.sg:GoToState("steal") end), }, onexit = function(inst) inst.SoundEmitter:KillSound("sniff") end, }, State { name = "steal_pre_above", tags = { "busy" }, onenter = function(inst, data) inst.Physics:Stop() inst:SetAbovePhysics() inst.SoundEmitter:KillSound("move") inst.AnimState:PlayAnimation("idle", false) end, timeline = { TimeEvent(1*FRAMES, function(inst) if not inst.SoundEmitter:PlayingSound("sniff") then inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/sniff", "sniff") end end), TimeEvent(52*FRAMES, function(inst) inst.SoundEmitter:KillSound("sniff") end), }, events = { EventHandler("animqueueover", function(inst) inst.sg:GoToState("steal") end) }, onexit = function(inst) inst.SoundEmitter:KillSound("sniff") end, }, State { name = "steal", tags = { "busy", "canrotate" }, onenter = function(inst, playanim) inst.Physics:Stop() inst:SetAbovePhysics() inst.AnimState:PlayAnimation("action") inst.AnimState:PushAnimation("idle", false) end, timeline = { TimeEvent(9*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/pickup") end), TimeEvent(12*FRAMES, function(inst) inst:PerformBufferedAction() end), TimeEvent(27*FRAMES, function(inst) if not inst.SoundEmitter:PlayingSound("sniff") then inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/sniff", "sniff") end end), TimeEvent(78*FRAMES, function(inst) inst.SoundEmitter:KillSound("sniff") end), }, events = { EventHandler("animqueueover", function(inst) inst.sg:GoToState("exit") end), }, onexit = function(inst) inst.SoundEmitter:KillSound("sniff") end, }, State { name = "exit", tags = { "busy" }, onenter = function(inst) inst.Physics:Stop() inst:SetAbovePhysics() inst.AnimState:PlayAnimation("exit") -- if inst.components.burnable:IsBurning() then -- inst.components.burnable:Extinguish() -- end end, timeline = { TimeEvent(8*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/jump") end), --TimeEvent(24*FRAMES, function(inst) end), TimeEvent(26*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/retract") end), TimeEvent(43*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/retract") end), }, events = { EventHandler("animover", function(inst) inst:SetUnderPhysics() inst.last_above_time = GetTime() inst.sg:GoToState("idle") end), }, }, --]] State { name = "idle", tags = { "idle", "canrotate" }, onenter = function(inst, playanim) inst.Physics:Stop() inst.SoundEmitter:KillSound("move") if inst.isunder then inst.sg:AddStateTag("noattack") end inst.AnimState:PlayAnimation(inst.isunder and "idle_under" or "idle", true) end, timeline = { TimeEvent(1*FRAMES, function(inst) if not (inst.isunder or inst.SoundEmitter:PlayingSound("sniff")) then inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/sniff", "sniff") end end), TimeEvent(52*FRAMES, function(inst) inst.SoundEmitter:KillSound("sniff") end), }, }, State { name = "run_start", tags = { "moving", "canrotate", "noattack", "invisible" }, onenter = function(inst) inst:SetUnderPhysics() inst.AnimState:PlayAnimation("walk_pre") if not inst.SoundEmitter:PlayingSound("move") then inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/move", "move") end inst.components.locomotor:WalkForward() end, events = { EventHandler("animover", function(inst) inst.sg:GoToState("run") end), } }, State { name = "run", tags = { "moving", "canrotate", "noattack", "invisible" }, onenter = function(inst) inst:SetUnderPhysics() inst.AnimState:PlayAnimation("walk_loop") inst.components.locomotor:WalkForward() end, timeline = { TimeEvent(0*FRAMES, SpawnMoveFx), TimeEvent(5*FRAMES, SpawnMoveFx), TimeEvent(10*FRAMES, SpawnMoveFx), TimeEvent(15*FRAMES, SpawnMoveFx), TimeEvent(20*FRAMES, SpawnMoveFx), TimeEvent(25*FRAMES, SpawnMoveFx), }, events = { EventHandler("animover", function(inst) inst.sg:GoToState("run") end), } }, State { name = "run_stop", tags = { "canrotate", "noattack", "invisible" }, onenter = function(inst) inst:SetUnderPhysics() inst.components.locomotor:StopMoving() --local should_softstop = false --if should_softstop then --inst.AnimState:PushAnimation("walk_pst") --else inst.AnimState:PlayAnimation("walk_pst") --end inst.SoundEmitter:KillSound("move") end, events = { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end), }, }, --[[ State { name = "action", tags = { "canrotate", "noattack" }, onenter = function(inst) inst:SetUnderPhysics() inst.components.locomotor:StopMoving() --local should_softstop = false --if should_softstop then --inst.AnimState:PushAnimation("walk_pst") --else inst.AnimState:PlayAnimation("walk_pst") --end inst:PerformBufferedAction() inst.SoundEmitter:KillSound("move") end, events = { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end), }, }, State { name = "gohome", tags = { "canrotate" }, onenter = function(inst, playanim) inst.Physics:Stop() if inst.isunder then inst.sg:AddStateTag("noattack") inst.AnimState:PlayAnimation("idle_under") else inst.AnimState:PlayAnimation("idle") end inst:PerformBufferedAction() end, events = { EventHandler("animover", function (inst, data) inst.sg:GoToState("idle") end), }, }, State { name = "make_molehill", tags = { "busy", "noattack" }, onenter = function(inst, playanim) inst.Physics:Stop() inst:SetUnderPhysics() inst.AnimState:PlayAnimation("mound") end, timeline = { TimeEvent(16*FRAMES, function(inst) inst:SetAbovePhysics() inst.sg:RemoveStateTag("noattack") inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/emerge") end), TimeEvent(30*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/emerge_voice") end), }, events = { EventHandler("animover", function(inst, data) inst:PerformBufferedAction() inst:SetUnderPhysics() inst.last_above_time = GetTime() inst.sg:GoToState("idle") end), }, }, State { name = "hit", tags = { "busy" }, onenter = function(inst) inst.SoundEmitter:KillSound("move") inst.SoundEmitter:KillSound("sniff") inst.AnimState:PlayAnimation("hit") inst.Physics:Stop() inst:SetAbovePhysics() end, events = { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end), }, }, State { name = "sleep", tags = { "busy", "sleeping" }, onenter = function(inst) inst.components.locomotor:StopMoving() if inst.isunder then inst:SetAbovePhysics() inst.AnimState:PlayAnimation("enter") inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/emerge") inst.AnimState:PushAnimation("sleep_pre", false) else inst.AnimState:PlayAnimation("sleep_pre") end end, timeline = { TimeEvent(FRAMES, function(inst) inst.SoundEmitter:KillSound("sniff") inst.SoundEmitter:KillSound("stunned") end), }, events = { EventHandler("animqueueover", function(inst) inst.sg:GoToState("sleeping") end), EventHandler("onwakeup", function(inst) inst.sg:GoToState("wake") end), }, }, State { name = "sleeping", tags = { "sleeping" }, onenter = function(inst) inst:SetAbovePhysics() inst.AnimState:PlayAnimation("sleep_loop") end, timeline = { TimeEvent(27*FRAMES, function(inst) if not inst.SoundEmitter:PlayingSound("sleep") then inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/sleep", "sleep") end end), TimeEvent(42*FRAMES, function(inst) inst.SoundEmitter:KillSound("sleep") end), }, events = { EventHandler("animover", function(inst) inst.sg:GoToState("sleeping") end), EventHandler("onwakeup", function(inst) inst.sg:GoToState("wake") end), }, }, State { name = "wake", tags = { "busy", "waking" }, onenter = function(inst) inst:SetAbovePhysics() inst.components.locomotor:StopMoving() inst.AnimState:PlayAnimation("sleep_pst") if inst.components.sleeper ~= nil and inst.components.sleeper:IsAsleep() then inst.components.sleeper:WakeUp() end end, timeline = { TimeEvent(FRAMES, function(inst) inst.SoundEmitter:KillSound("sleep") end) }, events = { EventHandler("animover", function(inst) inst.sg:GoToState("exit") end), }, }, ------------------HOME SLEEPING----------------- State{ name = "home", tags = {"busy", "silentmorph", "invisible" }, onenter = function(inst) inst.components.locomotor:Stop() local target = inst:GetBufferedAction().target --local siesta = HasTag("siestahut") local failreason = -- (siesta ~= TheWorld.state.isday and --(siesta --and (TheWorld:HasTag("cave") and "ANNOUNCE_NONIGHTSIESTA_CAVE" or "ANNOUNCE_NONIGHTSIESTA") --or (TheWorld:HasTag("cave") and "ANNOUNCE_NODAYSLEEP_CAVE" or "ANNOUNCE_NODAYSLEEP")) -- )or (target.components.burnable ~= nil and target.components.burnable:IsBurning() and "ANNOUNCE_NOSLEEPONFIRE") --or (IsNearDanger(inst) and "ANNOUNCE_NODANGERSLEEP") -- you can still sleep if your hunger will bottom out, but not absolutely or (inst.components.hunger.current < TUNING.CALORIES_MED and "ANNOUNCE_NOHUNGERSLEEP") or (inst.components.beaverness ~= nil and inst.components.beaverness:IsStarving() and "ANNOUNCE_NOHUNGERSLEEP") or nil if failreason ~= nil then inst:PushEvent("performaction", { action = inst.bufferedaction }) inst:ClearBufferedAction() inst.sg:GoToState("idle") if inst.components.talker ~= nil then inst.components.talker:Say(GetString(inst, failreason)) end return end inst.AnimState:PlayAnimation("idle_under") inst.sg:SetTimeout(11 * FRAMES) SetSleeperSleepState(inst) end, ontimeout = function(inst) local bufferedaction = inst:GetBufferedAction() if bufferedaction == nil then inst.AnimState:PlayAnimation("pig_pickup") inst.sg:GoToState("idle", true) return end local home = bufferedaction.target if home == nil or not home:HasTag("molehouse") or --home:HasTag("hassleeper") or --home:HasTag("siestahut") ~= TheWorld.state.isday or (home.components.burnable ~= nil and home.components.burnable:IsBurning()) then --Edge cases, don't bother with fail dialogue --Also, think I will let smoldering pass this one inst:PushEvent("performaction", { action = inst.bufferedaction }) inst:ClearBufferedAction() inst.AnimState:PlayAnimation("idle_under") inst.sg:GoToState("idle", true) else inst:PerformBufferedAction() inst.SoundEmitter:KillSound("move") inst.components.health:SetInvincible(true) inst:Hide() if inst.Physics ~= nil then inst.Physics:Teleport(inst.Transform:GetWorldPosition()) end if inst.DynamicShadow ~= nil then inst.DynamicShadow:Enable(false) end inst.sg:AddStateTag("sleeping") inst.sg:AddStateTag("home") inst.sg:RemoveStateTag("busy") if inst.components.playercontroller ~= nil then inst.components.playercontroller:Enable(true) end end end, onexit = function(inst) inst.components.health:SetInvincible(false) inst:Show() if inst.DynamicShadow ~= nil then inst.DynamicShadow:Enable(true) end if inst.sleepingbag ~= nil then --Interrupted while we are "sleeping" inst.sleepingbag.components.sleepingbag:DoWakeUp(true) inst.sleepingbag = nil SetSleeperAwakeState(inst) elseif not inst.sg.statemem.iswaking then --Interrupted before we are "sleeping" SetSleeperAwakeState(inst) end end, }, State { name = "home_sleeping", tags = { "sleeping", "home" }, --onenter = onentersleeping, onenter = function(inst) inst.components.locomotor:StopMoving() inst.components.health:DoDelta(3.5, false) --inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/bearger/sleep") inst.AnimState:PlayAnimation("sleep_loop") end, --timeline = timelines ~= nil and timelines.sleeptimeline or nil, timeline = { TimeEvent(35*FRAMES, function(inst) inst.SoundEmitter:PlaySound(SoundPath(inst, "sleeping")) end ), }, events = { EventHandler("animover", function(inst) inst.sg:GoToState("sleeping") end ), EventHandler("onwakeup", function(inst) inst.sg:GoToState("wake") end), }, }, State { name = "home_wake", tags = { "busy", "waking", "home" }, onenter = function(inst) if inst.components.locomotor ~= nil then inst.components.locomotor:StopMoving() end --inst.SoundEmitter:PlaySound("dontstarve/creatures/spiderqueen/wakeup") inst.AnimState:PlayAnimation("sleep_pst") if inst.components.sleeper ~= nil and inst.components.sleeper:IsAsleep() then inst.components.sleeper:WakeUp() end --if fns ~= nil and fns.onwake ~= nil then --fns.onwake(inst) --end end, --timeline = timelines ~= nil and timelines.waketimeline or nil, timeline = { TimeEvent(0*FRAMES, function(inst) inst.SoundEmitter:PlaySound(SoundPath(inst, "wakeUp")) end ), }, events = { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end), }, }, --]] } --CommonStates.AddFrozenStates(states) return StateGraph("molep", states, events, "idle") Here's the prefab and the stategraph. Edited July 25, 2017 by RedHairedHero Link to comment Share on other sites More sharing options...
ZupaleX Posted July 25, 2017 Share Posted July 25, 2017 Thanks, could you just put them inside spoilers so when I want to scroll to the last post I don't have to molest my mouse? ^^ Link to comment Share on other sites More sharing options...
ZupaleX Posted July 25, 2017 Share Posted July 25, 2017 (edited) One stuff comes to my mind. When you activate the cave the behavior is the same as on a dedicated server. The player hosting is not anymore a " mastersim" (as far as I know). So if you did not plan some client side code in here you'll get a surprise. master_postinit is only executed on the host side if I am correct, so it doesn't exists for the client. If the client is executing some code involving this function => crash EDIT: I just checked, master_postinit is indeed host only. Then I don't know if you took precaution that SetUnderPhysics method is never called on the client side but that's where I would start. Edited July 25, 2017 by ZupaleX Link to comment Share on other sites More sharing options...
RedHairedHero Posted July 25, 2017 Author Share Posted July 25, 2017 1 minute ago, ZupaleX said: One stuff comes to my mind. When you activate the cave the behavior is the same as on a dedicated server. The player hosting is not anymore a " mastersim" (as far as I know). So if you did not plan some client side code in here you'll get a surprise. master_postinit is only executed on the host side if I am correct, so it doesn't exists for the client. If the client is executing some code involving this function => crash So is it as simple as moving this into common postinit? inst.SetUnderPhysics = SetUnderPhysics inst.SetAbovePhysics = SetAbovePhysics MakeCharacterPhysics(inst, 99999, 0.5) inst.Physics:SetMass(99999) SetUnderPhysics(inst) Link to comment Share on other sites More sharing options...
ZupaleX Posted July 25, 2017 Share Posted July 25, 2017 (edited) AddPhysics is done on the client side as well, so that might work. EDIT: for characters, AddPhysics is not called directly but implicitly called by MakeCharacterPhysics which is supposed to be in the common_postinit as it should be executed on the client as well. EDIT2: btw thanks for the spoiler tag but now they are empty Edited July 25, 2017 by ZupaleX Link to comment Share on other sites More sharing options...
RedHairedHero Posted July 25, 2017 Author Share Posted July 25, 2017 5 minutes ago, ZupaleX said: AddPhysics is done on the client side as well, so that might work. Omg I can't believe that fixed it, I hate coding lol Link to comment Share on other sites More sharing options...
ZupaleX Posted July 25, 2017 Share Posted July 25, 2017 Glad it worked. I love coding. Link to comment Share on other sites More sharing options...
RedHairedHero Posted July 25, 2017 Author Share Posted July 25, 2017 (edited) 15 hours ago, ZupaleX said: Glad it worked. I love coding. So actually I'm running into a new issue. The character transforms and it works great, but pressing the same button is suppose to turn the character back to their previous state, but it's not. Instead when you press it again the only thing that happens is the character hides for a second and a sound plays. Any ideas? I fixed the spoilers. Edited July 25, 2017 by RedHairedHero Link to comment Share on other sites More sharing options...
ZupaleX Posted July 25, 2017 Share Posted July 25, 2017 Yes. I did not look into the details but here are my concerns. You call your function burrow, which is the one assigning the tag "underground" or "aboveground" only on the client side. Adding a tag on the client side won't propagate it to the server side. And other things are not propagated currently, like changing the SG, the physics, etc... In other words the display works for you, but anybody else on the server would still see the human build and probably see your character behaving exactly the same way as if you did not press that button. You need to send RPC to the server when you press the button so the server actually knows that you expect something to happen. Link to comment Share on other sites More sharing options...
RedHairedHero Posted July 28, 2017 Author Share Posted July 28, 2017 (edited) Spoiler local MakePlayerCharacter = require "prefabs/player_common" local assets = { Asset("SCRIPT", "scripts/prefabs/player_common.lua"), Asset( "ANIM", "anim/whisper.zip" ), Asset( "ANIM", "anim/whisper_furry.zip" ), Asset( "ANIM", "anim/whisper_dark.zip" ), Asset( "ANIM", "anim/whisper_dark_furry.zip" ), } local prefabs = { } local start_inv = { "carrot", "carrot", "carrot", "carrot", } local function onbecamehuman(inst) -- Set speed when reviving from ghost (optional) inst.components.locomotor:SetExternalSpeedMultiplier(inst, "whisper_speed_mod", 1 + .25) end local function onbecameghost(inst) inst.CanExamine = nil inst:RemoveTag("underground") inst:AddTag("aboveground") inst.components.locomotor:RemoveExternalSpeedMultiplier(inst, "whisper_speed_mod") end local function onload(inst) inst:ListenForEvent("ms_respawnedfromghost", onbecamehuman) inst:ListenForEvent("ms_becameghost", onbecameghost) if inst:HasTag("playerghost") then onbecameghost(inst) else onbecamehuman(inst) end end local function oneat(inst, food) local extrahunger = 0 if food.prefab == "carrot" or food.prefab == "carrot_cooked" then if food.components.perishable:IsStale() then extrahunger = 4 else if food.components.perishable:IsSpoiled() then extrahunger = 2 else --if it's fresh extrahunger = 6 end end end if food and food.components.edible and food.prefab == "carrot" then inst.components.hunger:DoDelta(extrahunger); end if food and food.components.edible and food.prefab == "carrot_cooked" then inst.components.hunger:DoDelta(extrahunger); end end local function OnResetBeard(inst) inst:RemoveTag("FURRY") if inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark") else inst.AnimState:SetBuild("whisper") end end local function OnGrowBeard(inst) inst:AddTag("FURRY") if inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark_furry") else inst.AnimState:SetBuild("whisper_furry") end inst.components.beard.bits = 4 end local function CannotExamine(inst) return false end local function SetUnderPhysics(inst) if inst.isunder ~= true then inst.isunder = true inst.Physics:SetCollisionGroup(COLLISION.CHARACTERS) inst.Physics:ClearCollisionMask() inst.Physics:CollidesWith(COLLISION.WORLD) inst.Physics:CollidesWith(COLLISION.OBSTACLES) end end local function SetAbovePhysics(inst) if inst.isunder ~= false then inst.isunder = false ChangeToCharacterPhysics(inst) end end local function OnKeyPressed(inst, data) if data.inst == ThePlayer then if data.key == KEY_B and not inst:HasTag("DARK") then if TheWorld.ismastersim and inst:HasTag("aboveground") then -- Since we are the server, do the action on the server. BufferedAction(inst, inst, ACTIONS.ENTER_BURROW):Do() else SendRPCToServer(RPC.DoWidgetButtonAction, ACTIONS.ENTER_BURROW.code, inst, ACTIONS.ENTER_BURROW.mod_name) end if TheWorld.ismastersim and inst:HasTag("underground") then -- Since we are the server, do the action on the server. BufferedAction(inst, inst, ACTIONS.EXIT_BURROW):Do() else SendRPCToServer(RPC.DoWidgetButtonAction, ACTIONS.EXIT_BURROW.code, inst, ACTIONS.EXIT_BURROW.mod_name) end --print("KEY_B has been pressed.") end if data.key == KEY_T then if TheWorld.ismastersim then -- Since we are the server, do the action on the server. BufferedAction(inst, inst, ACTIONS.DARKSIDE):Do() else SendRPCToServer(RPC.DoWidgetButtonAction, ACTIONS.DARKSIDE.code, inst, ACTIONS.DARKSIDE.mod_name) end --print("KEY_T has been pressed.") end end end local function controltransform (inst, data) if not inst:HasTag("playerghost") then if inst.components.sanity.current <= 30 and inst:HasTag("DARK") then --If in dark mode and sanity falls below 30 force the player back into normal mode SendRPCToServer(RPC.DoWidgetButtonAction, ACTIONS.DARKSIDE.code, inst, ACTIONS.DARKSIDE.mod_name) inst:AddTag("notransform") else if inst.components.sanity.current <= 30 then --If the players sanity is below or equal to 30 prevent them from transforming into dark mode inst:AddTag("notransform") else if inst.components.sanity.current > 30 then --If sanity is above 30 allow transformation inst:RemoveTag("notransform") end end end end end local function sanityfn(inst) local delta = 0 if TheWorld.state.isday then delta = TUNING.SANITY_NIGHT_MID end return delta end -- This initializes for both the server and client. Tags can be added here. local common_postinit = function(inst) inst:AddComponent("keyhandler") inst:ListenForEvent("keypressed", OnKeyPressed) inst.MiniMapEntity:SetIcon( "whisper.tex" ) inst:AddTag("aboveground") inst:AddTag("bearded") inst.SetUnderPhysics = SetUnderPhysics inst.SetAbovePhysics = SetAbovePhysics MakeCharacterPhysics(inst, 99999, 0.5) inst.Physics:SetMass(99999) SetUnderPhysics(inst) end -- This initializes for the server only. Components are added here. local master_postinit = function(inst) inst.soundsname = "willow" inst.components.sanity.custom_rate_fn = sanityfn inst.components.eater:SetDiet({ FOODTYPE.VEGGIE }, { FOODTYPE.VEGGIE }) inst:AddComponent("beard") inst.components.beard.onreset = OnResetBeard inst.components.beard:AddCallback( 7 , OnGrowBeard ) inst.components.sanity.night_drain_mult = 0 inst:AddComponent("lootdropper") if inst:HasTag("DARK") then inst.components.beard.prize = "beardhair" else inst.components.beard.prize = "manrabbit_tail" end inst.components.eater:SetOnEatFn(oneat) inst.components.health:SetMaxHealth(200) inst.components.hunger:SetMax(150) inst.components.sanity:SetMax(150) inst.components.health:SetAbsorptionAmount(-0.25) inst.components.combat.damagemultiplier = 0.75 if inst:HasTag("aboveground") then inst.components.hunger.hungerrate = 1 * TUNING.WILSON_HUNGER_RATE else if inst:HasTag("underground") then inst.components.hunger.hungerrate = 3 * TUNING.WILSON_HUNGER_RATE end end inst:ListenForEvent("sanitydelta", controltransform) inst.OnLoad = onload inst.OnNewSpawn = onload return inst end return MakePlayerCharacter("whisper", prefabs, assets, common_postinit, master_postinit, start_inv) Spoiler PrefabFiles = { "whisper", "whisper_none", } Assets = { Asset( "IMAGE", "images/saveslot_portraits/whisper.tex" ), Asset( "ATLAS", "images/saveslot_portraits/whisper.xml" ), Asset( "IMAGE", "images/selectscreen_portraits/whisper.tex" ), Asset( "ATLAS", "images/selectscreen_portraits/whisper.xml" ), Asset( "IMAGE", "images/selectscreen_portraits/whisper_silho.tex" ), Asset( "ATLAS", "images/selectscreen_portraits/whisper_silho.xml" ), Asset( "IMAGE", "bigportraits/whisper.tex" ), Asset( "ATLAS", "bigportraits/whisper.xml" ), Asset( "IMAGE", "images/map_icons/whisper.tex" ), Asset( "ATLAS", "images/map_icons/whisper.xml" ), Asset( "IMAGE", "images/avatars/avatar_whisper.tex" ), Asset( "ATLAS", "images/avatars/avatar_whisper.xml" ), Asset( "IMAGE", "images/avatars/avatar_ghost_whisper.tex" ), Asset( "ATLAS", "images/avatars/avatar_ghost_whisper.xml" ), Asset( "IMAGE", "images/avatars/self_inspect_whisper.tex" ), Asset( "ATLAS", "images/avatars/self_inspect_whisper.xml" ), Asset( "IMAGE", "images/names_whisper.tex" ), Asset( "ATLAS", "images/names_whisper.xml" ), Asset( "IMAGE", "bigportraits/whisper_none.tex" ), Asset( "ATLAS", "bigportraits/whisper_none.xml" ), Asset( "ANIM", "anim/whisper.zip" ), Asset( "ANIM", "anim/whisper_furry.zip" ), Asset( "ANIM", "anim/whisper_dark.zip" ), Asset( "ANIM", "anim/whisper_dark_furry.zip" ), Asset("SCRIPT", "scripts/prefabs/whisper.lua"), } local require = GLOBAL.require local STRINGS = GLOBAL.STRINGS modimport("libs/env.lua") --active key -- Import the lib use. modimport("libs/use.lua") -- Import the mod environment as our environment. use "libs/mod_env"(env) -- Imports to keep the keyhandler from working while typing in chat. use "data/widgets/controls" use "data/screens/chatinputscreen" use "data/screens/consolescreen" -- The character select screen lines STRINGS.CHARACTER_TITLES.whisper = "The Bunny" STRINGS.CHARACTER_NAMES.whisper = "Whisper" STRINGS.CHARACTER_DESCRIPTIONS.whisper = "*Has a dark side \n*Is a Bunnyman \n*Is a vegetarian" STRINGS.CHARACTER_QUOTES.whisper = "\"Embrace the dark side.\"" -- Custom speech strings STRINGS.CHARACTERS.WHISPER = require "speech_whisper" -- The character's name as appears in-game STRINGS.NAMES.WHISPER = "Whisper" AddMinimapAtlas("images/map_icons/whisper.xml") -- Add mod character to mod character list. Also specify a gender. Possible genders are MALE, FEMALE, ROBOT, NEUTRAL, and PLURAL. AddModCharacter("whisper", "FEMALE") local transform = State( { name = "transforming", tags = { "busy", "pausepredict", "nomorph", "nodangle" }, onenter = function(inst, cb) inst.components.locomotor:StopMoving() inst.sg.statemem.cb = cb inst.AnimState:OverrideSymbol("shadow_hands", "shadow_skinchangefx", "shadow_hands") inst.AnimState:OverrideSymbol("shadow_ball", "shadow_skinchangefx", "shadow_ball") inst.AnimState:OverrideSymbol("splode", "shadow_skinchangefx", "splode") inst.AnimState:PlayAnimation("skin_change", false)--true loops if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() end end, timeline = { --frame 42, hidden TimeEvent(62 * FRAMES, function(inst) if inst.sg.statemem.cb ~= nil then inst.sg.statemem.cb() inst.sg.statemem.cb = nil end end), }, events = { EventHandler("animqueueover", function(inst) if inst.AnimState:AnimDone() then inst.sg:GoToState("idle") end end), }, onexit = function(inst) if inst:HasTag("FURRY") and inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark_furry") else if not inst:HasTag("FURRY") and inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark") else if inst:HasTag("FURRY") and not inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_furry") else inst.AnimState:SetBuild("whisper") end end end if inst:HasTag("DARK") then inst.components.combat.damagemultiplier = 1.25 inst.components.sanity.dapperness = (TUNING.CRAZINESS_SMALL)--SMALL Night Armour, MED is Dark Sword inst.components.locomotor:SetExternalSpeedMultiplier(inst, "whisper_speed_mod", 1 ) else inst.components.combat.damagemultiplier = 1.0 inst.components.sanity.dapperness = 0 inst.components.locomotor:SetExternalSpeedMultiplier(inst, "whisper_speed_mod", 1 + .25) end if inst.sg.statemem.cb ~= nil then -- in case of interruption inst.sg.statemem.cb() inst.sg.statemem.cb = nil end inst.AnimState:OverrideSymbol("shadow_hands", "shadow_hands", "shadow_hands") if inst.components.playercontroller ~= nil then inst.components.playercontroller:EnableMapControls(true) inst.components.playercontroller:Enable(true) end inst.components.inventory:Show() inst:ShowActions(true) end, } ) AddStategraphState("wilson", transform) local DARKSIDE = GLOBAL.Action() DARKSIDE.str = "Darkside" DARKSIDE.id = "DARKSIDE" DARKSIDE.fn = function(act) if not act.target:HasTag("DARK") and not act.target:HasTag("notransform") then--if the user is normal and can transform, change to dark act.target:AddTag("DARK") act.target.SoundEmitter:PlaySound("dontstarve/creatures/werepig/transformToPig") act.target.sg:GoToState("transforming") act.target.components.beard.prize = "beardhair" else if act.target:HasTag("DARK") then--if user is dark, change to normal act.target:RemoveTag("DARK") act.target.SoundEmitter:PlaySound("dontstarve/creatures/werepig/transformToPig") act.target.sg:GoToState("transforming") act.target.components.beard.prize = "manrabbit_tail" end end end AddAction(DARKSIDE) local enter_burrow = State( { name = "enter_burrow", tags = { "busy", "pausepredict", "nomorph", "nodangle" }, onenter = function(inst, cb) inst.components.locomotor:StopMoving() inst.sg.statemem.cb = cb inst.AnimState:PlayAnimation("jump", false) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/jump") if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() end end, timeline = { TimeEvent(15 * FRAMES, function(inst) if not inst.sg.statemem.heavy then inst.Physics:Stop() end end), }, events = { EventHandler("animqueueover", function(inst) if inst.AnimState:AnimDone() then inst.sg:GoToState("idle") end end), }, onexit = function(inst) inst:RemoveTag("aboveground") inst:AddTag("underground") inst.AnimState:SetBank("mole") inst.AnimState:SetBuild("mole_build") inst:SetStateGraph("SGburrow") inst.CanExamine = CannotExamine inst.components.locomotor.fasteronroad = false --print("underground") if inst.sg.statemem.cb ~= nil then -- in case of interruption inst.sg.statemem.cb() inst.sg.statemem.cb = nil end if inst.components.playercontroller ~= nil then inst.components.playercontroller:EnableMapControls(true) inst.components.playercontroller:Enable(true) end inst.components.inventory:Show() inst:ShowActions(true) end, } ) AddStategraphState("wilson", enter_burrow) local ENTER_BURROW = GLOBAL.Action() ENTER_BURROW.str = "Enter Burrow" ENTER_BURROW.id = "ENTER_BURROW" ENTER_BURROW.fn = function(act) act.target.sg:GoToState("enter_burrow") end AddAction(ENTER_BURROW) local exit_burrow = State( { name = "exit_burrow", tags = { "busy", "pausepredict", "nomorph", "nodangle" }, onenter = function(inst, cb) inst.components.locomotor:StopMoving() inst.sg.statemem.cb = cb inst.AnimState:SetBank("wilson") if inst:HasTag("FURRY") then inst.AnimState:SetBuild("whisper_furry") else inst.AnimState:SetBuild("whisper") end inst.SoundEmitter:KillSound("move") inst.AnimState:PlayAnimation("jumpout") inst.SoundEmitter:PlaySound("dontstarve/creatures/mandrake/plant_dirt") ChangeToCharacterPhysics(inst) if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() end print ("onenter complete") end, timeline = { TimeEvent(10 * FRAMES, function(inst) if not inst.sg.statemem.heavy then inst.Physics:SetMotorVel(3, 0, 0) end end), TimeEvent(15 * FRAMES, function(inst) if not inst.sg.statemem.heavy then inst.Physics:SetMotorVel(2, 0, 0) end end), TimeEvent(15.2 * FRAMES, function(inst) if not inst.sg.statemem.heavy then if inst.sg.statemem.isphysicstoggle then ToggleOnPhysics(inst) end inst.SoundEmitter:PlaySound("dontstarve/movement/bodyfall_dirt") end end), TimeEvent(17 * FRAMES, function(inst) inst.Physics:SetMotorVel(inst.sg.statemem.heavy and .5 or 1, 0, 0) end), TimeEvent(18 * FRAMES, function(inst) inst.Physics:Stop() end), print ("timeline complete") }, events = { EventHandler("animqueueover", function(inst) if inst.AnimState:AnimDone() then inst:SetStateGraph("SGwilson") inst.sg:GoToState("idle") end end), }, onexit = function(inst) inst:RemoveTag("underground") inst:AddTag("aboveground") inst.CanExamine = nil inst.components.locomotor.fasteronroad = true --print("aboveground") if inst.sg.statemem.cb ~= nil then -- in case of interruption inst.sg.statemem.cb() inst.sg.statemem.cb = nil end if inst.components.playercontroller ~= nil then inst.components.playercontroller:EnableMapControls(true) inst.components.playercontroller:Enable(true) end inst.components.inventory:Show() inst:ShowActions(true) print ("onexit complete") end, } ) AddStategraphState("burrow", exit_burrow) local EXIT_BURROW = GLOBAL.Action() EXIT_BURROW.str = "Exit Burrow" EXIT_BURROW.id = "EXIT_BURROW" EXIT_BURROW.fn = function(act) act.target.sg:GoToState("exit_burrow") end AddAction(EXIT_BURROW) Spoiler require("stategraphs/commonstates") local WALK_SPEED = 4 local RUN_SPEED = 7 local events= { EventHandler("death", function(inst) inst.sg:GoToState("death") end), --CommonHandlers.OnLocomote(true,false), EventHandler("locomote", function(inst, data) if inst.sg:HasStateTag("busy") or inst:HasTag("busy") then return end local is_moving = inst.sg:HasStateTag("moving") local should_move = inst.components.locomotor:WantsToMoveForward() if inst.sg:HasStateTag("bedroll") or inst.sg:HasStateTag("tent") or inst.sg:HasStateTag("waking") then -- wakeup on locomote if inst.sleepingbag ~= nil and inst.sg:HasStateTag("sleeping") then inst.sleepingbag.components.sleepingbag:DoWakeUp() inst.sleepingbag = nil end elseif is_moving and not should_move then inst.sg:GoToState("run_stop") elseif not is_moving and should_move then inst.sg:GoToState("run_start") elseif data.force_idle_state and not (is_moving or should_move or inst.sg:HasStateTag("idle")) then inst.sg:GoToState("idle") end end), } local function SpawnMoveFx(inst) SpawnPrefab("mole_move_fx").Transform:SetPosition(inst.Transform:GetWorldPosition()) end local states= { State{ name = "death", tags = {"busy", "pausepredict", "nomorph" }, onenter = function(inst) assert(inst.deathcause ~= nil, "Entered death state without cause.") ClearStatusAilments(inst) inst.components.locomotor:Stop() inst.components.locomotor:Clear() inst:ClearBufferedAction() inst.SoundEmitter:KillSound("move") inst.AnimState:SetBank("wilson") if inst:HasTag("FURRY") then inst.AnimState:SetBuild("whisper_furry") else inst.AnimState:SetBuild("whisper") end inst.SoundEmitter:KillSound("move") inst:SetStateGraph("SGwilson") inst.SoundEmitter:PlaySound("dontstarve/wilson/death") inst.AnimState:Hide("swap_arm_carry") inst.AnimState:PlayAnimation("death") inst.Physics:Stop() RemovePhysicsColliders(inst) if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() inst.components.playercontroller:Enable(false) end --Don't process other queued events if we died this frame inst.sg:ClearBufferedEvents() end, events = { EventHandler("animover", function(inst) if inst.AnimState:AnimDone() then inst:PushEvent(inst.ghostenabled and "makeplayerghost" or "playerdied", { skeleton = true }) end end), }, }, State { name = "idle", tags = { "idle", "canrotate", "noattack" }, onenter = function(inst, playanim) inst.Physics:Stop() inst.SoundEmitter:KillSound("move") if inst.isunder then inst.sg:AddStateTag("noattack") end if playanim then inst.AnimState:PlayAnimation(playanim) inst.AnimState:PushAnimation("idle_under", true) else inst.AnimState:PlayAnimation("idle_under", true) end end, }, State { name = "run_start", tags = { "moving", "running", "canrotate", "noattack", "autopredict"}, -- "invisible" onenter = function(inst) inst:SetUnderPhysics() inst.AnimState:PlayAnimation("walk_pre") if not inst.SoundEmitter:PlayingSound("move") then inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/move", "move") end inst.components.locomotor:WalkForward() end, events = { EventHandler("animover", function(inst) inst.sg:GoToState("run") end), } }, State { name = "run", tags = { "moving", "canrotate", "noattack", "autopredict"}, -- "invisible" onenter = function(inst) inst:SetUnderPhysics() inst.AnimState:PlayAnimation("walk_loop") inst.components.locomotor:WalkForward() end, timeline = { TimeEvent(0*FRAMES, SpawnMoveFx), TimeEvent(5*FRAMES, SpawnMoveFx), TimeEvent(10*FRAMES, SpawnMoveFx), TimeEvent(15*FRAMES, SpawnMoveFx), TimeEvent(20*FRAMES, SpawnMoveFx), TimeEvent(25*FRAMES, SpawnMoveFx), }, events = { EventHandler("animover", function(inst) inst.sg:GoToState("run") end), } }, State { name = "run_stop", tags = { "canrotate", "noattack", "idle", "autopredict"}, -- "invisible" onenter = function(inst) inst:SetUnderPhysics() inst.components.locomotor:StopMoving() inst.AnimState:PlayAnimation("walk_pst") inst.SoundEmitter:KillSound("move") end, events = { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end), }, } } return StateGraph("burrow", states, events, "idle") So I've updated it to happen as a State now instead, but once again I'm running into a couple problems. So enter burrow state works fine client and server, but server side moving with the character is off, it seems the character moves faster then the actual burrowing animation and you get some rubberbanding. The second thing is the exit burrow state sort of works client side, the animation plays but the character doesn't return back to normal, just continues burrowing. The exit burrow was added to the SGmolep stategraph so it could be used, but I feel like that might be what's causing the issue. I resolved the second issue I was having and edited the spoilers to reflect the updates. Any ideas on what I'm doing wrong? Edited July 28, 2017 by RedHairedHero Link to comment Share on other sites More sharing options...
ZupaleX Posted July 28, 2017 Share Posted July 28, 2017 I am unsure about what you are doing with the RPC. Instead of trying to hack the core RPC, why don't you use the functions planned for the modders? AddModRPCHandler and SendModRPCToServer What you describing sounds like there is an issue at some point with the communication between the host and the client. So if the host has different info compared to the client regarding the speed it will create what you are describing. There might be something else going on here but first I would fix the way you are using RPCs. Link to comment Share on other sites More sharing options...
RedHairedHero Posted July 28, 2017 Author Share Posted July 28, 2017 (edited) Spoiler PrefabFiles = { "whisper", "whisper_none", } Assets = { Asset( "IMAGE", "images/saveslot_portraits/whisper.tex" ), Asset( "ATLAS", "images/saveslot_portraits/whisper.xml" ), Asset( "IMAGE", "images/selectscreen_portraits/whisper.tex" ), Asset( "ATLAS", "images/selectscreen_portraits/whisper.xml" ), Asset( "IMAGE", "images/selectscreen_portraits/whisper_silho.tex" ), Asset( "ATLAS", "images/selectscreen_portraits/whisper_silho.xml" ), Asset( "IMAGE", "bigportraits/whisper.tex" ), Asset( "ATLAS", "bigportraits/whisper.xml" ), Asset( "IMAGE", "images/map_icons/whisper.tex" ), Asset( "ATLAS", "images/map_icons/whisper.xml" ), Asset( "IMAGE", "images/avatars/avatar_whisper.tex" ), Asset( "ATLAS", "images/avatars/avatar_whisper.xml" ), Asset( "IMAGE", "images/avatars/avatar_ghost_whisper.tex" ), Asset( "ATLAS", "images/avatars/avatar_ghost_whisper.xml" ), Asset( "IMAGE", "images/avatars/self_inspect_whisper.tex" ), Asset( "ATLAS", "images/avatars/self_inspect_whisper.xml" ), Asset( "IMAGE", "images/names_whisper.tex" ), Asset( "ATLAS", "images/names_whisper.xml" ), Asset( "IMAGE", "bigportraits/whisper_none.tex" ), Asset( "ATLAS", "bigportraits/whisper_none.xml" ), Asset( "ANIM", "anim/whisper.zip" ), Asset( "ANIM", "anim/whisper_furry.zip" ), Asset( "ANIM", "anim/whisper_dark.zip" ), Asset( "ANIM", "anim/whisper_dark_furry.zip" ), Asset("SCRIPT", "scripts/prefabs/whisper.lua"), } local require = GLOBAL.require local STRINGS = GLOBAL.STRINGS modimport("libs/env.lua") --active key -- Import the lib use. modimport("libs/use.lua") -- Import the mod environment as our environment. use "libs/mod_env"(env) -- Imports to keep the keyhandler from working while typing in chat. use "data/widgets/controls" use "data/screens/chatinputscreen" use "data/screens/consolescreen" -- The character select screen lines STRINGS.CHARACTER_TITLES.whisper = "The Bunny" STRINGS.CHARACTER_NAMES.whisper = "Whisper" STRINGS.CHARACTER_DESCRIPTIONS.whisper = "*Has a dark side \n*Is a Bunny \n*Is a vegetarian" STRINGS.CHARACTER_QUOTES.whisper = "\"Embrace the dark side.\"" -- Custom speech strings STRINGS.CHARACTERS.WHISPER = require "speech_whisper" -- The character's name as appears in-game STRINGS.NAMES.WHISPER = "Whisper" AddMinimapAtlas("images/map_icons/whisper.xml") -- Add mod character to mod character list. Also specify a gender. Possible genders are MALE, FEMALE, ROBOT, NEUTRAL, and PLURAL. AddModCharacter("whisper", "FEMALE") local function IsDefaultScreen() if GLOBAL.TheFrontEnd:GetActiveScreen() and GLOBAL.TheFrontEnd:GetActiveScreen().name and type(GLOBAL.TheFrontEnd:GetActiveScreen().name) == "string" and GLOBAL.TheFrontEnd:GetActiveScreen().name == "HUD" then return true else return false end end local function Transform(player) if not player:HasTag("DARK") and not player:HasTag("burrow") then player:AddTag("DARK") player:AddTag("scarytoprey") player.SoundEmitter:PlaySound("dontstarve/creatures/werepig/transformToPig") player.sg:GoToState("transforming") player.components.sanity:DoDelta(-10) else if player:HasTag("DARK") then player:RemoveTag("DARK") player:RemoveTag("scarytoprey") player.SoundEmitter:PlaySound("dontstarve/creatures/werepig/transformToPig") player.sg:GoToState("transforming") player.components.sanity:DoDelta(-10) end end end local function Burrow(player) if not player:HasTag("burrow") and not player:HasTag("DARK") then player:AddTag("burrow") player.components.grue:AddImmunity("invincible") player.components.hunger:DoDelta(-10) player.components.hunger:SetRate(3 * TUNING.WILSON_HUNGER_RATE) player.sg:GoToState("enter_burrow") else if player:HasTag("burrow") then player:RemoveTag("burrow") player.components.hunger:SetRate(1 * TUNING.WILSON_HUNGER_RATE) player.sg:GoToState("exit_burrow") player.components.grue:RemoveImmunity("invincible") end end end AddModRPCHandler(modname, "Transform", Transform) AddModRPCHandler(modname, "Burrow", Burrow) local function SendTransformRPC() SendModRPCToServer(MOD_RPC[modname]["Transform"]) end local function SendBurrowRPC() SendModRPCToServer(MOD_RPC[modname]["Burrow"]) end GLOBAL.TheInput:AddKeyDownHandler(116, function() local player = GLOBAL.ThePlayer if player and not player.HUD:IsChatInputScreenOpen() and not player.HUD:IsConsoleScreenOpen() and IsDefaultScreen() then SendModRPCToServer(MOD_RPC[modname]["Transform"]) end end ) GLOBAL.TheInput:AddKeyDownHandler(98, function() local player = GLOBAL.ThePlayer if player and not player.HUD:IsChatInputScreenOpen() and not player.HUD:IsConsoleScreenOpen() and IsDefaultScreen() then SendModRPCToServer(MOD_RPC[modname]["Burrow"]) end end ) local transform = State( { name = "transforming", tags = { "busy", "pausepredict", "nomorph", "nodangle" }, onenter = function(inst, cb) if inst.components.inventory:IsHeavyLifting() then inst.components.inventory:DropItem( inst.components.inventory:Unequip(EQUIPSLOTS.BODY), true, true ) end inst.components.locomotor:StopMoving() inst.sg.statemem.cb = cb inst.AnimState:OverrideSymbol("shadow_hands", "shadow_skinchangefx", "shadow_hands") inst.AnimState:OverrideSymbol("shadow_ball", "shadow_skinchangefx", "shadow_ball") inst.AnimState:OverrideSymbol("splode", "shadow_skinchangefx", "splode") inst.AnimState:PlayAnimation("skin_change", false)--true loops if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() end end, timeline = { TimeEvent(62 * FRAMES, function(inst) if inst.sg.statemem.cb ~= nil then inst.sg.statemem.cb() inst.sg.statemem.cb = nil end end), }, events = { EventHandler("animqueueover", function(inst) if inst.AnimState:AnimDone() then inst.sg:GoToState("idle") end end), }, onexit = function(inst) if inst:HasTag("FURRY") and inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark_furry") else if not inst:HasTag("FURRY") and inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_dark") else if inst:HasTag("FURRY") and not inst:HasTag("DARK") then inst.AnimState:SetBuild("whisper_furry") else inst.AnimState:SetBuild("whisper") end end end if inst:HasTag("DARK") then inst.components.combat.damagemultiplier = 1.25 inst.components.locomotor:SetExternalSpeedMultiplier(inst, "whisper_speed_mod", 1 ) else inst.components.combat.damagemultiplier = 1.0 inst.components.locomotor:SetExternalSpeedMultiplier(inst, "whisper_speed_mod", 1 + .25) end if inst.sg.statemem.cb ~= nil then -- in case of interruption inst.sg.statemem.cb() inst.sg.statemem.cb = nil end inst.AnimState:OverrideSymbol("shadow_hands", "shadow_hands", "shadow_hands") if inst.components.playercontroller ~= nil then inst.components.playercontroller:EnableMapControls(true) inst.components.playercontroller:Enable(true) end inst:ShowActions(true) end, } ) AddStategraphState("wilson", transform) local enter_burrow = State( { name = "enter_burrow", tags = { "busy", "pausepredict", "nomorph", "nodangle" }, onenter = function(inst, cb) if inst.components.inventory:IsHeavyLifting() then inst.components.inventory:DropItem( inst.components.inventory:Unequip(EQUIPSLOTS.BODY), true, true ) end inst.components.locomotor:StopMoving() inst.sg.statemem.cb = cb inst.AnimState:PlayAnimation("jump", false) inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/jump") if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() end end, timeline = { TimeEvent(15 * FRAMES, function(inst) inst.Physics:Stop() end), }, events = { EventHandler("animqueueover", function(inst) if inst.AnimState:AnimDone() then inst.AnimState:SetBank("mole") inst.AnimState:SetBuild("mole_build") inst:SetStateGraph("SGburrow") inst.sg:GoToState("idle") end end), }, onexit = function(inst) inst.CanExamine = CannotExamine if inst.sg.statemem.cb ~= nil then -- in case of interruption inst.sg.statemem.cb() inst.sg.statemem.cb = nil end if inst.components.playercontroller ~= nil then inst.components.playercontroller:EnableMapControls(true) inst.components.playercontroller:Enable(true) end inst:ShowActions(false) end, } ) AddStategraphState("wilson", enter_burrow) local exit_burrow = State( { name = "exit_burrow", tags = { "busy", "pausepredict", "nomorph", "nodangle" }, onenter = function(inst, cb) inst.components.locomotor:StopMoving() inst.sg.statemem.cb = cb inst.AnimState:SetBank("wilson") if inst:HasTag("FURRY") then inst.AnimState:SetBuild("whisper_furry") else inst.AnimState:SetBuild("whisper") end inst.SoundEmitter:KillSound("move") inst.AnimState:PlayAnimation("jumpout") inst.SoundEmitter:PlaySound("dontstarve/creatures/mandrake/plant_dirt") ChangeToCharacterPhysics(inst) if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() end end, timeline = { TimeEvent(10 * FRAMES, function(inst) if not inst.sg.statemem.heavy then inst.Physics:SetMotorVel(3, 0, 0) end end), TimeEvent(15 * FRAMES, function(inst) if not inst.sg.statemem.heavy then inst.Physics:SetMotorVel(2, 0, 0) end end), TimeEvent(15.2 * FRAMES, function(inst) if not inst.sg.statemem.heavy then if inst.sg.statemem.isphysicstoggle then ToggleOnPhysics(inst) end inst.SoundEmitter:PlaySound("dontstarve/movement/bodyfall_dirt") end end), TimeEvent(17 * FRAMES, function(inst) inst.Physics:SetMotorVel(inst.sg.statemem.heavy and .5 or 1, 0, 0) end), TimeEvent(18 * FRAMES, function(inst) inst.Physics:Stop() end), }, events = { EventHandler("animqueueover", function(inst) if inst.AnimState:AnimDone() then inst:SetStateGraph("SGwilson") inst.sg:GoToState("idle") end end), }, onexit = function(inst) inst.CanExamine = nil if inst.sg.statemem.cb ~= nil then -- in case of interruption inst.sg.statemem.cb() inst.sg.statemem.cb = nil end if inst.components.playercontroller ~= nil then inst.components.playercontroller:EnableMapControls(true) inst.components.playercontroller:Enable(true) end inst:ShowActions(true) end, } ) AddStategraphState("burrow", exit_burrow) Spoiler local MakePlayerCharacter = require "prefabs/player_common" local assets = { Asset("SCRIPT", "scripts/prefabs/player_common.lua"), Asset( "ANIM", "anim/whisper.zip" ), Asset( "ANIM", "anim/whisper_furry.zip" ), Asset( "ANIM", "anim/whisper_dark.zip" ), Asset( "ANIM", "anim/whisper_dark_furry.zip" ), } local prefabs = { } local start_inv = { "carrot", "carrot", "carrot", "carrot", } local function onbecamehuman(inst) -- Set speed when reviving from ghost (optional) inst.components.locomotor:SetExternalSpeedMultiplier(inst, "whisper_speed_mod", 1 + .25 ) end local function onbecameghost(inst) inst.CanExamine = nil inst.components.locomotor:RemoveExternalSpeedMultiplier(inst, "whisper_speed_mod") end local function onload(inst) inst:ListenForEvent("ms_respawnedfromghost", onbecamehuman) inst:ListenForEvent("ms_becameghost", onbecameghost) inst.components.hunger:SetRate(1 * TUNING.WILSON_HUNGER_RATE) if inst:HasTag("playerghost") then onbecameghost(inst) else onbecamehuman(inst) end end local function oneat(inst, food) local extrahunger = 0 if food.prefab == "carrot" or food.prefab == "carrot_cooked" then if food.components.perishable:IsStale() then extrahunger = 4 else if food.components.perishable:IsSpoiled() then extrahunger = 2 else --if it's fresh extrahunger = 6 end end end if food and food.components.edible and food.prefab == "carrot" then inst.components.hunger:DoDelta(extrahunger) end if food and food.components.edible and food.prefab == "carrot_cooked" then inst.components.hunger:DoDelta(extrahunger) end end local function CannotExamine(inst) return false end local function SetUnderPhysics(inst) if inst.isunder ~= true then inst.isunder = true inst.Physics:SetCollisionGroup(COLLISION.CHARACTERS) inst.Physics:ClearCollisionMask() inst.Physics:CollidesWith(COLLISION.WORLD) inst.Physics:CollidesWith(COLLISION.OBSTACLES) end end local function SetAbovePhysics(inst) if inst.isunder ~= false then inst.isunder = false ChangeToCharacterPhysics(inst) end end local function sanityfn(inst) local delta = 0 if TheWorld.state.isday then delta = TUNING.SANITY_NIGHT_MID end return delta end -- This initializes for both the server and client. Tags can be added here. local common_postinit = function(inst) inst.MiniMapEntity:SetIcon( "whisper.tex" ) inst:RemoveTag("scarytoprey") inst:AddTag("mole") inst.SetUnderPhysics = SetUnderPhysics inst.SetAbovePhysics = SetAbovePhysics MakeCharacterPhysics(inst, 99999, 0.5) inst.Physics:SetMass(99999) SetUnderPhysics(inst) end -- This initializes for the server only. Components are added here. local master_postinit = function(inst) inst.soundsname = "willow" inst.components.sanity.custom_rate_fn = sanityfn inst.components.eater:SetDiet({ FOODTYPE.VEGGIE }, { FOODTYPE.VEGGIE }) inst.components.sanity.night_drain_mult = 0 inst.components.eater:SetOnEatFn(oneat) inst.components.health:SetMaxHealth(200) inst.components.hunger:SetMax(150) inst.components.sanity:SetMax(150) inst.components.health:SetAbsorptionAmount(-0.25) inst.components.combat.damagemultiplier = 0.75 inst.OnLoad = onload inst.OnNewSpawn = onload return inst end return MakePlayerCharacter("whisper", prefabs, assets, common_postinit, master_postinit, start_inv) Spoiler require("stategraphs/commonstates") local WALK_SPEED = 4 local RUN_SPEED = 7 local events= { EventHandler("death", function(inst) inst.sg:GoToState("death") end), EventHandler("locomote", function(inst, data) if inst.sg:HasStateTag("busy") or inst:HasTag("busy") then return end local is_moving = inst.sg:HasStateTag("moving") local should_move = inst.components.locomotor:WantsToMoveForward() if is_moving and not should_move then inst.sg:GoToState("run_stop") elseif not is_moving and should_move then inst.sg:GoToState("run_start") elseif data.force_idle_state and not (is_moving or should_move or inst.sg:HasStateTag("idle")) then inst.sg:GoToState("idle") end end), } local function SpawnMoveFx(inst) SpawnPrefab("mole_move_fx").Transform:SetPosition(inst.Transform:GetWorldPosition()) end local states= { State{ name = "death", tags = {"busy", "pausepredict", "nomorph" }, onenter = function(inst) assert(inst.deathcause ~= nil, "Entered death state without cause.") inst.components.locomotor:Stop() inst.components.locomotor:Clear() inst:ClearBufferedAction() inst.SoundEmitter:KillSound("move") inst.AnimState:SetBank("wilson") if inst:HasTag("FURRY") then inst.AnimState:SetBuild("whisper_furry") else inst.AnimState:SetBuild("whisper") end inst.SoundEmitter:KillSound("move") inst:SetStateGraph("SGwilson") inst.sg:GoToState("death") inst.Physics:Stop() RemovePhysicsColliders(inst) if inst.components.playercontroller ~= nil then inst.components.playercontroller:RemotePausePrediction() inst.components.playercontroller:Enable(false) end --Don't process other queued events if we died this frame inst.sg:ClearBufferedEvents() end, events = { EventHandler("animover", function(inst) if inst.AnimState:AnimDone() then inst:PushEvent(inst.ghostenabled and "makeplayerghost" or "playerdied", { skeleton = true }) end end), }, }, State { name = "idle", tags = { "idle", "canrotate", "noattack" }, onenter = function(inst, pushanim) --inst.entity:SetIsPredictingMovement(false) inst.Physics:Stop() inst.SoundEmitter:KillSound("move") if pushanim then inst.AnimState:PlayAnimation(pushanim) inst.AnimState:PushAnimation("idle_under", true) else inst.AnimState:PlayAnimation("idle_under", true) end end, onexit = function(inst) inst.entity:SetIsPredictingMovement(true) end, }, State { name = "run_start", tags = { "moving", "canrotate", "noattack", "invisible" }, onenter = function(inst) inst:SetUnderPhysics() inst.components.locomotor:WalkForward() inst.AnimState:PlayAnimation("walk_pre") if not inst.SoundEmitter:PlayingSound("move") then inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/mole/move", "move") end end, onupdate = function(inst) inst.components.locomotor:WalkForward() end, events = { EventHandler("animover", function(inst) if inst.AnimState:AnimDone() then inst.sg:GoToState("run") end end), } }, State { name = "run", tags = { "moving", "running", "canrotate", "noattack" }, onenter = function(inst) inst:SetUnderPhysics() inst.components.locomotor:WalkForward() inst.AnimState:PlayAnimation("walk_loop", true) end, onupdate = function(inst) inst.components.locomotor:WalkForward() end, timeline = { TimeEvent(0*FRAMES, SpawnMoveFx), TimeEvent(5*FRAMES, SpawnMoveFx), TimeEvent(10*FRAMES, SpawnMoveFx), TimeEvent(15*FRAMES, SpawnMoveFx), TimeEvent(20*FRAMES, SpawnMoveFx), TimeEvent(25*FRAMES, SpawnMoveFx), }, events = { EventHandler("animover", function(inst) inst.sg:GoToState("run") end), }, ontimeout = function(inst) inst.sg:GoToState("run") end, }, State { name = "run_stop", tags = { "canrotate", "noattack", "invisible" }, onenter = function(inst) inst:SetUnderPhysics() inst.components.locomotor:StopMoving() inst.AnimState:PlayAnimation("walk_pst") inst.SoundEmitter:KillSound("move") end, events = { EventHandler("animover", function(inst) if inst.AnimState:AnimDone() then inst.sg:GoToState("idle") end end), }, } } return StateGraph("burrow", states, events, "idle") I've updated the code and spoilers with the Mod RPC recommendation. So looking a bit further into the client side there's a few things I'm noticing. The first was the issue I mentioned earlier where the character is faster then the burrow effects and rubberbands. The second issue I'm seeing is that it appears the animations from the stategraph aren't playing while in client. "idle_under", "walk_pst", "walk_pre", and "walk_loop". Update: It seems the issue occurs if the player has Lag Compensation turned on in the settings, turning it off fixed both the speed and animation. Edited July 28, 2017 by RedHairedHero 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