mirror of
https://github.com/ornfelt/azerothcore_lua_scripts
synced 2026-08-22 06:26:03 -04:00
220 lines
No EOL
12 KiB
Lua
220 lines
No EOL
12 KiB
Lua
-- We create a 'box' (aka 'namespace') to keep all our code for this NPC in one place.
|
|
-- We appropriately call this box 'TurretBoi'.
|
|
local TurretBoi = {}
|
|
|
|
-- We tell the game which NPC this script is for by specifying its ID number.
|
|
-- Since we'll be using TurretBoi.NPC_ID in mulitple parts of the script, we want to define it.
|
|
-- This also makes it much easier to change later on if we need to do so since all we need to change is one npcid.
|
|
TurretBoi.NPC_ID = 832350
|
|
|
|
-- We make a list of spells this NPC will use. Right now, it's just one spell.
|
|
TurretBoi.SPELL_IDS = {
|
|
SPELL_TO_CAST = 38083 -- The spell this NPC will cast.
|
|
-- You can put more spells here:
|
|
-- SECOND_SPELL_TO_CAST = 123456
|
|
}
|
|
|
|
-- When this NPC needs to cast a spell, this function gets called.
|
|
-- Don't worry about the 'eventId', 'delay', and 'repeats' stuff, they are just placeholders we have to include.
|
|
-- See notes below.
|
|
function TurretBoi.CastSpell(eventId, delay, repeats, creature)
|
|
-- The NPC casts the spell on whomever it's fighting.
|
|
-- 'creature:GetVictim' means that the creature is attacking it's current (highest threat) target
|
|
-- The 'true' statement at the end tells the creature to cast this spell instantly and free (0 mana, energy, etc).
|
|
---- If you have more than one spell being cast in combat, this is the easiest way to prevent the npc from interrupting itself.
|
|
---- You can of course work around this to ensure no other spell is being cast, but then it will skip over said spell.
|
|
---- The better alternative is create a que system which allows the first cast to go through, puts the second cast on standby, and casts it after some time has passed.
|
|
---- For the purposes of this guide, that's way too complicated. I've included an example at the very bottom of the script though.
|
|
creature:CastSpell(creature:GetVictim(), TurretBoi.SPELL_IDS.SPELL_TO_CAST, true)
|
|
end
|
|
|
|
-- *If you wanted a second spell to cast, you'd define it the same way as above:
|
|
--function TurretBoi.CastSecondSpell(eventId, delay, repeats, creature)
|
|
-- creature:CastSpell(creature:GetVictim(), TurretBoi.SPELL_IDS.SECOND_SPELL_TO_CAST, true)
|
|
--end
|
|
|
|
|
|
-- This function runs when the NPC starts fighting.
|
|
function TurretBoi.OnEnterCombat(event, creature)
|
|
-- We set up the NPC to cast its spell every 3 seconds in milliseconds.
|
|
-- 0 means it repeats over and over. Setting this number to 1 would make it happen only once, for example.
|
|
creature:RegisterEvent(TurretBoi.CastSpell, 3000, 0)
|
|
-- *Here's also where you'd add an event to cast a second spell
|
|
-- creature:RegisterEvent(TurretBoi.CastSecondSpell, 7000, 0)
|
|
end
|
|
|
|
-- This function runs when the NPC stops fighting.
|
|
function TurretBoi.OnLeaveCombat(event, creature)
|
|
-- We tell the NPC to forget any special actions we set up earlier.
|
|
-- This is important for saving memory and making sure events don't just keep happning.
|
|
-- It's basically telling the script 'Hey, we're done!'
|
|
creature:RemoveEvents()
|
|
end
|
|
|
|
-- This function runs when the NPC dies.
|
|
function TurretBoi.OnDied(event, creature)
|
|
-- Again, we tell the NPC to forget any special actions we set up earlier.
|
|
creature:RemoveEvents()
|
|
end
|
|
|
|
-- We tell the game to use the above functions when certain things happen to the NPC.
|
|
-- You can find these 'hooks' in modules/mod-eluna/src/LuaEngine/Hooks.h
|
|
-- Basically when A happens, make B happen too.
|
|
RegisterCreatureEvent(TurretBoi.NPC_ID, 1, TurretBoi.OnEnterCombat)
|
|
RegisterCreatureEvent(TurretBoi.NPC_ID, 2, TurretBoi.OnLeaveCombat)
|
|
RegisterCreatureEvent(TurretBoi.NPC_ID, 4, TurretBoi.OnDied)
|
|
|
|
--------------------------------
|
|
--------------------------------
|
|
-- ADDITIONAL NOTES
|
|
--------------------------------
|
|
--------------------------------
|
|
|
|
-- Question: Why do we need to include placeholders (arguments) like 'eventId', 'delay', and 'repeats' even if we don't use them?
|
|
-- Think of it like a handshake. Both sides expect it to go a certain way.
|
|
-- If we leave out any part, the handshake fails, and the game might act weird or crash.
|
|
|
|
-- Question: How do you know what placeholders ('eventId', 'delay', etc.) to include in the first place?
|
|
-- This comes from reading the manual, looking at examples, and sometimes asking other people who know.
|
|
-- If we get it wrong, the game will usually give us a hint about what's missing.
|
|
|
|
-- Question: What's the deal with this 'TurretBoi' box we keep everything in?
|
|
-- Putting all related things in one 'box' (we call it a namespace) helps keep our script tidy and makes sure it doesn't mess with other scripts.
|
|
-- It's like giving it its own room with a nameplate.
|
|
|
|
-- Question: Any downsides to using these 'boxes' or namespaces?
|
|
-- Yes, if you're new, this can look confusing.
|
|
-- Also, if you're not careful, you could end up using more computer memory.
|
|
-- Lastly, if you put everything in separate 'boxes', it might be hard to make them talk to each other.
|
|
|
|
---------------------------------
|
|
---------------------------------
|
|
-- CUSTOM MAGMADAR FIGHT EXAMPLE
|
|
---------------------------------
|
|
---------------------------------
|
|
|
|
--[[
|
|
|
|
-- Defines a table to store functions and variables for Magmadar throughout the fight
|
|
local Magmadar = {}
|
|
-- Initialize a table to store a queue of spells.
|
|
-- This is a system I came up with so that bosses won't interrupt themselves with their own spell casts and skip certain spells.
|
|
-- If you have skipcheck set to true on spell casts,
|
|
-- hen you do not need a spell que system since all spells will be cast instantly and not interrupt eachother.
|
|
Magmadar.spellQueue = {}
|
|
|
|
|
|
-- Defines a list of spells. You can always call these directly with the ID number instead directly in the cast functions.
|
|
local SPELL_FRENZY = 19451
|
|
local SPELL_PANIC = 19408
|
|
local SPELL_LAVA_BOMB = 19411
|
|
local SPELL_LAVA_BOMB_RANGED = 20474
|
|
local SPELL_SUMMON_CORE_HOUND = 364726 -- this is a custom spell i made to summon core hounds
|
|
local SPELL_ENRAGE = 27680
|
|
|
|
-- Defines a range to look up the distance necessary to cast "ranged" Lava Bomb as he targets melee and ranged seperately.
|
|
local MELEE_TARGET_LOOKUP_DIST = 10.0
|
|
|
|
-- Periodically casts frenzy on self with the emote broadcast to players to be more blizzlike
|
|
function Magmadar.CastFrenzy(eventId, delay, calls, creature)
|
|
table.insert(Magmadar.spellQueue, {spell = SPELL_FRENZY, targetType = 'self', emote = "Magmadar goes into a killing Frenzy!"})
|
|
end
|
|
|
|
-- Casts panic, an aoe spell. You could have this spell target anyone since it's aoe so I just decided to have it cast on the current victim to make things simple
|
|
function Magmadar.CastPanic(eventId, delay, calls, creature)
|
|
table.insert(Magmadar.spellQueue, {spell = SPELL_PANIC, targetType = 'victim'})
|
|
end
|
|
|
|
-- This is the function for melee lava bomb. Selects from a random pool of ai targets, which will include bots. ai targets are threat targets on Magmadar's table. Alternatively could have set this to only cast on players.
|
|
function Magmadar.CastLavaBomb(eventId, delay, calls, creature)
|
|
table.insert(Magmadar.spellQueue, {spell = SPELL_LAVA_BOMB, targetType = 'random', range = MELEE_TARGET_LOOKUP_DIST, maxRange = false}) -- this is set to false so that Magmadar does not check range of players prior to casting.
|
|
end
|
|
|
|
-- Ranged lavabomb function
|
|
function Magmadar.CastLavaBombRanged(eventId, delay, calls, creature)
|
|
table.insert(Magmadar.spellQueue, {spell = SPELL_LAVA_BOMB_RANGED, targetType = 'random', range = MELEE_TARGET_LOOKUP_DIST, maxRange = true}) -- this is set to true to cast lava bomb on ranged targets.
|
|
end
|
|
|
|
-- periodically summons corehounds. Any spells that are flagged as unit target caster should be cast on the creature itself
|
|
function Magmadar.CastSummonCoreHound(eventId, delay, calls, creature)
|
|
table.insert(Magmadar.spellQueue, {spell = SPELL_SUMMON_CORE_HOUND, targetType = 'self'})
|
|
end
|
|
|
|
-- This is an enrage funciton i put on magmadar as a dps check that will cast after 3 minutes
|
|
function Magmadar.CastEnrage(eventId, delay, calls, creature)
|
|
table.insert(Magmadar.spellQueue, {spell = SPELL_ENRAGE, targetType = 'self', emote = "Magmadar becomes enraged, significantly increasing attack speed and damage!"})
|
|
end
|
|
|
|
-- This is the actual spell que system
|
|
-- Determine's target based on the targetType property
|
|
-- 'self' targets the creature itself
|
|
-- 'victim' targets the creature's current target (its victim)
|
|
-- 'random' targets a random entity within the range, or outside of it if maxRange is set to true
|
|
-- The target is determined using the creature's AI targets, which includes both players and NPCs
|
|
-- If there are multiple targets in range, one is selected randomly
|
|
-- If a target is determined, the creature casts the spell at the target.
|
|
-- If the spell in the queue has an emote associated with it, the creature also sends that emote.
|
|
function Magmadar.ProcessSpellQueue(eventId, delay, calls, creature)
|
|
if not creature:IsCasting() and #Magmadar.spellQueue > 0 then
|
|
local nextSpell = table.remove(Magmadar.spellQueue, 1)
|
|
local target
|
|
|
|
if nextSpell.targetType == 'self' then
|
|
target = creature
|
|
elseif nextSpell.targetType == 'victim' then
|
|
target = creature:GetVictim()
|
|
elseif nextSpell.targetType == 'random' then
|
|
local targets = creature:GetAITargets(nextSpell.range or 0)
|
|
if #targets > 0 then
|
|
if nextSpell.maxRange then
|
|
for _, t in pairs(targets) do
|
|
if t:GetDistance(creature) > nextSpell.range then
|
|
target = t
|
|
break
|
|
end
|
|
end
|
|
else
|
|
target = targets[math.random(1, #targets)]
|
|
end
|
|
end
|
|
end
|
|
|
|
if target then
|
|
creature:CastSpell(target, nextSpell.spell, false)
|
|
if nextSpell.emote then
|
|
creature:SendUnitEmote(nextSpell.emote)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Enter combat functions, registering defined spell-casting events with random intervals.
|
|
-- Additionally, Magmadar regularly checks its spell queue.
|
|
function Magmadar.OnEnterCombat(event, creature, target)
|
|
creature:RegisterEvent(Magmadar.CastFrenzy, math.random(12000, 16000), 0) -- example: casts frenzy between 12-16 seconds if is not casting, otherwise casts it directly afterwards
|
|
creature:RegisterEvent(Magmadar.CastPanic, math.random(20000, 25000), 0)
|
|
creature:RegisterEvent(Magmadar.CastLavaBomb, math.random(8000, 10000), 0)
|
|
creature:RegisterEvent(Magmadar.CastLavaBombRanged, math.random(9000, 12000), 0)
|
|
creature:RegisterEvent(Magmadar.CastSummonCoreHound, 55000, 0)
|
|
creature:RegisterEvent(Magmadar.CastEnrage, 300000, 1) -- 5 minute enrage timer
|
|
creature:RegisterEvent(Magmadar.ProcessSpellQueue, 2000, 0)
|
|
end
|
|
|
|
-- When the creature leaves combat, it removes all its events and clears the spell queue, otherwise he continues to cast and process functions
|
|
function Magmadar.OnLeaveCombat(event, creature)
|
|
creature:RemoveEvents()
|
|
Magmadar.spellQueue = {}
|
|
end
|
|
|
|
-- When the creature dies, it removes all its events and clears the spell queue, otherwise he continues to cast even while died lol
|
|
function Magmadar.OnDied(event, creature, killer)
|
|
creature:RemoveEvents()
|
|
Magmadar.spellQueue = {}
|
|
end
|
|
|
|
-- Registers events with the Magmadar based on its creature_template ID (11982)
|
|
-- The numbers correspond to: 1 - when the creature enters combat, 2 - when the creature leaves combat, 4 - when the creature dies.
|
|
RegisterCreatureEvent(11982, 1, Magmadar.OnEnterCombat)
|
|
RegisterCreatureEvent(11982, 2, Magmadar.OnLeaveCombat)
|
|
RegisterCreatureEvent(11982, 4, Magmadar.OnDied)
|
|
]]-- |