Jump to content

How to make a playable mob?


Recommended Posts

Exactly what the title says. 

I'm now somewhat experienced in making custom character mods (I've created two so far) using the Extended Sample Character mod by Dragon Wolf Leo, with some help from online tutorials and tips from some really cool people on the forums. 

Now, though, I've been attempting to create another character, except it's a mob (a Doydoy, in this case) - similar to the Playable Pets mod by Leonardo Coxington. I want to add my own custom stats and perks to it as to make it stand out from just being a playable Doydoy, which I think I'll be able to do by myself (for the most part), but at the moment I have no idea how to actually get started. Somebody has said that the mod was made by "making the mob's stategraph compatible with player inputs", but I am clueless as to how I can do that. 

Any help is appreciated. Thank you for your time. 

Link to comment
Share on other sites

Its not really that difficult. You can check out PP's code if you want to but:

 

Basically to make a mob playable you just need to set the bank and build when your character spawns (you can do this via masterpost_init or you can make a SetChar function like I did and have masterpostinit call that, doesn't matter) and have a stategraph. Usually you only need to do minor edits, but it depends on the mob. 

First off what you need to do is make the actionhandlers, you'd have to find every action and set that action to a state:

local actionhandlers =
{
	--ActionHandler(ACTIONS.ACTION, "state"), --example

	ActionHandler(ACTIONS.GOHOME, "gohome"),
	ActionHandler(ACTIONS.ATTACK, "peck"),
	ActionHandler(ACTIONS.INVESTIGATE, "action"),	
}

 

You can find the list of actions in the scripts/actions.lua. Note that some actions will need custom states specifically made for those actions like fishing. You will need to explore SGWilson's states and see whats required.

Nextup is Eventhandlers, usually these don't need much changed.

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("death", function(inst) inst.sg:GoToState("death") end),
    EventHandler("doattack", function(inst, data) if not inst.components.health:IsDead() and (inst.sg:HasStateTag("hit") or not inst.sg:HasStateTag("busy")) then inst.sg:GoToState("attack", data.target) end end),
    CommonHandlers.OnSleep(),
    CommonHandlers.OnLocomote(true,false),
    CommonHandlers.OnFreeze(),
	
	 EventHandler("ms_opengift",
        function(inst)
            if not inst.sg:HasStateTag("busy") then
                inst.sg:GoToState("opengift") --you can check out my opengift in any of my mob's stategraph files.
            end
        end),
}

This is a pretty common event table. You can control when you should go to the hitstate, etc. 

Now something to note is that players always RUN, they never walk (unless specific conditions happen, or you don't use CommonHandlers.OnLocomote). So you will need to make a run_start, run, and run_pst state. A lot of mobs already have these states so you can look up their stategraphs for an example.

States don't usually require any edits except for a couple of exceptions:

Attack states: Players don't target things the same way mobs do, so ripping the attack state right out of the mob's stategraph will leave you with an attack that attacks nothing. Theres two ways to fix this:

1.Replace inst.components.combat:DoAttack() with inst:PerformBufferedAction(). This is the easiest way to solve this issue.

(also an important note: inst:PerformBufferedAction must be used in all states that are used by the actionhandler, it what makes you actually do the action).

2. Set a target in the attack state, which is a bit more complicated but is necessary for certain things (like multi-attacks or teleporting to the target).

local buffaction = inst:GetBufferedAction()
			local target = buffaction ~= nil and buffaction.target or nil
			inst.components.combat:SetTarget(target)
			if target ~= nil then
            if target:IsValid() then
                inst:FacePoint(target:GetPosition())
                inst.sg.statemem.attacktarget = target
				end
			end

Put this in your onenter function in the attack state. Then add inst.components.combat:DoAttack(inst.sg.statemem.attacktarget) where you want it.

The last thing that is required is probably the death state, you don't want it to work like it works for mobs (it will make you stay dead forever, players don't fade). So just make sure you AnimOver event looks like this:

events =
        {
            EventHandler("animover", function(inst)
                if inst.AnimState:AnimDone() then
			TheWorld:PushEvent("ms_playerdespawnanddelete", inst) --This despawns the player, but make sure they drop all their items first! or else the items will get deleted too!
                end
            end),
        },

If you want mobs to become ghosts after they die, you'll need to do some extra work. Something that I can't really recall everything that its needed for atm. Now for the last step for the SG is this at the bottom:

return StateGraph("yourprefabnamehere", states, events, "idle", actionhandlers)

Then you're pretty much done,  you might have to end up making additional edits or adding variables once you run it, but this should get you started.

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
  • Create New...