FleurDuSoleil3 Posted March 5, 2018 Share Posted March 5, 2018 (edited) Credit goes to the Bigby Wolf mod for the health threshold activator; however, there are other options available to you. I will preface this by saying I'm not a very good coder; most of this was discovered by asking for help or digging through the game files. With that said, here we go. It will also be needlessly prolix. What we'll be achieving here is having you make your character become any other mob in the game via a transformation that can be triggered by health, the full moon, or by eating an item. For custom-made mobs, I don't know. The principles probably remain the same. For creatures without any action handlers in their stategraphs, I don't know. This is an amateur hour tutorial. I simply don't have the knowledge as of yet as I have not needed it. Firstly, you'll need to post the following code in your character's prefab file: local function BecomeYour_Custom_Character(inst) inst.your_custom_werewolf = false inst.AnimState:SetBank("wilson") inst.AnimState:SetBuild("your_custom_character") inst:SetStateGraph("SGwilson") end local function BecomeYour_Custom_Werewolf(inst) inst.your_custom_werewolf = true inst.AnimState:SetBank("your_custom_werewolf's_anim_bank") ----- You can find the bank of whatever creature you are using in its prefab. inst.AnimState:SetBuild("your_custom_werewolf's_anim_build") ---- You can find the animation for x's _build in your creature's prefab. inst:SetStateGraph("SGyour_custom_werewolf") ------- See stategraph below end So, let us take a look at becoming a Bunnyman to see an example of this. local function BecomeRabbitman(inst) inst.wererabbit = false inst.AnimState:SetBank("wilson") inst.AnimState:SetBuild("rabbitman") inst:SetStateGraph("SGwilson") end local function Become_Wererabbit(inst) inst.wererabbit = true inst.AnimState:SetBank("manrabbit") inst.AnimState:SetBuild("manrabbit_build") inst:SetStateGraph("SGwererabbit") end Now we need to make sure the game knows when to start this transformation. Health-based transformation: Spoiler local becomeformtreshold = 60 --- when health hits 60, we transform local unbecomeformtreshold = 62 --- when health reaches 62, we transform back local function health_event_listener(inst, data) if inst.components.health.currenthealth <= becomeformtreshold and not inst.your_custom_werewolf then becomeyour_custom_werewolf(inst) elseif inst.components.health.currenthealth >= unbecomeformtreshold and inst.your_custom_werewolf then becomeyour_custom_character(inst) end end inst:ListenForEvent("healthdelta", health_event_listener) Moon-based transformation: inst:ListenForEvent("nighttime", function(global, data) if GetClock():GetMoonPhase() == "full" and not inst.your_custom_werewolf then becomeyour_custom_werewolf(inst) end end, GetWorld()) inst:ListenForEvent("daytime", function(global, data) if GetClock():IsDay() and inst.your_custom_werewolf then becomeyour_custom_character(inst) end end, GetWorld()) Food-based transformation: inst:ListenForEvent("oneat", function(inst, data) if data.food.prefab == "whatever_you_want" and not inst.your_custom_werewolf then becomeyour_custom_werewolf(inst) --- turn into your werewolf when you eat a carrot or something elseif data.food.prefab == "whatever_you_want" and inst.your_custom_werewolf then becomeyour_custom_character(inst) --- turn back into your character when you eat another one end end) Stategraph: You are going to need to find whatever creature you are using's stategraph (located in the game's data folders). You will need to replicate it and rename it, then create your own stategraph folder in your mod character's scripts folder and put your new renamed stategraph in there. So the path will be YourMod/scripts/stategraphs/SGyour_custom_werewolf The stategraph will need some editing. On a basic level, such as turning into a bunnyman or pig, you will need to remove any instance of the term 'follower' from the stategraph (more often than not by just getting rid of the entire line it is contained in). This will prevent crashes if you equip an item such as a hat. For most creatures, such as the Deerclops, death will cause a crash. I don't know why because I'm an untalented hack, but I removed the following line from the Deerclops and it stopped crashing: inst.components.lootdropper:DropLoot(Vector3(inst.Transform:GetWorldPosition())) You can also add in actions (you can find a list of actions in the game's data folder under 'actions.lua'). For instance, you may want to add in the action PICK and PICKUP using one of the creature's existing animations to avoid any crashes when you attempt to pick things up. Here is an example of making a bunnyman able to pick objects such as grass and flowers: local actionhandlers = { ActionHandler(ACTIONS.GOHOME, "gohome"), ActionHandler(ACTIONS.EAT, "eat"), ActionHandler(ACTIONS.PICKUP, "pickup"), ----- The bunnyman can already pick up items ActionHandler(ACTIONS.EQUIP, "pickup"), ActionHandler(ACTIONS.ADDFUEL, "pickup"), ActionHandler(ACTIONS.PICK, "pickup"), ---------- So we added in the PICK action using the bunnyman's "pickup" animation } Here is a demonstration using the Bunnyman. Bunnyman's stategraph before we use it: Spoiler require("stategraphs/commonstates") local actionhandlers = { ActionHandler(ACTIONS.GOHOME, "gohome"), ActionHandler(ACTIONS.EAT, "eat"), ActionHandler(ACTIONS.PICKUP, "pickup"), ActionHandler(ACTIONS.EQUIP, "pickup"), ActionHandler(ACTIONS.ADDFUEL, "pickup"), } local events= { CommonHandlers.OnStep(), CommonHandlers.OnLocomote(true,true), CommonHandlers.OnSleep(), CommonHandlers.OnFreeze(), CommonHandlers.OnAttack(), CommonHandlers.OnAttacked(true), CommonHandlers.OnDeath(), } local function beardit(inst, anim) return inst.beardlord and "beard_"..anim or anim end local states= { State{ name= "funnyidle", tags = {"busy"}, onenter = function(inst) inst.Physics:Stop() if inst.beardlord then inst.AnimState:PlayAnimation("beard_taunt") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/wererabbit_taunt") elseif inst.components.health:GetPercent() < TUNING.BUNNYMAN_PANIC_THRESH then inst.AnimState:PlayAnimation("idle_angry") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/angry_idle") elseif inst.components.follower.leader and inst.components.follower:GetLoyaltyPercent() < 0.05 then inst.AnimState:PlayAnimation("hungry") inst.SoundEmitter:PlaySound("dontstarve/wilson/hungry") elseif inst.components.combat.target then inst.AnimState:PlayAnimation("idle_angry") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/angry_idle") elseif inst.components.follower.leader and inst.components.follower:GetLoyaltyPercent() > 0.3 then inst.AnimState:PlayAnimation("idle_happy") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/happy") else inst.AnimState:PlayAnimation("idle_creepy") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/idle_med") end end, events= { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end ), }, }, State{ name= "happy", tags = {"busy"}, onenter = function(inst) inst.Physics:Stop() inst.AnimState:PlayAnimation("idle_happy") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/happy") end, events= { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end ), }, }, State { name = "frozen", tags = {"busy"}, onenter = function(inst) inst.AnimState:PlayAnimation("frozen") inst.Physics:Stop() --inst.components.highlight:SetAddColour(Vector3(82/255, 115/255, 124/255)) end, }, State{ name = "death", tags = {"busy"}, onenter = function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/death") inst.AnimState:PlayAnimation("death") inst.Physics:Stop() RemovePhysicsColliders(inst) inst.components.lootdropper:DropLoot(Vector3(inst.Transform:GetWorldPosition())) end, }, State{ name = "abandon", tags = {"busy"}, onenter = function(inst, leader) inst.Physics:Stop() inst.AnimState:PlayAnimation("abandon") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/angry_idle") inst:FacePoint(Vector3(leader.Transform:GetWorldPosition())) end, events = { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end ), }, }, State{ name = "attack", tags = {"attack", "busy"}, onenter = function(inst) if inst.beardlord then inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/wererabbit_attack") else inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/attack") end inst.components.combat:StartAttack() inst.Physics:Stop() inst.AnimState:PlayAnimation(beardit(inst,"atk")) end, timeline= { TimeEvent(13*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/bite") inst.components.combat:DoAttack() inst.sg:RemoveStateTag("attack") inst.sg:RemoveStateTag("busy") end), }, events= { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end), }, }, State{ name = "eat", tags = {"busy"}, onenter = function(inst) inst.Physics:Stop() inst.AnimState:PlayAnimation("eat") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/eat") end, timeline= { TimeEvent(20*FRAMES, function(inst) inst:PerformBufferedAction() end), }, events= { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end ), }, }, State{ name = "hit", tags = {"busy"}, onenter = function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hurt") inst.AnimState:PlayAnimation("hit") inst.Physics:Stop() end, events= { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end ), }, }, } CommonStates.AddWalkStates(states, { walktimeline = { TimeEvent(0*FRAMES, PlayFootstep ), TimeEvent(0*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hop") end), TimeEvent(12*FRAMES, PlayFootstep ), TimeEvent(12*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hop") end), }, }, { startwalk = function(inst) return beardit(inst,"walk_pre") end, walk = function(inst) return beardit(inst,"walk_loop") end, stopwalk = function(inst) return beardit(inst,"walk_pst") end, }, function(inst) return not inst.beardlord end ) CommonStates.AddRunStates(states, { runtimeline = { TimeEvent(0*FRAMES, PlayFootstep ), TimeEvent(0*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hop") end), TimeEvent(10*FRAMES, PlayFootstep ), TimeEvent(10*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hop") end), }, }, { startrun = function(inst) return beardit(inst,"run_pre") end, run = function(inst) return beardit(inst,"run_loop") end, stoprun = function(inst) return beardit(inst,"run_pst") end, }, function(inst) return not inst.beardlord end ) CommonStates.AddSleepStates(states, { sleeptimeline = { TimeEvent(35*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/sleep") end ), }, }) CommonStates.AddIdle(states,"funnyidle", function(inst) return beardit(inst,"idle_loop") end, { TimeEvent(0*FRAMES, function(inst) if inst.beardlord then inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/wererabbit_breathin") end end ), TimeEvent(15*FRAMES, function(inst) if inst.beardlord then inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/wererabbit_idle") end end ), }) CommonStates.AddSimpleState(states,"refuse", "pig_reject", {"busy"}) CommonStates.AddFrozenStates(states) CommonStates.AddSimpleActionState(states,"pickup", "pig_pickup", 10*FRAMES, {"busy"}) CommonStates.AddSimpleActionState(states, "gohome", "pig_pickup", 4*FRAMES, {"busy"}) return StateGraph("pig", states, events, "idle", actionhandlers) And after: Spoiler require("stategraphs/commonstates") local actionhandlers = { ActionHandler(ACTIONS.GOHOME, "gohome"), ActionHandler(ACTIONS.EAT, "eat"), ActionHandler(ACTIONS.PICKUP, "pickup"), ActionHandler(ACTIONS.EQUIP, "pickup"), ActionHandler(ACTIONS.ADDFUEL, "pickup"), ActionHandler(ACTIONS.PICK, "pickup"), } local events= { CommonHandlers.OnStep(), CommonHandlers.OnLocomote(true,true), CommonHandlers.OnSleep(), CommonHandlers.OnFreeze(), CommonHandlers.OnAttack(), CommonHandlers.OnAttacked(true), CommonHandlers.OnDeath(), } local function beardit(inst, anim) return inst.beardlord and "beard_"..anim or anim end local states= { State{ name= "funnyidle", tags = {"busy"}, onenter = function(inst) inst.Physics:Stop() if inst.beardlord then inst.AnimState:PlayAnimation("beard_taunt") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/wererabbit_taunt") elseif inst.components.health:GetPercent() < TUNING.BUNNYMAN_PANIC_THRESH then inst.AnimState:PlayAnimation("idle_angry") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/angry_idle") inst.AnimState:PlayAnimation("hungry") inst.SoundEmitter:PlaySound("dontstarve/wilson/hungry") elseif inst.components.combat.target then inst.AnimState:PlayAnimation("idle_angry") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/angry_idle") inst.AnimState:PlayAnimation("idle_happy") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/happy") else inst.AnimState:PlayAnimation("idle_creepy") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/idle_med") end end, events= { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end ), }, }, State { name = "frozen", tags = {"busy"}, onenter = function(inst) inst.AnimState:PlayAnimation("frozen") inst.Physics:Stop() --inst.components.highlight:SetAddColour(Vector3(82/255, 115/255, 124/255)) end, }, State{ name = "death", tags = {"busy"}, onenter = function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/death") inst.AnimState:PlayAnimation("death") inst.Physics:Stop() RemovePhysicsColliders(inst) end, }, State{ name = "abandon", tags = {"busy"}, onenter = function(inst, leader) inst.Physics:Stop() inst.AnimState:PlayAnimation("abandon") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/angry_idle") inst:FacePoint(Vector3(leader.Transform:GetWorldPosition())) end, events = { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end ), }, }, State{ name = "attack", tags = {"attack", "busy"}, onenter = function(inst) if inst.beardlord then inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/wererabbit_attack") else inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/attack") end inst.components.combat:StartAttack() inst.Physics:Stop() inst.AnimState:PlayAnimation(beardit(inst,"atk")) end, timeline= { TimeEvent(13*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/bite") inst.components.combat:DoAttack() inst.sg:RemoveStateTag("attack") inst.sg:RemoveStateTag("busy") end), }, events= { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end), }, }, State{ name = "eat", tags = {"busy"}, onenter = function(inst) inst.Physics:Stop() inst.AnimState:PlayAnimation("eat") inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/eat") end, timeline= { TimeEvent(20*FRAMES, function(inst) inst:PerformBufferedAction() end), }, events= { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end ), }, }, State{ name = "hit", tags = {"busy"}, onenter = function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hurt") inst.AnimState:PlayAnimation("hit") inst.Physics:Stop() end, events= { EventHandler("animover", function(inst) inst.sg:GoToState("idle") end ), }, }, } CommonStates.AddWalkStates(states, { walktimeline = { TimeEvent(0*FRAMES, PlayFootstep ), TimeEvent(0*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hop") end), TimeEvent(12*FRAMES, PlayFootstep ), TimeEvent(12*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hop") end), }, }, { startwalk = function(inst) return beardit(inst,"walk_pre") end, walk = function(inst) return beardit(inst,"walk_loop") end, stopwalk = function(inst) return beardit(inst,"walk_pst") end, }, function(inst) return not inst.beardlord end ) CommonStates.AddRunStates(states, { runtimeline = { TimeEvent(0*FRAMES, PlayFootstep ), TimeEvent(0*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hop") end), TimeEvent(10*FRAMES, PlayFootstep ), TimeEvent(10*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/hop") end), }, }, { startrun = function(inst) return beardit(inst,"run_pre") end, run = function(inst) return beardit(inst,"run_loop") end, stoprun = function(inst) return beardit(inst,"run_pst") end, }, function(inst) return not inst.beardlord end ) CommonStates.AddSleepStates(states, { sleeptimeline = { TimeEvent(35*FRAMES, function(inst) inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/sleep") end ), }, }) CommonStates.AddIdle(states,"funnyidle", function(inst) return beardit(inst,"idle_loop") end, { TimeEvent(0*FRAMES, function(inst) if inst.beardlord then inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/wererabbit_breathin") end end ), TimeEvent(15*FRAMES, function(inst) if inst.beardlord then inst.SoundEmitter:PlaySound("dontstarve/creatures/bunnyman/wererabbit_idle") end end ), }) CommonStates.AddSimpleState(states,"refuse", "pig_reject", {"busy"}) CommonStates.AddFrozenStates(states) CommonStates.AddSimpleActionState(states,"pickup", "pig_pickup", 10*FRAMES, {"busy"}) CommonStates.AddSimpleActionState(states, "gohome", "pig_pickup", 4*FRAMES, {"busy"}) return StateGraph("pig", states, events, "idle", actionhandlers) There is a little bit more you can do with the transformation. Just as with your custom character, you can customise the werecreature: Spoiler local function becomeYour_Custom_Werewolf(inst) inst.your_custom_werewolf = true inst.AnimState:SetBank("your_custom_werewolf's_anim_bank") inst.AnimState:SetBuild("your_custom_werewolf's_anim_build") inst:SetStateGraph("SGyour_custom_werewolf") inst.entity:AddLight() inst.Light:Enable(true) inst.Light:SetRadius(40) inst.Light:SetFalloff(.5) inst.Light:SetIntensity(0.9) inst.Light:SetColour(245/255,255/255,245/255) --- see in the dark, if I recall correctly. You will be safe from Charlie with this method. local health_percent = inst.components.health:GetPercent() inst.components.health:SetMaxHealth(200) inst.components.health:SetPercent(health_percent, true) --- set their health local sanity_percent = inst.components.sanity:GetPercent() inst.components.sanity.max = 100 --- set their sanity inst.components.sanity:SetPercent(sanity_percent, true) inst.components.sanity:DoDelta(-TUNING.SANITY_HUGE) --- do sanity damage upon transformation local hunger_percent = inst.components.hunger:GetPercent() inst.components.hunger:SetMax(wererabbit_hunger) inst.components.hunger:SetPercent(hunger_percent, true) --- set hunger values inst.Transform:SetScale(1.25, 1.25, 1.25) --- set scale of werecreature inst:AddTag("scarytoprey") inst:AddTag("monster") --- you can add tags inst.components.talker.colour = Vector3(.9, .4, .4, 1) --- give the werecreature a special colour font. This one is red STRINGS.CHARACTERS.YOUR_CUSTOM_CHARACTER = require "speech_your_custom_werewolf" --- give the werecreature a custom speech file inst.components.talker.special_speech = false end There is a lot more, but those are just some examples. Don't forget to revert anything you do in the transformation to the werewolf in the transformation back to your character. For instance: Spoiler local function BecomeYour_Custom_Character(inst) inst.your_custom_werewolf = false inst.AnimState:SetBank("wilson") inst.AnimState:SetBuild("your_custom_character") inst:SetStateGraph("SGwilson") inst.components.talker.colour = Vector3( .9, .9, .9, 9) --- revert the font colour back to white. inst.Transform:SetScale(1., 1., 1.) --- set scale back to default end local function BecomeYour_Custom_Werewolf(inst) inst.your_custom_werewolf = true inst.AnimState:SetBank("your_custom_werewolf's_anim_bank") ----- You can find the bank of whatever creature you are using in its prefab. inst.AnimState:SetBuild("your_custom_werewolf's_anim_build") ---- You can find the animation for x's _build in your creature's prefab. inst:SetStateGraph("SGyour_custom_werewolf") ------- See stategraph below inst.components.talker.colour = Vector3(.9, .4, .4, 1) --- give the werecreature a coloured font. This one is red inst.Transform:SetScale(1.25, 1.25, 1.25) --- set size of werecreature end And that should be all. Good luck, everyone! I hope this helps. Edited November 11, 2019 by FleurDuSoleil3 Improving the tutorial 1 Link to comment Share on other sites More sharing options...
Yakuzashi Posted August 20, 2018 Share Posted August 20, 2018 I have followed your tutorial but somehow my character is not able to transform. I have no error message, it just seems that I have made mistake that I don't see. I would really appreciate your help but I feel bad to bother you. Sorry and thank you in advance. Taern TEST.7z Taern TEST.rar Link to comment Share on other sites More sharing options...
FleurDuSoleil3 Posted November 8, 2018 Author Share Posted November 8, 2018 (edited) @Yakuzashi Hi, there. I haven't had too much of an in-depth look at your mod (I haven't loaded it up in the game, basically), but I noticed: In the becometaern function, you have written inst.AnimState:SetBank("taern") It should be inst.AnimState:SetBank("wilson") If this doesn't work, let me know and I'll have a proper fiddle with your mod. Edited November 11, 2019 by FleurDuSoleil3 Misinformation 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