Last active
April 11, 2021 21:49
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
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