Skip to content

Instantly share code, notes, and snippets.

@LaughingLeader
Last active April 11, 2021 21:49
Show Gist options
  • Save LaughingLeader/7bd856beb51b1198a8ce2d126143bc53 to your computer and use it in GitHub Desktop.
Save LaughingLeader/7bd856beb51b1198a8ce2d126143bc53 to your computer and use it in GitHub Desktop.
An example of using LeaderLib's skill listener to create a function that runs during each different skill state.
--[[
Add LeaderLib's Lua folder to VSCode workspace (not the mod) so EmmyLua will use the scripts for auto-completion.
With the way EmmyLua works, if you set up these globals in a different file,
EmmyLua will associate their usage with LeaderLib's scripts, so you'll see types/etc pointing back to the originals.
]]
--This should ideally be called in a script loaded by both client and server.
--It will import LeaderLib's various globals into your mod's table, so you can use helpers without setting them first.
--Mods.MyModTable needs to be the name you specified in your mod's ositools settings.
Mods.LeaderLib.Import(Mods.MyModTable)
--This one will import everything that's valid, instead of just the recommended globals, so take care not to conflict with global names.
--Mods.LeaderLib.ImportUnsafe(Mods.MyModTable)
local function CleanupMoveAction(handle, target, x, y, z)
if GetDistanceToPosition(target, x,y,z) <= Ext.Random(0.5, 1.25) then
NRD_GameActionDestroy(handle)
return true
end
return false
end
local function PlayEffectAtTarget(effect, target, targetType)
if targetType == "string" then
local x,y,z = GetPosition(target)
PlayEffectAtPosition(effect, x, y, z)
elseif targetType == "table" then
local x,y,z = table.unpack(target)
PlayEffectAtPosition(effect, x, y, z)
end
end
local effects = {
"RS3_FX_Skills_Void_Power_Attack_Impact_01",
"RS3_FX_Skills_Void_SwapGround_Impact_Root_01",
"RS3_FX_Skills_Void_Divine_Impact_Root_01",
}
RegisterSkillListener("Projectile_Grenadier_Grenade_Vortex", function(skill, caster, state, data)
if state == SKILL_STATE.PREPARE then
--[[
Called during NRD_OnActionStateEnter. Should only be invoked once until the skill is cast or cancelled,
as LeaderLib ignores the constant PrepareSkill events for the character after the first.
data is nil since there aren't any targets.
]]
elseif state == SKILL_STATE.USED then
-- Called once after CharacterUsedSkill/associated events, after any targets have been gathered.
---@see SkillEventData
elseif state == SKILL_STATE.CAST then
-- Called after SkillCast. May have targets.
---@see SkillEventData#ForEach
--ForEach runs the function on each target, which may be a position or object,
--defaulting to objects only unless a mode is specified (-1 for all, 0 for objects, 1 for positions).
data:ForEach(function(target, targetType, skillData)
PlayEffectAtTarget(Common.GetRandomTableEntry(effects), target, targetType)
end, data.TargetMode.All)
elseif state == SKILL_STATE.HIT then
-- When the skill hits an object during NRD_OnHit.
---@see HitData#Success
elseif state == SKILL_STATE.PROJECTILEHIT then
-- Called after the extender's ProjectileHit event.
-- data is ProjectileHitData
---@see ProjectileHitData
if not StringHelpers.IsNullOrEmpty(data.Target) and IsTagged(caster, "MyMod_BonusGrenadeEffects") == 1 then
GameHelpers.ExplodeProjectile(caster, data.Position, "Projectile_MyMod_BonusVortexDamage")
end
local x,y,z = table.unpack(data.Target)
local radius = Ext.GetStat(skill).ExplodeRadius
for _,v in pairs(Ext.GetCharactersAroundPosition(x, y, z, radius)) do
if CharacterIsEnemy(caster, v) == 1 then
local handle = NRD_CreateGameObjectMove(v, x, y, z, "", caster)
local timerName = "Timers_MyMod_VortexPull_"..caster..v
local attempts = 0
local startTimerFunction
--Used by the oneshot timer to clean up the move action when the target is within range of the projectile target,
local cleanupMoveAction = function()
attempts = attempts + 1
--Safeguard in case we're stuck in an endless loop for some reason
if attempts >= 7 then
return
end
if not CleanupMoveAction(handle, v, x, y, z) then
startTimerFunction(250)
else
-- Safeguard to make sure the target is still on the AI grid
local tx,ty,tz = GetPosition(v)
if not GameHelpers.Grid.IsValidPosition(tx, tz) then
TeleportToRandomPosition(v, 1.0, "")
end
end
end
--We're making a separate function for calling the timer so we can call it from cleanupMoveAction,
--until the target is within range or the limit is exceeded.
startTimerFunction = function(delay)
StartOneshotTimer(timerName, delay, cleanupMoveAction)
end
startTimerFunction(1000)
end
end
end
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment