Jump to content

Recommended Posts

I have completed my mod, however, the action my character can do is available to ALL characters when the mod is installed. How do I make it so only this one character can perform that action instead of anyone. Is there a code I can use to identify the character by name and say that only that character can then perform the listed action?

19 minutes ago, alainmcd said:

Either add a specific component for your character (like the Reader component for waxwell and wickerbottom), or check for your character in the action (if act.doer and act.doer.prefab == "yourcharacter").

Not sure where to add the act.doer check in the code. Should I add it in the components file for the throwable or should I add it in the modmain somewhere?

No, I meant if you were creating a whole new action for your character. Take a brief look at actions.lua to see what I meant. Or don't.

If you want your item to only be available to your character, make your life easier and just make it character-specific. Add this to your item definition:

inst:AddComponent("characterspecific")
inst.components.characterspecific:SetOwner("yourcharacter")

If you want your item to be available to all characters and to make it so only your character throw it, try using something like this in your modmain.lua:

local function Throw(self, pt, thrower)
	if (self.specialuseby ~= nil and thrower and thrower.prefab ~= self.specialuseby) then
		return false
	end
	self:OldThrow(pt, thrower)
end

local function ThrowablePostInit(self)
	self.OldThrow = self.Throw
	self.Throw = Throw
	
	function self:SpecialUseBy(prefab)
		self.specialuseby = prefab
	end
end

AddComponentPostInit("throwable", ThrowablePostInit)

and this in your item file:

inst:AddComponent("throwable")
inst.components.throwable:SpecialUseBy("yourcharacter")

Note: it might work, but I wouldn't count on it. It's untested and I'm not reliable to begin with. Hopefully the intent is clear and helps you find a solution. Feel free to ask if anything isn't obvious or if you have any issues.

Edited by alainmcd
2 hours ago, alainmcd said:

No, I meant if you were creating a whole new action for your character. Take a brief look at actions.lua to see what I meant. Or don't.

If you want your item to only be available to your character, make your life easier and just make it character-specific. Add this to your item definition:


inst:AddComponent("characterspecific")
inst.components.characterspecific:SetOwner("yourcharacter")

If you want your item to be available to all characters and to make it so only your character throw it, try using something like this in your modmain.lua:


local function Throw(self, pt, thrower)
	if (self.specialuseby ~= nil and thrower and thrower.prefab ~= self.specialuseby) then
		return false
	end
	self:OldThrow(pt, thrower)
end

local function ThrowablePostInit(self)
	self.OldThrow = self.Throw
	self.Throw = Throw
	
	function self:SpecialUseBy(prefab)
		self.specialuseby = prefab
	end
end

AddComponentPostInit("throwable", ThrowablePostInit)

and this in your item file:


inst:AddComponent("throwable")
inst.components.throwable:SpecialUseBy("yourcharacter")

Note: it might work, but I wouldn't count on it. It's untested and I'm not reliable to begin with. Hopefully the intent is clear and helps you find a solution. Feel free to ask if anything isn't obvious or if you have any issues.

I can't get it to work and I've been at this for hours. It seems like wherever I add an exception for one character to have this action, it breaks EVERYTHING. Here is my code, without the exceptions added in, so you can see what I'm dealing with and sorry for how much of a spaghetti mess this is.


TUNING.TIGERSPEAR_DAMAGE = 50
TUNING.TIGERHUNGER = 3
TUNING.TIGERSANITY = 1

local SMALL_USES = GetModConfigData("SMALL_USES")
local LARGE_USES = GetModConfigData("LARGE_USES")
local RANGE_CHECK = GetModConfigData("RANGE_CHECK")


PrefabFiles = {
    "tiger",
    "tigerspear",
    "spear_projectile",
}

Assets = {
    Asset( "IMAGE", "images/saveslot_portraits/tiger.tex" ),
    Asset( "ATLAS", "images/saveslot_portraits/tiger.xml" ),

    Asset( "IMAGE", "images/selectscreen_portraits/tiger.tex" ),
    Asset( "ATLAS", "images/selectscreen_portraits/tiger.xml" ),
    
    Asset( "IMAGE", "images/selectscreen_portraits/tiger_silho.tex" ),
    Asset( "ATLAS", "images/selectscreen_portraits/tiger_silho.xml" ),

    Asset( "IMAGE", "bigportraits/tiger.tex" ),
    Asset( "ATLAS", "bigportraits/tiger.xml" ),
    
    Asset( "IMAGE", "images/map_icons/tiger.tex" ),
    Asset( "ATLAS", "images/map_icons/tiger.xml" ),
    
    Asset("ATLAS", "images/inventoryimages/tigerspear.xml"),
    
    Asset("ANIM", "anim/spear_projectile.zip"),

}

local require = GLOBAL.require
local Ingredient = GLOBAL.Ingredient
local RECIPETABS = GLOBAL.RECIPETABS
local STRINGS = GLOBAL.STRINGS
local TECH = GLOBAL.TECH


local State = GLOBAL.State
local TimeEvent = GLOBAL.TimeEvent
local EventHandler = GLOBAL.EventHandler
local FRAMES = GLOBAL.FRAMES


GLOBAL.STRINGS.CHARACTER_TITLES.tiger = "The Bestial Huntress"
GLOBAL.STRINGS.CHARACTER_NAMES.tiger = "Tiger"
GLOBAL.STRINGS.CHARACTER_DESCRIPTIONS.tiger = "*Isn't afraid of the dark\n*Can move quickly\n*Dislikes Dogs"
GLOBAL.STRINGS.CHARACTER_QUOTES.tiger = "\"Meow?\""
GLOBAL.STRINGS.NAMES.TIGERSPEAR = "Tiger's Spear"
GLOBAL.STRINGS.RECIPE_DESC.TIGERSPEAR = "An Unbreakable Spear"
GLOBAL.STRINGS.CHARACTERS.GENERIC.DESCRIBE.TIGERSPEAR = "An Unbreakable Spear"

AddPrefabPostInit("spear", function(inst)
        local spear = inst
        local function onattack(inst, attacker, target, skipsanity)
            attacker.components.hunger:DoDelta(-TUNING.TIGERHUNGER)
            attacker.components.sanity:DoDelta(TUNING.TIGERSANITY)
            local smalltarget = target:HasTag("smallcreature")
            if target.components.combat then
                target.components.combat:GetAttacked(attacker, attacker.components.combat:CalcDamage(target, spear), weapon)
            end            
            
            local newspear = GLOBAL.SpawnPrefab('spear')
            newspear.Transform:SetPosition(inst:GetPosition():Get())
            if newspear.components.finiteuses then
                newspear.components.finiteuses:SetUses(spear.components.finiteuses:GetUses())
                newspear.components.finiteuses:Use((smalltarget and not missed)
                    and GLOBAL.TUNING.SPEAR_USES/SMALL_USES
                    or GLOBAL.TUNING.SPEAR_USES/LARGE_USES)
            end
            newspear:AddTag("scarytoprey")
            newspear:DoTaskInTime(1, function(inst) inst:RemoveTag("scarytoprey") end)
            inst:Remove()
            spear:Remove()

            attacker.SoundEmitter:PlaySound("dontstarve/wilson/attack_weapon", nil, nil, true)
        end
    
        inst:AddComponent('spearthrowable')
        inst.components.spearthrowable:SetRange(8, 10)
        inst.components.spearthrowable:SetOnAttack(onattack)
        inst.components.spearthrowable:SetProjectile("spear_projectile")
end)

AddPrefabPostInit("tigerspear", function(inst)
    local tigerspear = inst
    local function onattack(inst, attacker, target, skipsanity)
        attacker.components.hunger:DoDelta(-TUNING.TIGERHUNGER)
        attacker.components.sanity:DoDelta(TUNING.TIGERSANITY)
        local smalltarget = target:HasTag("smallcreature")
        if target.components.combat then
            target.components.combat:GetAttacked(attacker, attacker.components.combat:CalcDamage(target, tigerspear), weapon)
        end            
            
        local newtigerspear = GLOBAL.SpawnPrefab('tigerspear')
        newtigerspear.Transform:SetPosition(inst:GetPosition():Get())
        if newtigerspear.components.finiteuses then
            newtigerspear.components.finiteuses:SetUses(spear.components.finiteuses:GetUses())
            newtigerspear.components.finiteuses:Use((smalltarget and not missed)
                and GLOBAL.TUNING.SPEAR_USES/SMALL_USES
                or GLOBAL.TUNING.SPEAR_USES/LARGE_USES)
        end
        newtigerspear:AddTag("scarytoprey")
        newtigerspear:DoTaskInTime(1, function(inst) inst:RemoveTag("scarytoprey") end)
        inst:Remove()
        
        tigerspear:Remove()

        attacker.SoundEmitter:PlaySound("dontstarve/wilson/attack_weapon", nil, nil, true)
    end
    
    inst:AddComponent('spearthrowable')
    inst.components.spearthrowable:SetRange(8, 10)
    inst.components.spearthrowable:SetOnAttack(onattack)
    inst.components.spearthrowable:SetProjectile("spear_projectile")
end)

local SPEARTHROW = GLOBAL.Action(4,        -- priority
                                false,    -- instant (set to not instant)
                                true,    -- right mouse button
                                10,        -- distance check
                                false,    -- ghost valid (set to not ghost valid)
                                false,    -- can force (false)
                                nil)    -- range check function
SPEARTHROW.str = "Throw spear"
SPEARTHROW.id = "SPEARTHROW"
SPEARTHROW.fn = function(act)
    if act.invobject then
        local target = act.target
        if target == nil then
            for k,v in pairs(GLOBAL.TheSim:FindEntities(act.pos.x, act.pos.y, act.pos.z, 20)) do
                if v.components and v.components.combat and v.components.combat:CanBeAttacked(act.doer) and
                act.doer.components and act.doer.components.combat and act.doer.components.combat:CanTarget(v)
                and (not v:HasTag("wall")) then
                    target = v
                    break
                end
            end
        end
        if target then
                local prefab = act.invobject.prefab
                act.invobject.components.spearthrowable:LaunchProjectile(act.doer, target)
                local newspear = act.doer.components.inventory:FindItem(
                    function(item) return item.prefab == prefab end)
                if newspear then
                    act.doer.components.inventory:Equip(newspear)
                end
        end
        return true
    end
end
AddAction(SPEARTHROW)

local throw_spear = State({
        name = "throw_spear",
        tags = { "attack", "notalking", "abouttoattack", "autopredict" },

        onenter = function(inst)
            local buffaction = inst:GetBufferedAction()
            local target = buffaction ~= nil and buffaction.target or nil
            inst.components.combat:SetTarget(target)
            inst.components.combat:StartAttack()
            inst.components.locomotor:Stop()

            inst.AnimState:PlayAnimation("throw")

            inst.sg:SetTimeout(2)

            if target ~= nil and target:IsValid() then
                inst:FacePoint(target.Transform:GetWorldPosition())
                inst.sg.statemem.attacktarget = target
            elseif buffaction ~= nil and buffaction.pos ~= nil then
                inst:FacePoint(buffaction.pos)
            end
        end,

        timeline =
        {
            TimeEvent(7 * FRAMES, function(inst)
                inst:PerformBufferedAction()
                inst.sg:RemoveStateTag("abouttoattack")
            end),
        },

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

        events =
        {
            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,})
AddStategraphState("wilson", throw_spear)


AddStategraphActionHandler("wilson", GLOBAL.ActionHandler(SPEARTHROW, function(inst, action)
    if not inst.sg:HasStateTag("attack") then
        return "throw_spear"
    end
end))

local tigerspear = GLOBAL.Recipe("tigerspear", { Ingredient("twigs", 4)}, RECIPETABS.WAR,  TECH.SCIENCE_ZERO)
tigerspear.atlas = "images/inventoryimages/tigerspear.xml"

GLOBAL.STRINGS.CHARACTERS.TIGER = require "speech_tiger"

table.insert(GLOBAL.CHARACTER_GENDERS.FEMALE, "tiger")

AddMinimapAtlas("images/map_icons/tiger.xml")
AddModCharacter("tiger")

Am I correct to understand that this code works (no crashes) and lets any character throw both regular spears and tiger's spears, and you want to change it to only allow your character to throw spears? If that's the case, try adding this right after SPEARTHROW.fn = function(act):

	if (act.invobject and (act.invobject.prefab == "spear" or act.invobject.prefab == "tigerspear"))
	and not (act.doer and act.doer.prefab == "yourcharacter") then
		return false
	end

 

15 minutes ago, alainmcd said:

Am I correct to understand that this code works (no crashes) and lets any character throw both regular spears and tiger's spears, and you want to change it to only allow your character to throw spears? If that's the case, try adding this right after SPEARTHROW.fn = function(act):


	if (act.invobject and (act.invobject.prefab == "spear" or act.invobject.prefab == "tigerspear"))
	and not (act.doer and act.doer.prefab == "yourcharacter") then
		return false
	end

 

It works! It's a little clunky but.. it works! The other characters still try to throw the spear, it says "I can't do that", then the spear appears in their hand again. But it never leaves their inventory. Amazing! Whenever I tried that, either every character couldn't do it or it'd just crash

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...