Берёшь сначала абилку с библиотеки (не забудь про луа), потом в дд всовываешь модифаер, который постоянно работает (Passive, ThinkInvertal), и при каждом ThinkInterval (OnIntervalThink) будет юзать, то что в OnSpellStart, не забудь про AbilityBehavior, да и не забудь про timers.lua из баребонки, чтобы её подключить тебе надо вставить в свою кастомку эту штуку, затем пропиши в addon_game_mode.lua в самом начале require("timers.lua"), по идеи получится этоКак сделать так, чтобы ременанты использовались сами каждые несколько секунд? Пожалуйста, напишите код, я в луа и кастомных скиллах вообще не шарю, а так хоть посмотрю на примере вашего кода.
"storm_spirit_static_remnant_passive"
{
"BaseClass" "ability_datadriven"
"AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE"
"AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL"
"SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO"
"FightRecapLevel" "1"
"AbilityTextureName" "storm_spirit_static_remnant"
"AbilityCastPoint" "0 0 0 0"
"AbilityCooldown" "3.5"
"AbilityDuration" "12.0 12.0 12.0 12.0"
"AbilityManaCost" "70 80 90 100"
"AbilitySpecial"
{
"01"
{
"var_type" "FIELD_INTEGER"
"static_remnant_radius" "235"
}
"02"
{
"var_type" "FIELD_INTEGER"
"static_remnant_damage_radius" "260"
}
"03"
{
"var_type" "FIELD_FLOAT"
"static_remnant_delay" "1.0"
}
"04"
{
"var_type" "FIELD_INTEGER"
"static_remnant_damage" "140 180 220 260"
}
"05"
{
"var_type" "FIELD_FLOAT"
"interval" "4 3 2 1" // Интервал каста
}
}
"precache"
{
"particle" "particles/units/heroes/hero_stormspirit/stormspirit_static_remnant.vpcf"
}
"Modifiers"
{
"modifier_static_remnant_trigger_datadriven"
{
"Passive" "1"
"IsHidden" "1"
"ThinkInterval" "%interval"
"OnIntervalThink"
{
"FireSound"
{
"Target" "CASTER"
"EffectName" "Hero_StormSpirit.StaticRemnantPlant"
}
"RunScript"
{
"ScriptFile" "путь/к/луа/storm_spirit_static_remnant.lua"
"Function" "static_remnant_init"
}
}
}
"modifier_static_remnant_dummy_datadriven"
{
"OverrideAnimation" "ACT_DOTA_CAST_ABILITY_1"
"EffectName" "particles/units/heroes/hero_stormspirit/stormspirit_static_remnant.vpcf"
"EffectAttachType" "follow_origin"
"States"
{
"MODIFIER_STATE_INVULNERABLE" "MODIFIER_STATE_VALUE_ENABLED"
"MODIFIER_STATE_NO_HEALTH_BAR" "MODIFIER_STATE_VALUE_ENABLED"
"MODIFIER_STATE_NO_UNIT_COLLISION" "MODIFIER_STATE_VALUE_ENABLED"
"MODIFIER_STATE_NOT_ON_MINIMAP" "MODIFIER_STATE_VALUE_ENABLED"
"MODIFIER_STATE_UNSELECTABLE" "MODIFIER_STATE_VALUE_ENABLED"
"MODIFIER_STATE_COMMAND_RESTRICTED" "MODIFIER_STATE_VALUE_ENABLED"
}
}
"modifier_static_remnant_dummy_freeze_datadriven"
{
"States"
{
"MODIFIER_STATE_FROZEN" "MODIFIER_STATE_VALUE_ENABLED"
}
}
}
}
function static_remnant_init( keys )
local caster = keys.caster
local target = caster:GetAbsOrigin()
local ability = keys.ability
local model_name = caster:GetModelName()
local dummyModifierName = "modifier_static_remnant_dummy_datadriven"
local dummyFreezeModifierName = "modifier_static_remnant_dummy_freeze_datadriven"
local remnant_timer = 0.0
local remnant_interval_check = 0.1
local delay = ability:GetLevelSpecialValueFor( "static_remnant_delay", ability:GetLevel() - 1 )
local trigger_radius = ability:GetLevelSpecialValueFor( "static_remnant_radius", ability:GetLevel() - 1 )
local damage_radius = ability:GetLevelSpecialValueFor( "static_remnant_damage_radius", ability:GetLevel() - 1 )
local ability_damage = ability:GetLevelSpecialValueFor( "static_remnant_damage", ability:GetLevel() - 1 )
local ability_damage_type = ability:GetAbilityDamageType()
local ability_duration = ability:GetDuration()
local dummy = CreateUnitByName( caster:GetName(), target, false, caster, nil, caster:GetTeamNumber() )
ability:ApplyDataDrivenModifier( caster, dummy, dummyModifierName, {} )
Timers:CreateTimer( delay, function()
if not dummy:HasModifier( dummyFreezeModifierName ) then
ability:ApplyDataDrivenModifier( caster, dummy, dummyFreezeModifierName, {} )
end
local units = FindUnitsInRadius( caster:GetTeamNumber(), target, caster, trigger_radius,
DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_BASIC + DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, 0, false )
if #units > 0 then
for k, v in pairs( units ) do
local damageTable = {
victim = v,
attacker = caster,
damage = ability_damage,
damage_type = ability_damage_type
}
ApplyDamage( damageTable )
end
StartSoundEvent( "Hero_StormSpirit.StaticRemnantExplode", dummy )
dummy:RemoveSelf()
return nil
end
remnant_timer = remnant_timer + remnant_interval_check
if remnant_timer >= ability_duration then
dummy:RemoveSelf()
return nil
else
return remnant_interval_check
end
end
)
end
TIMERS_VERSION = "1.05"
--[[
-- A timer running every second that starts immediately on the next frame, respects pauses
Timers:CreateTimer(function()
print ("Hello. I'm running immediately and then every second thereafter.")
return 1.0
end
)
Timers(function()
print ("Hello. I'm running immediately and then every second thereafter.")
return 1.0
end)
Timers:CreateTimer(GameMode.someFunction, GameMode)
-- A timer running every second that starts 5 seconds in the future, respects pauses
Timers:CreateTimer(5, function()
print ("Hello. I'm running 5 seconds after you called me and then every second thereafter.")
return 1.0
end
)
Timers:CreateTimer({
endTime = 10, -- when this timer should first execute, you can omit this if you want it to run first on the next frame
callback = function()
print ("Hello. I'm running 10 seconds after when I was started.")
end
})
Timers:CreateTimer({
useGameTime = false,
endTime = 10, -- when this timer should first execute, you can omit this if you want it to run first on the next frame
callback = function()
print ("Hello. I'm running 10 seconds after I was started even if someone paused the game.")
end
})
Timers:CreateTimer("uniqueTimerString3", {
useGameTime = false,
endTime = 120,
callback = function()
print ("Hello. I'm running after 2 minutes and then every second thereafter.")
return 1
end
})
Timers:CreateTimer("uniqueTimerString3", {
useOldStyle = true,
endTime = GameRules:GetGameTime() + 5,
callback = function()
print ("Hello. I'm running after 5 seconds and then every second thereafter.")
return GameRules:GetGameTime() + 1
end
})
]]
TIMERS_THINK = 0.01
if Timers == nil then
print ( '[Timers] creating Timers' )
Timers = {}
setmetatable(Timers, {
__call = function(t, ...)
return t:CreateTimer(...)
end
})
end
function Timers:start()
Timers = self
self.timers = {}
local ent = SpawnEntityFromTableSynchronous("info_target", {targetname="timers_lua_thinker"})
ent:SetThink("Think", self, "timers", TIMERS_THINK)
end
function Timers:Think()
local now = GameRules:GetGameTime()
for k,v in pairs(Timers.timers) do
local bUseGameTime = true
if v.useGameTime ~= nil and v.useGameTime == false then
bUseGameTime = false
end
local bOldStyle = false
if v.useOldStyle ~= nil and v.useOldStyle == true then
bOldStyle = true
end
local now = GameRules:GetGameTime()
if not bUseGameTime then
now = Time()
end
if v.endTime == nil then
v.endTime = now
end
if now >= v.endTime then
-- Remove from timers list
Timers.timers[k] = nil
Timers.runningTimer = k
Timers.removeSelf = false
local status, nextCall
if v.context then
status, nextCall = xpcall(function() return v.callback(v.context, v) end, function (msg)
return msg..'\n'..debug.traceback()..'\n'
end)
else
status, nextCall = xpcall(function() return v.callback(v) end, function (msg)
return msg..'\n'..debug.traceback()..'\n'
end)
end
Timers.runningTimer = nil
if status then
if nextCall and not Timers.removeSelf then
if bOldStyle then
v.endTime = v.endTime + nextCall - now
else
v.endTime = v.endTime + nextCall
end
Timers.timers[k] = v
end
else
Timers:HandleEventError('Timer', k, nextCall)
end
end
end
return TIMERS_THINK
end
function Timers:HandleEventError(name, event, err)
name = tostring(name or 'unknown')
event = tostring(event or 'unknown')
err = tostring(err or 'unknown')
if not self.errorHandled then
-- Store that we handled an error
self.errorHandled = true
end
end
function Timers:CreateTimer(name, args, context)
if type(name) == "function" then
if args ~= nil then
context = args
end
args = {callback = name}
name = DoUniqueString("timer")
elseif type(name) == "table" then
args = name
name = DoUniqueString("timer")
elseif type(name) == "number" then
args = {endTime = name, callback = args}
name = DoUniqueString("timer")
end
if not args.callback then
print("Invalid timer created: "..name)
return
end
local now = GameRules:GetGameTime()
if args.useGameTime ~= nil and args.useGameTime == false then
now = Time()
end
if args.endTime == nil then
args.endTime = now
elseif args.useOldStyle == nil or args.useOldStyle == false then
args.endTime = now + args.endTime
end
args.context = context
Timers.timers[name] = args
return name
end
function Timers:RemoveTimer(name)
Timers.timers[name] = nil
if Timers.runningTimer == name then
Timers.removeSelf = true
end
end
function Timers:RemoveTimers(killAll)
local timers = {}
Timers.removeSelf = true
if not killAll then
for k,v in pairs(Timers.timers) do
if v.persist then
timers[k] = v
end
end
end
Timers.timers = timers
end
if not Timers.timers then Timers:start() end
GameRules.Timers = Timers
Большие спасибо!Берёшь сначала абилку с библиотеки (не забудь про луа), потом в дд всовываешь модифаер, который постоянно работает (Passive, ThinkInvertal), и при каждом ThinkInterval (OnIntervalThink) будет юзать, то что в OnSpellStart, не забудь про AbilityBehavior, да и не забудь про timers.lua из баребонки, чтобы её подключить тебе надо вставить в свою кастомку эту штуку, затем пропиши в addon_game_mode.lua в самом начале require("timers.lua"), по идеи получится это
C++:"storm_spirit_static_remnant_passive" { "BaseClass" "ability_datadriven" "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" "FightRecapLevel" "1" "AbilityTextureName" "storm_spirit_static_remnant" "AbilityCastPoint" "0 0 0 0" "AbilityCooldown" "3.5" "AbilityDuration" "12.0 12.0 12.0 12.0" "AbilityManaCost" "70 80 90 100" "AbilitySpecial" { "01" { "var_type" "FIELD_INTEGER" "static_remnant_radius" "235" } "02" { "var_type" "FIELD_INTEGER" "static_remnant_damage_radius" "260" } "03" { "var_type" "FIELD_FLOAT" "static_remnant_delay" "1.0" } "04" { "var_type" "FIELD_INTEGER" "static_remnant_damage" "140 180 220 260" } "05" { "var_type" "FIELD_FLOAT" "interval" "4 3 2 1" // Интервал каста } } "precache" { "particle" "particles/units/heroes/hero_stormspirit/stormspirit_static_remnant.vpcf" } "Modifiers" { "modifier_static_remnant_trigger_datadriven" { "Passive" "1" "IsHidden" "1" "ThinkInterval" "%interval" "OnIntervalThink" { "FireSound" { "Target" "CASTER" "EffectName" "Hero_StormSpirit.StaticRemnantPlant" } "RunScript" { "ScriptFile" "путь/к/луа/storm_spirit_static_remnant.lua" "Function" "static_remnant_init" } } } "modifier_static_remnant_dummy_datadriven" { "OverrideAnimation" "ACT_DOTA_CAST_ABILITY_1" "EffectName" "particles/units/heroes/hero_stormspirit/stormspirit_static_remnant.vpcf" "EffectAttachType" "follow_origin" "States" { "MODIFIER_STATE_INVULNERABLE" "MODIFIER_STATE_VALUE_ENABLED" "MODIFIER_STATE_NO_HEALTH_BAR" "MODIFIER_STATE_VALUE_ENABLED" "MODIFIER_STATE_NO_UNIT_COLLISION" "MODIFIER_STATE_VALUE_ENABLED" "MODIFIER_STATE_NOT_ON_MINIMAP" "MODIFIER_STATE_VALUE_ENABLED" "MODIFIER_STATE_UNSELECTABLE" "MODIFIER_STATE_VALUE_ENABLED" "MODIFIER_STATE_COMMAND_RESTRICTED" "MODIFIER_STATE_VALUE_ENABLED" } } "modifier_static_remnant_dummy_freeze_datadriven" { "States" { "MODIFIER_STATE_FROZEN" "MODIFIER_STATE_VALUE_ENABLED" } } } }
Lua:function static_remnant_init( keys ) local caster = keys.caster local target = caster:GetAbsOrigin() local ability = keys.ability local model_name = caster:GetModelName() local dummyModifierName = "modifier_static_remnant_dummy_datadriven" local dummyFreezeModifierName = "modifier_static_remnant_dummy_freeze_datadriven" local remnant_timer = 0.0 local remnant_interval_check = 0.1 local delay = ability:GetLevelSpecialValueFor( "static_remnant_delay", ability:GetLevel() - 1 ) local trigger_radius = ability:GetLevelSpecialValueFor( "static_remnant_radius", ability:GetLevel() - 1 ) local damage_radius = ability:GetLevelSpecialValueFor( "static_remnant_damage_radius", ability:GetLevel() - 1 ) local ability_damage = ability:GetLevelSpecialValueFor( "static_remnant_damage", ability:GetLevel() - 1 ) local ability_damage_type = ability:GetAbilityDamageType() local ability_duration = ability:GetDuration() local dummy = CreateUnitByName( caster:GetName(), target, false, caster, nil, caster:GetTeamNumber() ) ability:ApplyDataDrivenModifier( caster, dummy, dummyModifierName, {} ) Timers:CreateTimer( delay, function() if not dummy:HasModifier( dummyFreezeModifierName ) then ability:ApplyDataDrivenModifier( caster, dummy, dummyFreezeModifierName, {} ) end local units = FindUnitsInRadius( caster:GetTeamNumber(), target, caster, trigger_radius, DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_BASIC + DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, 0, false ) if #units > 0 then for k, v in pairs( units ) do local damageTable = { victim = v, attacker = caster, damage = ability_damage, damage_type = ability_damage_type } ApplyDamage( damageTable ) end StartSoundEvent( "Hero_StormSpirit.StaticRemnantExplode", dummy ) dummy:RemoveSelf() return nil end remnant_timer = remnant_timer + remnant_interval_check if remnant_timer >= ability_duration then dummy:RemoveSelf() return nil else return remnant_interval_check end end ) end
Lua:TIMERS_VERSION = "1.05" --[[ -- A timer running every second that starts immediately on the next frame, respects pauses Timers:CreateTimer(function() print ("Hello. I'm running immediately and then every second thereafter.") return 1.0 end ) Timers(function() print ("Hello. I'm running immediately and then every second thereafter.") return 1.0 end) Timers:CreateTimer(GameMode.someFunction, GameMode) -- A timer running every second that starts 5 seconds in the future, respects pauses Timers:CreateTimer(5, function() print ("Hello. I'm running 5 seconds after you called me and then every second thereafter.") return 1.0 end ) Timers:CreateTimer({ endTime = 10, -- when this timer should first execute, you can omit this if you want it to run first on the next frame callback = function() print ("Hello. I'm running 10 seconds after when I was started.") end }) Timers:CreateTimer({ useGameTime = false, endTime = 10, -- when this timer should first execute, you can omit this if you want it to run first on the next frame callback = function() print ("Hello. I'm running 10 seconds after I was started even if someone paused the game.") end }) Timers:CreateTimer("uniqueTimerString3", { useGameTime = false, endTime = 120, callback = function() print ("Hello. I'm running after 2 minutes and then every second thereafter.") return 1 end }) Timers:CreateTimer("uniqueTimerString3", { useOldStyle = true, endTime = GameRules:GetGameTime() + 5, callback = function() print ("Hello. I'm running after 5 seconds and then every second thereafter.") return GameRules:GetGameTime() + 1 end }) ]] TIMERS_THINK = 0.01 if Timers == nil then print ( '[Timers] creating Timers' ) Timers = {} setmetatable(Timers, { __call = function(t, ...) return t:CreateTimer(...) end }) end function Timers:start() Timers = self self.timers = {} local ent = SpawnEntityFromTableSynchronous("info_target", {targetname="timers_lua_thinker"}) ent:SetThink("Think", self, "timers", TIMERS_THINK) end function Timers:Think() local now = GameRules:GetGameTime() for k,v in pairs(Timers.timers) do local bUseGameTime = true if v.useGameTime ~= nil and v.useGameTime == false then bUseGameTime = false end local bOldStyle = false if v.useOldStyle ~= nil and v.useOldStyle == true then bOldStyle = true end local now = GameRules:GetGameTime() if not bUseGameTime then now = Time() end if v.endTime == nil then v.endTime = now end if now >= v.endTime then -- Remove from timers list Timers.timers[k] = nil Timers.runningTimer = k Timers.removeSelf = false local status, nextCall if v.context then status, nextCall = xpcall(function() return v.callback(v.context, v) end, function (msg) return msg..'\n'..debug.traceback()..'\n' end) else status, nextCall = xpcall(function() return v.callback(v) end, function (msg) return msg..'\n'..debug.traceback()..'\n' end) end Timers.runningTimer = nil if status then if nextCall and not Timers.removeSelf then if bOldStyle then v.endTime = v.endTime + nextCall - now else v.endTime = v.endTime + nextCall end Timers.timers[k] = v end else Timers:HandleEventError('Timer', k, nextCall) end end end return TIMERS_THINK end function Timers:HandleEventError(name, event, err) name = tostring(name or 'unknown') event = tostring(event or 'unknown') err = tostring(err or 'unknown') if not self.errorHandled then -- Store that we handled an error self.errorHandled = true end end function Timers:CreateTimer(name, args, context) if type(name) == "function" then if args ~= nil then context = args end args = {callback = name} name = DoUniqueString("timer") elseif type(name) == "table" then args = name name = DoUniqueString("timer") elseif type(name) == "number" then args = {endTime = name, callback = args} name = DoUniqueString("timer") end if not args.callback then print("Invalid timer created: "..name) return end local now = GameRules:GetGameTime() if args.useGameTime ~= nil and args.useGameTime == false then now = Time() end if args.endTime == nil then args.endTime = now elseif args.useOldStyle == nil or args.useOldStyle == false then args.endTime = now + args.endTime end args.context = context Timers.timers[name] = args return name end function Timers:RemoveTimer(name) Timers.timers[name] = nil if Timers.runningTimer == name then Timers.removeSelf = true end end function Timers:RemoveTimers(killAll) local timers = {} Timers.removeSelf = true if not killAll then for k,v in pairs(Timers.timers) do if v.persist then timers[k] = v end end end Timers.timers = timers end if not Timers.timers then Timers:start() end GameRules.Timers = Timers