Jump to content

Recommended Posts

Hi and thanks for taking the time to read this.

1 - about attack animation, it is only to change the animation when my custom character is not using a weapon (so it will use another animation instead of "punch"). I was able to make it work overriding the "attack" stategraph and adding a custom tag (in modmain). But I do not know if this is the only method. If there is a simpler or "cleaner" method of doing it, I'd like to know.
The problem is that when I create a world with caves, the code does not work and I continue to use punch animation.

2 - Is there any way to remove the "heal" action from a custom character? So that she can not use the item, only the option to "examine" appears (instead of using the item and saying: "i cant do that").

To make a character unable to use healing item, use it in modmain.lua

local require = GLOBAL.require
local State = GLOBAL.State
local Action = GLOBAL.Action
local ActionHandler = GLOBAL.ActionHandler
local TimeEvent = GLOBAL.TimeEvent
local EventHandler = GLOBAL.EventHandler
local ACTIONS = GLOBAL.ACTIONS
local FRAMES = GLOBAL.FRAMES

local FAKEHEAL = GLOBAL.Action({ priority=10, mount_valid=true })	
FAKEHEAL.str = "Examine"
FAKEHEAL.id = "FAKEHEAL"
FAKEHEAL.fn = function(act)

    if act.invobject and act.invobject.components.healer and act.doer:HasTag("YOUR CHARACTER'S TAG") then

        act.doer.components.talker:Say("Oh no I suck at nursing!", 2.5, false)
        return true
    end
end

AddAction(FAKEHEAL)

AddComponentAction("USEITEM", "healer", function(inst, doer, target, actions, right) -- heal others
    if target.replica.health ~= nil and target.replica.health:CanHeal() and doer:HasTag("YOUR CHARACTER'S TAG")  then
        table.insert(actions, ACTIONS.FAKEHEAL)
    end
end)

AddComponentAction("INVENTORY", "healer", function(inst, doer, actions, right) -- self heal
    if doer.replica.health ~= nil and doer.replica.health:CanHeal() and doer:HasTag("YOUR CHARACTER'S TAG") then
        table.insert(actions, ACTIONS.FAKEHEAL)
    end
end)

local fakeheal_handler = ActionHandler(ACTIONS.FAKEHEAL)

AddStategraphActionHandler("wilson", fakeheal_handler)
AddStategraphActionHandler("wilson_client", fakeheal_handler)

 

To make a character unable to be healed, use it in master_postinit of your character's lua file.

inst.components.health.canheal = false

 

BTW how do you override a stategraph? Can you show me your code?

Edited by Norfeder

I've tried with components, actions... I can't believe the answer was in just one line of code. I feel totally trolled ._.

inst.components.health.canheal = false

Thanks for this "fakeheal" action, I understood what it does, but it was not necessary, since this line of code results in what I wanted xD

For the attack override, I've tried this:

Spoiler

AddStategraphState("wilson", State{
        name = "attack",
        tags = { "attack", "notalking", "abouttoattack", "autopredict" },

        onenter = function(inst)
            local buffaction = inst:GetBufferedAction()
            local target = buffaction ~= nil and buffaction.target or nil
            local equip = inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
            inst.components.combat:SetTarget(target)
            inst.components.combat:StartAttack()
            inst.components.locomotor:Stop()
            local cooldown = inst.components.combat.min_attack_period + .5 * FRAMES
            if inst.components.rider:IsRiding() then
                if equip ~= nil and (equip.components.projectile ~= nil or equip:HasTag("rangedweapon")) then
                    inst.AnimState:PlayAnimation("player_atk_pre")
                    inst.AnimState:PushAnimation("player_atk", false)
                    if (equip.projectiledelay or 0) > 0 then
                        --V2C: Projectiles don't show in the initial delayed frames so that
                        --     when they do appear, they're already in front of the player.
                        --     Start the attack early to keep animation in sync.
                        inst.sg.statemem.projectiledelay = 8 * FRAMES - equip.projectiledelay
                        if inst.sg.statemem.projectiledelay > FRAMES then
                            inst.sg.statemem.projectilesound =
                                (equip:HasTag("icestaff") and "dontstarve/wilson/attack_icestaff") or
                                (equip:HasTag("firestaff") and "dontstarve/wilson/attack_firestaff") or
                                "dontstarve/wilson/attack_weapon"
                        elseif inst.sg.statemem.projectiledelay <= 0 then
                            inst.sg.statemem.projectiledelay = nil
                        end
                    end
                    if inst.sg.statemem.projectilesound == nil then
                        inst.SoundEmitter:PlaySound(
                            (equip:HasTag("icestaff") and "dontstarve/wilson/attack_icestaff") or
                            (equip:HasTag("firestaff") and "dontstarve/wilson/attack_firestaff") or
                            "dontstarve/wilson/attack_weapon",
                            nil, nil, true
                        )
                    end
                    cooldown = math.max(cooldown, 13 * FRAMES)
                else
                    inst.AnimState:PlayAnimation("atk_pre")
                    inst.AnimState:PushAnimation("atk", false)
                    DoMountSound(inst, inst.components.rider:GetMount(), "angry", true)
                    cooldown = math.max(cooldown, 16 * FRAMES)
                end
            elseif equip ~= nil and equip:HasTag("whip") then
                inst.AnimState:PlayAnimation("whip_pre")
                inst.AnimState:PushAnimation("whip", false)
                inst.sg.statemem.iswhip = true
                inst.SoundEmitter:PlaySound("dontstarve/common/whip_pre", nil, nil, true)
                cooldown = math.max(cooldown, 17 * FRAMES)
            elseif equip ~= nil and equip:HasTag("book") then
                inst.AnimState:PlayAnimation("attack_book")
                inst.sg.statemem.isbook = true
                inst.SoundEmitter:PlaySound("dontstarve/wilson/attack_whoosh", nil, nil, true)
                cooldown = math.max(cooldown, 19 * FRAMES)
            elseif equip ~= nil and equip:HasTag("chop_attack") and inst:HasTag("woodcutter") then
                inst.AnimState:PlayAnimation(inst.AnimState:IsCurrentAnimation("woodie_chop_loop") and inst.AnimState:GetCurrentAnimationTime() < 7.1 * FRAMES and "woodie_chop_atk_pre" or "woodie_chop_pre")
                inst.AnimState:PushAnimation("woodie_chop_loop", false)
                inst.sg.statemem.ischop = true
                cooldown = math.max(cooldown, 11 * FRAMES)
            elseif equip ~= nil and equip.components.weapon ~= nil and not equip:HasTag("punch") then
                inst.AnimState:PlayAnimation("atk_pre")
                inst.AnimState:PushAnimation("atk", false)
                if (equip.projectiledelay or 0) > 0 then
                    --V2C: Projectiles don't show in the initial delayed frames so that
                    --     when they do appear, they're already in front of the player.
                    --     Start the attack early to keep animation in sync.
                    inst.sg.statemem.projectiledelay = 8 * FRAMES - equip.projectiledelay
                    if inst.sg.statemem.projectiledelay > FRAMES then
                        inst.sg.statemem.projectilesound =
                            (equip:HasTag("icestaff") and "dontstarve/wilson/attack_icestaff") or
                            (equip:HasTag("firestaff") and "dontstarve/wilson/attack_firestaff") or
                            "dontstarve/wilson/attack_weapon"
                    elseif inst.sg.statemem.projectiledelay <= 0 then
                        inst.sg.statemem.projectiledelay = nil
                    end
                end
                if inst.sg.statemem.projectilesound == nil then
                    inst.SoundEmitter:PlaySound(
                        (equip:HasTag("icestaff") and "dontstarve/wilson/attack_icestaff") or
                        (equip:HasTag("shadow") and "dontstarve/wilson/attack_nightsword") or
                        (equip:HasTag("firestaff") and "dontstarve/wilson/attack_firestaff") or
                        "dontstarve/wilson/attack_weapon",
                        nil, nil, true
                    )
                end
                cooldown = math.max(cooldown, 13 * FRAMES)
            elseif equip ~= nil and (equip:HasTag("light") or equip:HasTag("nopunch")) then
                inst.AnimState:PlayAnimation("atk_pre")
                inst.AnimState:PushAnimation("atk", false)
                inst.SoundEmitter:PlaySound("dontstarve/wilson/attack_weapon", nil, nil, true)
                cooldown = math.max(cooldown, 13 * FRAMES)
            elseif inst:HasTag("beaver") then
                inst.sg.statemem.isbeaver = true
                inst.AnimState:PlayAnimation("atk_pre")
                inst.AnimState:PushAnimation("atk", false)
                inst.SoundEmitter:PlaySound("dontstarve/wilson/attack_whoosh", nil, nil, true)
                cooldown = math.max(cooldown, 13 * FRAMES)
            elseif inst:HasTag("mytag") then
                inst.AnimState:PlayAnimation("newanimation")
                cooldown = math.max(cooldown, 24 * FRAMES)
            else
                inst.AnimState:PlayAnimation("punch")
                inst.SoundEmitter:PlaySound("dontstarve/wilson/attack_whoosh", nil, nil, true)
                cooldown = math.max(cooldown, 24 * FRAMES)
            end

            inst.sg:SetTimeout(cooldown)

            if target ~= nil then
                inst.components.combat:BattleCry()
                if target:IsValid() then
                    inst:FacePoint(target:GetPosition())
                    inst.sg.statemem.attacktarget = target
                end
            end
        end,

        onupdate = function(inst, dt)
            if (inst.sg.statemem.projectiledelay or 0) > 0 then
                inst.sg.statemem.projectiledelay = inst.sg.statemem.projectiledelay - dt
                if inst.sg.statemem.projectiledelay <= FRAMES then
                    if inst.sg.statemem.projectilesound ~= nil then
                        inst.SoundEmitter:PlaySound(inst.sg.statemem.projectilesound, nil, nil, true)
                        inst.sg.statemem.projectilesound = nil
                    end
                    if inst.sg.statemem.projectiledelay <= 0 then
                        inst:PerformBufferedAction()
                        inst.sg:RemoveStateTag("abouttoattack")
                    end
                end
            end
        end,

        timeline =
        {
            TimeEvent(6 * FRAMES, function(inst)
                if inst.sg.statemem.isbeaver then
                    inst:PerformBufferedAction()
                    inst.sg:RemoveStateTag("abouttoattack")
                elseif inst.sg.statemem.ischop then
                    inst.SoundEmitter:PlaySound("dontstarve/wilson/attack_weapon", nil, nil, true)
                end
            end),
            TimeEvent(8 * FRAMES, function(inst)
                if not (inst.sg.statemem.isbeaver or
                        inst.sg.statemem.iswhip or
                        inst.sg.statemem.isbook) and
                    inst.sg.statemem.projectiledelay == nil then
                    inst:PerformBufferedAction()
                    inst.sg:RemoveStateTag("abouttoattack")
                end
            end),
            TimeEvent(10 * FRAMES, function(inst)
                if inst.sg.statemem.iswhip or inst.sg.statemem.isbook then
                    inst:PerformBufferedAction()
                    inst.sg:RemoveStateTag("abouttoattack")
                end
            end),
        },

        ontimeout = function(inst)
            inst.sg:RemoveStateTag("attack")
            inst.sg:AddStateTag("idle")
        end,

        events =
        {
            EventHandler("equip", function(inst) inst.sg:GoToState("idle") end),
            EventHandler("unequip", function(inst) inst.sg:GoToState("idle") end),
            EventHandler("animqueueover", function(inst)
                if inst.AnimState:AnimDone() then
                    inst.sg:GoToState("idle")
                end
            end),
        },

        onexit = function(inst)
            inst.components.combat:SetTarget(nil)
            if inst.sg:HasStateTag("abouttoattack") then
                inst.components.combat:CancelAttack()
            end
        end,
    })

 

Just added

elseif inst:HasTag("mytag") then
                inst.AnimState:PlayAnimation("newanimation")
                cooldown = math.max(cooldown, 24 * FRAMES)

before the punch animation. So, other characters can still punch normally.

Edited by Rita

The animation problem only happens when cave world is enabled right? My friends said that he encountered the exact same problem when doing a play test with me. I don't know why but this problem has never happened to me. My solution is to let the action.fn trigger the stategraph instead of the action itself.

local CUSTOMATTACK = GLOBAL.Action({ priority=3, canforce=true, mount_valid=true })	
CUSTOMATTACK.str = "Attack"
CUSTOMATTACK.id = "CUSTOMATTACK"
CUSTOMATTACK.fn = function(act)
  
    act.doer.sg:GoToState("custom_attack_state")
    act.doer.components.combat:DoAttack(act.target)
  
    return true
end

AddAction(CUSTOMATTACK)

local customattack_handler = ActionHandler(CUSTOMATTACK)
AddStategraphActionHandler("wilson", customattack_handler)
AddStategraphActionHandler("wilson_client", customattack_handler)

AddComponentAction("SCENE", "health", function(inst, doer, actions, right)
    if not right and inst.replica.health ~= nil and not inst.replica.health:IsDead() and inst.replica.combat ~= nil and inst.replica.combat:CanBeAttacked(doer) and doer:HasTag("YOUR CHARACTER'S TAG")  then
        table.insert(actions, ACTIONS.CUSTOMATTACK)
    end
end)

So now you have to make a new stategraph for this fake action and you can't use BufferedAction() in stategraph anymore. But at least it makes the animation works.

Edited by Norfeder

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
×
  • Create New...