Jump to content

Recommended Posts

Trying to bounce from C++ to LUA is a ride. So far I've not had any luck getting things to work. One of those things being increasing a mod character's speed for X amount of seconds after taking damage from a hostile source. I've peeked at other mods, looked into LUA tutorials, and dug through DST's base code and have yet to wrap my head around what I've got wrong.

-- When hit, increase speed for 5 seconds
local function CombatSpeed(inst)
    local self = inst.components.combat
    local _GetAttacked = self.GetAttacked
    
    self.GetAttacked = function(self, attacker, damage, weapon, stimuli)
        if attacker and damage then
            -- Increase run speed for 5 seconds
            local startTimer = os.time()
            local endTimer = startTimer+5
            
            if os.time() <= endTimer then
            inst.components.locomotor.walkspeed = (TUNING.WILSON_WALK_SPEED * 1.6)
            inst.components.locomotor.runspeed = (TUNING.WILSON_RUN_SPEED * 1.6)
        
                -- if hit again before timer ends, reset 5 second timer countdown
                if attacker and damage then
                startTimer = os.time()
                end
            end
            --if timer ends, reset run speed back to default
            inst.components.locomotor.walkspeed = (TUNING.WILSON_WALK_SPEED * 1.0)
            inst.components.locomotor.runspeed = (TUNING.WILSON_RUN_SPEED * 1.0)
        end
        return _GetAttacked(self, attacker, damage, weapon, stimuli)
    end
end

If anyone could point me in the right direction on how to do this, that would be awesome. My brain is fried from looking at this for hours and I bet you if anything, it's something super simple and obvious that I'm overlooking at the moment.

Hello, welcome to the forums! (The good part, that is...)
What I see here is that you're trying to hook into the combat component's GetAttacked function. While yes, this is entirely possible, there's Event Listeners in DST for it if what you're aiming for doesn't require modifying how Combat itself works to begin with.

You can try to combine an event listener for your character, and an external speed modifier together. For this, I'd do:

inst:ListenForEvent("attacked", FN_NAME_HERE)

in your Character's Lua file, under the master_postinit function.

Then, above both Common and Master postinits, you can add a local fn with a name that you chose above:

--Data is a table, purely optional, but it contains all of what the GetAttacked function passes (attacker, damage, etc.)
local function FN_NAME_HERE(inst, data)
	--Params are: Source (from which instance), Modifier, Key (as String, optional)
	inst.components.locomotor:SetExternalSpeedMultiplier(inst, 1.6, "panic")

	--This should be self-explanatory! A timer component should exist on all players to begin with, so no need for nil checks.
	if not inst.components.timer:TimerExists("panic_speedboost") then
		inst.components.timer:StartTimer("panic_speedboost", 5)
    else
    	inst.components.timer:SetTimeLeft("panic_speedboost", 5)
    end
end

Then, below the first event listener, add another one:

inst:ListenForEvent("timerdone", FN_NAME_HERE)

After that, you create another local function with the same name.

--This time, data contains only one thing: Timer name, as a string. Data itself is still a table, however!
local function FN_NAME_HERE(inst, data)
	if data.name == "panic_speedboost" then
		inst.components.locomotor:RemoveExternalSpeedMultiplier(inst, "panic")
    end
end

 

That should do it. There's also a debuff component for things, but that requires creating a prefab and assigning three functions to what happens when the effect is applied, removed, and extended. If you want to know and would rather make this into a buff, please do tell me.
Also, most game components are stored in scripts/components; taking a look into those may give you insight on what you can do with them, and create more character perks from it. Good luck, though!

  • Like 2
  • Thanks 1

You can informally add a component like Baguettes did of course, but if you're interested in something more modular then make a component instead.

To add a component I'd recommend making a file in your modfolder like so: scripts/components/componentname
I can relate to modding taking a lot out of you. When you do inst:AddComponent"battlehaste" it will automatically find battlehaste.lua

Spoiler
-- Where your OnAttacked function is passed, any attacked data that we want to ignore is is blocked here
--[[
In components/combat.lua this "attacked" event pushes the following data:
attacker, damage, damageresolved, original_damage, weapon, stimuli, spdamage, redirected, noimpactsound
We will use data.attacker and data.damage
--]]
local function OnAttacked(inst, data)
    local self = inst.components.battlehaste
	local attacker = data.attacker
	local damage = data.damage
	
	--IsValid check because other "attacked" handlers may have removed us (from components/frozen.lua)
	--NOTE: see how EntityScript:PushEvent caches all listeners; that is why an invalid entity could still reach this event listener (from components/frozen.lua)
	if not attacker or not damage or not inst:IsValid() or not self.locomotor or not self.timer then return end -- Basic checks
	if not attacker:HasTag"hostile" or damage <= 0 then return end -- Special checks
	self:StartHasteTask()
end

local function OnTimerDone(inst, data)
	local self = inst.components.battlehaste
    if data.name == "battlehaste" then
        self:StopHasteTask()
    end
end

local BattleHaste = Class(function(self, inst)
	self.inst = inst
	self.inst:AddTag"battlehaste" -- Can easily check if something has this component
	self.task = nil
	self.checkperiod = .1 -- How often the task updates a second
	self:Reset() -- Simple init
	
	if not inst.components.locomotor then -- Locomotor is a required component, may as well try to add it if not present
		inst:AddComponent("locomotor")
	end
	self.locomotor = self.inst.components.locomotor
	
	if not inst.components.timer then -- Timer is a required component
		inst:AddComponent("timer")
	end
	self.timer = self.inst.components.timer
	inst:ListenForEvent("timerdone", OnTimerDone)

	self.inst:ListenForEvent("attacked", OnAttacked) -- Our main function, being attacked is a built-in global thing you can listen for
end)

function BattleHaste:StopHasteTask()
	self.locomotor:RemoveExternalSpeedMultiplier(self.inst, "battlehaste")
	if self.task then
		self.task:Cancel()
		self.task = nil
	end
end

--[[
Don't concern yourself too much with this but it's basically a smooth speed increase
--]]
function BattleHaste:RampMath(time, type)
	if not type then return self.speed end
	local min = 0
	local max = self.speed
	local phase = self.ramp * type
	local cos = ((math.cos(((time + phase) * math.pi) / self.ramp)) + 1)/2
	local range = self.speed - min
	local ramp = (cos * range) + min
	return ramp
end

-- The purpose of our component is mainly in this task
local function HasteTask(inst)
	local self = inst.components.battlehaste
	if not self.timer:TimerExists"battlehaste" then --Just in case
		self:StopHasteTask()
		return
	end
	local curtime = self.timer:GetTimeElapsed"battlehaste"
	local ramptype = nil
	if curtime <= self.duration then
		if curtime < self.ramp then -- ease-in
			ramptype = 1
		elseif curtime > self.duration - self.ramp then -- ease-out
			ramptype = 0
		end
		local curspeed = self:RampMath(curtime, ramptype)
		local addspeed = curspeed
		if ramptype == 1 then
			addspeed = math.max(self.ongoingspeed, addspeed)
		end
		self.locomotor:SetExternalSpeedMultiplier(self.inst, "battlehaste", 1 + addspeed, SourceModifierList.additive) 
	end
end

function BattleHaste:StartHasteTask()
	if self.task then
		self.ongoingspeed = self.locomotor:GetExternalSpeedMultiplier(self.inst, "battlehaste") - 1
	else
		self.ongoingspeed = 0
	end
	self:StopHasteTask()
	if not self.timer:TimerExists"battlehaste" then
		self.timer:StartTimer("battlehaste", self.duration)
	else
		self.timer:SetTimeLeft("battlehaste", self.duration)
	end
	self.task = self.inst:DoPeriodicTask(self.checkperiod, HasteTask)
end

-- Can set values externally if needed
function BattleHaste:SetSpeedDuration(time) 
    self.duration = time
end

function BattleHaste:SetSpeedMult(speed)
	self.speed = speed
end

function BattleHaste:SetRamping(ramp) -- How long it takes for speedboost to accelerate/deccelerate
	self.ramp = ramp
end

-- Basic saving/loading
function BattleHaste:Reset()
    self.duration = 5
	self.speed = 0.6
	self.ramp = 0.3
	self.task = nil
	self.ongoingspeed = 0
end

function BattleHaste:OnSave()
    local data = 
	{
		duration = self.duration,
		speed = self.speed,
		ramp = self.ramp,
		ongoingspeed = self.ongoingspeed,
	}
	return data
end

function BattleHaste:OnLoad(data)
	self.duration = data.duration or 5
	self.speed = data.speed or 0.6
	self.ramp = data.ramp or .3 -- How long it takes to ramp
	self.ongoingspeed = data.ongoingspeed or 0
end

return BattleHaste

 

 

Of course, I don't make components often (in fact this is my first time making one) so sorry if it's bugged, but with components you can put this on anything.

Edited by oregu
  • Like 1
  • Thanks 1

Thank you both so much for your help! LUA still continues to elude me, but despite the horrors I shall persist!

I know it's going to be even a bigger mess figuring this out, and honestly if it's too much I may abandon the idea, but my next plan is an aura to increase action speeds for foraging items. I know it'll have to involve editing the animation speeds as well, but on the bright side there's at least one fun perk thanks to both of your help!

Making items should be easier (knock on wood) and I'll start on that before messing with more perk making, regardless, wish me luck!

And again, I cannot thank you both enough for the help! I know it's not much, but I credited you both as helpful sources inside the main character script files! :> Hope you two have a wonderful day and if you see me posting again asking for aid, then the horrors of LUA have still got their spooky little claws in me R.I.P.

 :spidercowers:

  • Like 2

:^( Alas, I don't know if it's something I'm doing or what, but both methods of implementing this perk only winds up with me unable to load into a world with the mod enabled. Not sure if there's an easier way to test what I'm doing beyond launching DST off and on every change made, but man is this mod doing my head in still.

57 minutes ago, D3dans said:

:^( Alas, I don't know if it's something I'm doing or what, but both methods of implementing this perk only winds up with me unable to load into a world with the mod enabled. Not sure if there's an easier way to test what I'm doing beyond launching DST off and on every change made, but man is this mod doing my head in still.

Nevermind I'm a liar! Turns out I misplaced a comma in the speech LUA and that was the whole reason the mod itself wouldn't load... The little things in coding I swear. :snarlingspider:

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