mirror of
https://github.com/ProjectEQ/projecteqquests
synced 2026-08-12 00:26:10 -04:00
438 lines
24 KiB
Lua
438 lines
24 KiB
Lua
-- it's possible to retry `animals`, `steel/silk`, and `sensei` on live due to a bug
|
|
--
|
|
-- if the `elements` or `emotions` phase was failed and then completed on a retry
|
|
-- attempt, the later phases will not end the dz on failure and instead reset state
|
|
-- normally these later phases disallow retries and end the dz on failure
|
|
--
|
|
-- since live will not respawn a wave more than once, failing the `animals` phase twice
|
|
-- results in the npcs depopping but not respawning (allowing it to be bypassed)
|
|
-- `silk/steel` and `sensei` do not depop but can be infinitely attempted due to this bug
|
|
local enable_live_bug = true
|
|
|
|
-- npc ids for zone version 10 (Stormreach Challenge)
|
|
-- todo: grids (current grids for elements are also not live accurate)
|
|
local waves = {
|
|
elements = {
|
|
fire = { id = 340630, spawn2 = 256786, grid = 15 }, -- #Disciple_of_Fire
|
|
earth = { id = 340629, spawn2 = 256785, grid = 16 }, -- #Disciple_of_Earth
|
|
water = { id = 340628, spawn2 = 256784, grid = 14 }, -- #Disciple_of_Water
|
|
wood = { id = 340627, spawn2 = 256783, grid = 13 }, -- #Disciple_of_Wood
|
|
metal = { id = 340626, spawn2 = 256782, grid = 12 }, -- #Disciple_of_Metal
|
|
},
|
|
emotions = {
|
|
rage = { id = 340625, spawn2 = 256781, dst = { x = -114.875, y = -324.0, z = 248.75, h = 0.0 } }, -- #Master_of_Rage
|
|
deceit = { id = 340624, spawn2 = 256780, dst = { x = -101.0, y = -324.0, z = 248.75, h = 0.0 } }, -- #Master_of_Deceit
|
|
doubt = { id = 340623, spawn2 = 256779, dst = { x = -85.0, y = -323.875, z = 248.75, h = 0.0 } }, -- #Master_of_Doubt
|
|
content = { id = 340622, spawn2 = 256778, dst = { x = -70.875, y = -323.875, z = 248.75, h = 0.0 } }, -- #Master_of_Contentment
|
|
},
|
|
animals = {
|
|
puma = { id = 340621, spawn2 = 256777, dst = { x = -114.875, y = -324.0, z = 248.75, h = 0.0 } }, -- #Grandmaster_Puma
|
|
snake = { id = 340620, spawn2 = 256776, dst = { x = -92.875, y = -323.875, z = 248.75, h = 0.0 } }, -- #Grandmaster_Snake
|
|
beetle = { id = 340619, spawn2 = 256775, dst = { x = -70.875, y = -323.875, z = 249.25, h = 0.0 } }, -- #Grandmaster_Beetle
|
|
},
|
|
steelsilk = {
|
|
steel = { id = 340618, spawn2 = 256774, dst = { x = -115.0, y = -323.875, z = 249.25, h = 0.0 } }, -- #Steel
|
|
silk = { id = 340617, spawn2 = 256773, dst = { x = -70.875, y = -324.0, z = 248.75, h = 0.0 } }, -- #Silk
|
|
},
|
|
}
|
|
|
|
local states = {
|
|
none = 0,
|
|
start = { move = 10, wait = 11, begin = 12 },
|
|
elements = { move = 20, chat = 21, reply = 22, aggro = 23, fight = 24 },
|
|
emotions = { move = 30, rage = 31, deceit = 32, doubt = 33, content = 34, aggro = 35, fight = 36 },
|
|
animals = { move = 40, aggro = 41, fight = 42 },
|
|
steelsilk = { move = 50, steel = 51, silk = 52, aggro = 53, fight = 54 },
|
|
sensai = { beat = 60, success = 61, move = 62, aggro = 63, fight = 64 },
|
|
retry = 70,
|
|
failed = 80,
|
|
done = 90,
|
|
}
|
|
|
|
local feign_available = true;
|
|
|
|
local start_area = { min_x = -121, max_x = -65, min_y = -142, max_y = -40 }
|
|
local dojo_area = { min_x = -207, max_x = 18, min_y = -344, max_y = -146 }
|
|
local sensai_loc = { x = -92.875, y = -332.875, z = 253.5, h = 1.25 }
|
|
|
|
local state = states.none
|
|
local next_wait = 0 -- next state transition time
|
|
local attempt = 0 -- elements and emotions waves allow a second attempt
|
|
local respawned = {} -- waves that have been respawned (live only respawns once)
|
|
|
|
local function get_clients(area, gm_bypass)
|
|
local clients = {}
|
|
local has_gm = false
|
|
for client in eq.get_entity_list():GetClientList().entries do
|
|
local x, y = client:GetX(), client:GetY()
|
|
if client.valid and x >= area.min_x and x <= area.max_x and y >= area.min_y and y <= area.max_y then
|
|
clients[#clients+1] = client
|
|
if client:GetGM() then
|
|
if gm_bypass and not has_gm then
|
|
eq.debug(("Client %s with GM flag is in area, bypassing client count requirement"):format(client:GetName()))
|
|
end
|
|
has_gm = true
|
|
end
|
|
end
|
|
end
|
|
return clients, has_gm
|
|
end
|
|
|
|
local function is_wave_dead(wave_npcs)
|
|
for npc in eq.get_entity_list():GetNPCList().entries do
|
|
for _, wave_npc in pairs(wave_npcs) do
|
|
if npc.valid and not npc:IsCorpse() and npc:GetNPCTypeID() == wave_npc.id then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function move_npcs(wave_npcs)
|
|
eq.debug("moving npc wave")
|
|
for _, wave_npc in pairs(wave_npcs) do
|
|
local npc = eq.get_entity_list():GetNPCByNPCTypeID(wave_npc.id)
|
|
if npc.valid and wave_npc.grid then
|
|
npc:AssignWaypoints(wave_npc.grid)
|
|
elseif npc.valid and wave_npc.dst then
|
|
npc:MoveTo(wave_npc.dst.x, wave_npc.dst.y, wave_npc.dst.z, wave_npc.dst.h, true)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function make_aggro(npc)
|
|
npc:CastToNPC():SetSpecialAbility(SpecialAbility.immune_melee, 0)
|
|
npc:CastToNPC():SetSpecialAbility(SpecialAbility.immune_magic, 0)
|
|
npc:CastToNPC():SetSpecialAbility(SpecialAbility.immune_aggro, 0)
|
|
npc:CastToNPC():SetSpecialAbility(SpecialAbility.immune_aggro_on, 0)
|
|
npc:CastToNPC():SetSpecialAbility(SpecialAbility.no_harm_from_client, 0)
|
|
npc:CastToNPC():ModifyNPCStat("aggro", "200")
|
|
end
|
|
|
|
local function make_wave_aggro(wave_npcs)
|
|
eq.debug("enabling wave aggro")
|
|
for _, wave_npc in pairs(wave_npcs) do
|
|
local npc = eq.get_entity_list():GetNPCByNPCTypeID(wave_npc.id)
|
|
if npc.valid then
|
|
make_aggro(npc)
|
|
eq.set_timer("tick", 5 * 1000, npc)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function say_emote(npc, animate, msg)
|
|
eq.zone_emote(MT.SayEcho, msg)
|
|
if animate then
|
|
npc:DoAnim(111, 7) -- hand to ear animation (only occurs with some npc emotes)
|
|
end
|
|
end
|
|
|
|
local function signal_emote(wave_npc, animate, msg)
|
|
local npc = eq.get_entity_list():GetNPCByNPCTypeID(wave_npc.id)
|
|
if npc.valid then
|
|
say_emote(npc, animate, msg)
|
|
end
|
|
end
|
|
|
|
local function do_dojo_leash_check(npc, return_loc)
|
|
local x, y = npc:GetX(), npc:GetY()
|
|
if x < dojo_area.min_x or x > dojo_area.max_x or y < dojo_area.min_y or y > dojo_area.max_y then
|
|
npc:CastSpell(3230, npc:GetID()) -- Spell: Balance of the Nameless
|
|
npc:SpellFinished(3230, npc) -- double casts
|
|
if not return_loc then
|
|
npc:GotoBind()
|
|
else
|
|
npc:GMMove(return_loc.x, return_loc.y, return_loc.z, return_loc.h)
|
|
end
|
|
npc:WipeHateList()
|
|
end
|
|
end
|
|
|
|
-- depop and respawn a wave
|
|
local function reset_wave(wave_npcs, depop)
|
|
eq.debug("resetting wave, depopping: " .. tostring(depop))
|
|
if depop then
|
|
for _, wave_npc in pairs(wave_npcs) do
|
|
eq.depop_all(wave_npc.id)
|
|
end
|
|
end
|
|
|
|
-- live only respawns a wave once (seen via the retry bug)
|
|
if not respawned[wave_npcs] then
|
|
for _, wave_npc in pairs(wave_npcs) do
|
|
if depop or not eq.get_entity_list():IsMobSpawnedByNpcTypeID(wave_npc.id) then
|
|
eq.spawn_from_spawn2(wave_npc.spawn2)
|
|
end
|
|
end
|
|
respawned[wave_npcs] = true
|
|
end
|
|
end
|
|
|
|
-- sets new state for a wave failure
|
|
local function do_fail(allow_retry)
|
|
if allow_retry then
|
|
if attempt == 0 then
|
|
state = states.retry
|
|
attempt = attempt + 1
|
|
else
|
|
state = states.failed
|
|
end
|
|
elseif attempt == 0 or not enable_live_bug then
|
|
state = states.failed
|
|
else -- live bug, if attempt > 0 and not allow_retry (animals/steelsilk/sensai) state is reset
|
|
eq.debug("resetting state due to live bug")
|
|
state = states.start.move
|
|
end
|
|
end
|
|
|
|
local function sensei_spawn(e)
|
|
eq.set_timer("tick", 5 * 1000)
|
|
end
|
|
|
|
local function sensei_timer(e)
|
|
-- eq.debug(("[%s] state: %s next_wait time: %s"):format(os.date(), tostring(state), os.date("%X", next_wait)))
|
|
local now = os.time()
|
|
local wait_finished = now >= next_wait
|
|
|
|
if state == states.none and #get_clients(start_area) > 0 then
|
|
say_emote(e.self, true, "Storm Reach Sensei says, 'Welcome to the Storm Reach Dojo. I am pleased that you have taken up our humble challenge. As soon as you are ready, bring at least six of your fellows into the dojo and we shall begin. Be aware that this is a contest to the death. Do not enter the dojo unless you are prepared to pay that price.'")
|
|
state = states.start.move
|
|
elseif state == states.start.move then
|
|
e.self:MoveTo(sensai_loc.x, sensai_loc.y, sensai_loc.z, sensai_loc.h, true)
|
|
state = states.start.wait
|
|
elseif state == states.start.wait then
|
|
local clients, has_gm = get_clients(dojo_area, true)
|
|
if #clients >= 6 or has_gm then
|
|
say_emote(e.self, true, "The Storm Reach Sensei says, 'Good, you have decided to meet the challenge. I will not bore you with unworthy tales of our training methods or our meager accomplishments, certainly your own deeds far outshine our own. We are but meager servants to the Strom Mistress, Yar`Lir, and are of little consequence. We have been asked to test you and, certainly, ourselves in a battle to the death. The residents of this place are away at tasks beyond those such as we, and for a short while we have this room to ourselves. We must finish our contest before they return, and so there is little time for pleasantries.'")
|
|
state = states.start.begin
|
|
end
|
|
elseif state == states.start.begin then
|
|
say_emote(e.self, true, "The Storm Reach Sensei says, 'A winner will be declared if they are the only combatants alive in this room. Your first challenge will be to defeat the Disciples of The Five Elements. Certainly they will be easily defeated by such mighty beings, but it is within their hearts to try. Your battle will begin in one minute. Prepare yourselves.'")
|
|
e.self:SetAppearance(Appearance.Sitting) -- live will sit while still moving
|
|
state = states.elements.move
|
|
elseif state == states.elements.move then
|
|
move_npcs(waves.elements)
|
|
state = states.elements.chat
|
|
next_wait = now + 25
|
|
elseif state == states.elements.chat and wait_finished then
|
|
signal_emote(waves.elements.metal, true, "In a clear, ringing voice, The Disciple of Metal says, 'Please forgive my poor humor, but I am very eager to test your mettle.'")
|
|
signal_emote(waves.elements.earth, true, "The Disciple of Earth rumbles, 'When we have defeated you, I shall see that your corpses rot in the soil as is proper.'")
|
|
signal_emote(waves.elements.fire, true, "The Disciple of Fire shouts, 'I make no apologies. I shall burn your hearts out!'")
|
|
state = states.elements.reply
|
|
elseif state == states.elements.reply then
|
|
signal_emote(waves.elements.water, true, "The Disciple of Water says, 'Ah, yes. You must forgive Fire, for he cares little for pleasantries. It is part of his charm. You most certainly will be worthy opponents. I am honored to face you.'")
|
|
signal_emote(waves.elements.wood, true, "The Disciple of Wood says, 'May our efforts here earn favor with those that watch over us all.'")
|
|
state = states.elements.aggro
|
|
next_wait = now + 20
|
|
elseif state == states.elements.aggro and wait_finished then
|
|
make_wave_aggro(waves.elements)
|
|
state = states.elements.fight
|
|
elseif state == states.elements.fight then
|
|
local clients = get_clients(dojo_area)
|
|
local wave_dead = is_wave_dead(waves.elements)
|
|
if wave_dead and #clients > 0 then -- live pauses during wave fights if wave dead but no clients in dojo
|
|
say_emote(e.self, true, "The Storm Reach Sensei says, 'Congratulations! You have defeated the Disciples of the Five Elements. You have proven yourself worthy to face the Four Emotions. They have mastered their emotions and from them they gain strength. Each has become a conduit for the emotions of our kind, a paradigm, if you will. Certainly you shall defeat them, for you are the champions of your people and they are slaves. We shall begin again in two minutes. Rest and consult with each other, if you have need.'")
|
|
e.self:SetAppearance(Appearance.Sitting) -- if was still moving during start dialogue
|
|
state = states.emotions.move
|
|
elseif not wave_dead and #clients == 0 then
|
|
say_emote(e.self, true, "The Storm Reach Sensei says, 'I share your concern over your loss. Your failure shames me, for we have obviously taken advantage of you in some fashion. The glory from our victory is lessened by this shame. If we are lucky our mistress will smile upon the Storm Reach Dojo and the Elementals. Perhaps she will be in a kindly mood and will not fault us for the weaknesses of our opponents.'")
|
|
reset_wave(waves.elements, true)
|
|
do_fail(true)
|
|
end
|
|
elseif state == states.emotions.move then
|
|
move_npcs(waves.emotions)
|
|
state = states.emotions.rage
|
|
next_wait = now + eq.seconds("1m10s") -- emotes start 1m15s after message
|
|
elseif state == states.emotions.rage and wait_finished then
|
|
signal_emote(waves.emotions.rage, true, "The Master of Rage shouts, 'I hate you! I will rend you into bite-sized chunks of flesh and feed them to the puma!' Rage's face darkens with blood and flecks of spittle fly from his mouth as he pants and stares at his enemies.")
|
|
state = states.emotions.deceit
|
|
elseif state == states.emotions.deceit then
|
|
signal_emote(waves.emotions.deceit, true, "The Master of Deceit smiles broadly. 'You certainly will find Rage difficult to stop. I see it boiling up inside many of you. Allow it to take over, and Rage will ruin you.'")
|
|
state = states.emotions.doubt
|
|
elseif state == states.emotions.doubt then
|
|
signal_emote(waves.emotions.doubt, true, "The Master of Doubt mutters, 'I... You...' he sighs, 'I do not know what will happen next. I see the same misgiving in some of you. Could it be that you are like me? No, that seems unlikely.'")
|
|
state = states.emotions.content
|
|
elseif state == states.emotions.content then
|
|
signal_emote(waves.emotions.content, true, "The Master of Contentment drawls, 'Ah! I am pleased to meet you all. This is a fine challenge for us, is it not? I find this sort of exercise invigorating, though rarely do I find invigoration to be a good thing.'")
|
|
state = states.emotions.aggro
|
|
next_wait = now + 10
|
|
elseif state == states.emotions.aggro and wait_finished then
|
|
make_wave_aggro(waves.emotions)
|
|
state = states.emotions.fight
|
|
elseif state == states.emotions.fight then
|
|
local clients = get_clients(dojo_area)
|
|
local wave_dead = is_wave_dead(waves.emotions)
|
|
if wave_dead and #clients > 0 then
|
|
eq.zone_emote(MT.SayEcho, "The Storm Reach Sensei says, 'Well Done. It is rare that an opponent is able to defeat the goblin Emotions. Your next test will be greater, I fear. You must face our three Animals. These are grandmasters of our arts and have never suffered defeat against opponents outside of these grand islands of the sky. You look weary. You shall have three minutes to prepare for the next contest.")
|
|
state = states.animals.move
|
|
elseif not wave_dead and #clients == 0 then
|
|
eq.zone_emote(MT.SayEcho, "The Storm Reach Sensei says, 'This is unfortunate for you. Please tell your masters that the sensei of the Storm Reach Dojo considers you worthy opponents. Our Emotions are powerful and few can withstand them. Defeating the Elements is a worthy accomplishment. Perhaps this small honor will mollify them slightly and help you save face.'")
|
|
reset_wave(waves.emotions, true)
|
|
do_fail(true)
|
|
end
|
|
elseif state == states.animals.move then
|
|
move_npcs(waves.animals)
|
|
state = states.animals.aggro
|
|
next_wait = now + eq.seconds("2m30s")
|
|
elseif state == states.animals.aggro and wait_finished then
|
|
make_wave_aggro(waves.animals)
|
|
state = states.animals.fight
|
|
elseif state == states.animals.fight then
|
|
local clients = get_clients(dojo_area)
|
|
local wave_dead = is_wave_dead(waves.animals)
|
|
if wave_dead and #clients > 0 then
|
|
eq.zone_emote(MT.SayEcho, "The Storm Reach Sensei says, 'Amazing, simply amazing! Never before have our grandmasters been defeated by anyone other than your next opponents. Prepare yourself, for next you must face Silk and Steel. You have four minutes to prepare, use those minutes wisely.'")
|
|
state = states.steelsilk.move
|
|
elseif not wave_dead and #clients == 0 then
|
|
eq.zone_emote(MT.SayEcho, "The Storm Reach Sensei says, 'You can tell your masters that you tried you best. Our Animals are ferocious and cunning, defeating them was obviously beyond your ability.'")
|
|
reset_wave(waves.animals, true)
|
|
do_fail(false)
|
|
end
|
|
elseif state == states.steelsilk.move then
|
|
move_npcs(waves.steelsilk)
|
|
state = states.steelsilk.steel
|
|
next_wait = now + eq.seconds("3m") -- emotes start 3m5s after message
|
|
elseif state == states.steelsilk.steel and wait_finished then
|
|
signal_emote(waves.steelsilk.steel, false, "Steel looks at each of you in turn, his cold eyes taking in every detail. 'Individually, you are flawed. I see rust. I see cracks. Weakness is abundant in you. I shall exploit that weakness and shatter you one at a time.'")
|
|
state = states.steelsilk.silk
|
|
elseif state == states.steelsilk.silk then
|
|
signal_emote(waves.steelsilk.silk, false, "In a voice more fluent and soft than any you've heard a goblin use, Silk says, 'Forgive my brother. He has a keen mind but a blunt mouth. We look forward to this test of our skills. Thank you for honoring us by accepting the challenge.'")
|
|
state = states.steelsilk.aggro
|
|
next_wait = now + 15
|
|
elseif state == states.steelsilk.aggro and wait_finished then
|
|
make_wave_aggro(waves.steelsilk)
|
|
state = states.steelsilk.fight
|
|
elseif state == states.steelsilk.fight then
|
|
local clients = get_clients(dojo_area)
|
|
local wave_dead = is_wave_dead(waves.steelsilk)
|
|
if wave_dead and #clients > 0 then
|
|
eq.zone_emote(MT.SayEcho, "The Storm Reach Sensei says, 'Wonderful! You have defeated all of the students of the Storm Reach Dojo! Celebrate your victory. Remember this day.'")
|
|
e.self:SetAppearance(Appearance.Standing)
|
|
state = states.sensai.beat
|
|
next_wait = now + 25
|
|
elseif not wave_dead and #clients == 0 then
|
|
eq.zone_emote(MT.SayEcho, "The Storm Reach Sensei says, 'A valiant attempt. You should return to your homes with your heads held high. Silk and Steel have never been defeated as a team, except by one. Allow that to comfort you.'")
|
|
reset_wave(waves.steelsilk, false) -- respawns dead one but does not depop if still alive
|
|
do_fail(false)
|
|
end
|
|
elseif state == states.sensai.beat and wait_finished then
|
|
eq.zone_emote(MT.SayEcho, "The Storm Reach Sensei says, 'You have beaten all of my students, and have only had an advantage in numbers. Experience has taught me that such advantages usually lead to disaster caused by overconfidence or reliance on the lesser skills of others. Today you have proven that my experience is not always correct, a humbling moment.'")
|
|
state = states.sensai.success
|
|
next_wait = now + 25
|
|
elseif state == states.sensai.success and wait_finished then
|
|
eq.zone_emote(MT.SayEcho, "The Storm Reach Sensei says, 'You should return to those that sent you and tell them of your success. It will be a moment of shame for me to return to my mistress and explain the defeat of my students. I offer you my humble congratulations.'")
|
|
state = states.sensai.move
|
|
next_wait = now + 30
|
|
elseif state == states.sensai.move and wait_finished then
|
|
eq.zone_emote(MT.SayEcho, "The Storm Reach Sensei says, 'However, our challenge is not over. You were to defeat all of the members of the Storm Reach Dojo to prove your worth. You have indeed defeated all of the students of my humble dojo, but you have not defeated the master of this insignificant school. To complete the challenge, you must defeat the school's master.'")
|
|
e.self:MoveTo(-92.875, -324.0, 250.75, 0.0, true)
|
|
state = states.sensai.aggro
|
|
elseif state == states.sensai.aggro then
|
|
if #get_clients(dojo_area) == 0 then
|
|
do_fail(false)
|
|
else
|
|
eq.get_entity_list():MessageClose(e.self, true, 100, MT.SayEcho, "The Storm Reach Sensei summons his inner strength and begins to grow larger.")
|
|
e.self:ChangeSize(8)
|
|
make_aggro(e.self)
|
|
state = states.sensai.fight
|
|
end
|
|
elseif state == states.sensai.fight then
|
|
do_dojo_leash_check(e.self, sensai_loc)
|
|
if (attempt == 0 or not enable_live_bug) and #get_clients(dojo_area) == 0 then
|
|
do_fail(false) -- sensai does not reset if in live bug state, just remains attackable
|
|
end
|
|
elseif state == states.retry then
|
|
say_emote(e.self, true, "The Storm Reach Sensei says, 'How unfortunate. It appears that you were not prepared. You most certainly may return and begin again. It is the least a good host can do. I am, however, not that good of a host, you will be permitted only this last attempt to defeat the members of the humble Storm Reach Dojo, for must serve our masters soon.'")
|
|
state = states.start.move
|
|
elseif state == states.failed then
|
|
say_emote(e.self, true, "The Storm Reach Sensei says, 'Do not be discouraged. You have failed to defeat these humble slaves, this is true. You outnumbered your opponents and used mighty magical weapons while they used nothing but their bodies and minds. Please be consoled by the fact that we are the slaves of the great Yar`Lir, who is so mighty that even her servants are powerful. You have proven what our mistress believed, that your kind are weak and shall be destroyed. This was the inevitable conclusion.'")
|
|
do_dojo_leash_check(e.self, sensai_loc)
|
|
state = states.done
|
|
eq.end_dz_task() -- todo: live ends dz but not mission, touching switch outside starts a new dz (requires source changes)
|
|
end
|
|
end
|
|
|
|
local function enemy_timer(e)
|
|
do_dojo_leash_check(e.self)
|
|
local mobid = e.self:GetNPCTypeID();
|
|
|
|
if mobid == 340624 and e.timer == "buttlove" then -- Surprise Buttsecks
|
|
if math.random(1,100) >= 75 then -- Surprise Motherfucker
|
|
local random_hate = e.self:GetHateRandom():CastToClient();
|
|
|
|
if random_hate.valid and random_hate:IsClient() and not random_hate:GetGM() then
|
|
eq.get_entity_list():MessageClose(e.self, false, 120, MT.Yellow, e.self:GetCleanName() .. " vanishes and quickly reappears behind their prey.");
|
|
e.self:GMMove(random_hate:GetX(),random_hate:GetY(),random_hate:GetZ(),random_hate:GetHeading());
|
|
random_hate:Damage(e.self, math.random(5800,6200),39552,8); -- 5800-6200 Backstab Damage
|
|
end
|
|
end
|
|
elseif mobid == 340619 and e.timer == "monk_fd" then
|
|
if e.self:GetHPRatio() <= 35 and feign_available then -- fd
|
|
feign_available = false;
|
|
make_attackable(e.self, false);
|
|
e.self:WipeHateList();
|
|
e.self:SetAppearance(3); --lay down on floor
|
|
e.self:Emote(" hunches into a tight ball and become invulnerable to attacks.");
|
|
eq.set_timer("stand_up", 20 * 1000);
|
|
eq.set_timer("reset_fd", 90 * 1000);
|
|
end
|
|
|
|
eq.set_timer("monk_fd", 5 * 1000);
|
|
elseif mobid == 340619 and e.timer == "reset_fd" then
|
|
eq.stop_timer(e.timer);
|
|
feign_available = true;
|
|
elseif mobid == 340619 and e.timer == "stand_up" then
|
|
eq.stop_timer(e.timer);
|
|
make_attackable(e.self, true);
|
|
e.self:Emote(" uncurls and attacks.");
|
|
end
|
|
end
|
|
|
|
function make_attackable(mob, attackable)
|
|
mob:SetSpecialAbility(SpecialAbility.immune_melee, attackable and 0 or 1)
|
|
mob:SetSpecialAbility(SpecialAbility.immune_magic, attackable and 0 or 1)
|
|
mob:SetSpecialAbility(SpecialAbility.immune_aggro, attackable and 0 or 1)
|
|
mob:SetSpecialAbility(SpecialAbility.immune_aggro_on, attackable and 0 or 1)
|
|
mob:SetSpecialAbility(SpecialAbility.no_harm_from_client, attackable and 0 or 1)
|
|
end
|
|
|
|
function deceit_spawn(e)
|
|
eq.set_timer("buttlove", 6 * 1000); -- 6 Sec tick chance to Surprise someone
|
|
end
|
|
|
|
function beetle_spawn(e)
|
|
eq.set_timer("monk_fd", 5 * 1000);
|
|
end
|
|
|
|
function steel_spawn(e)
|
|
e.self:ModSkillDmgTaken(0, -60); -- 1h blunt
|
|
e.self:ModSkillDmgTaken(1, -60); -- 1h slashing
|
|
e.self:ModSkillDmgTaken(2, -60); -- 2h blunt
|
|
e.self:ModSkillDmgTaken(3, -60); -- 2h slashing
|
|
e.self:ModSkillDmgTaken(8, -60); -- backstab
|
|
e.self:ModSkillDmgTaken(10, -60); -- bash
|
|
e.self:ModSkillDmgTaken(21, -60); -- dragon punch
|
|
e.self:ModSkillDmgTaken(23, -60); -- eagle strike
|
|
e.self:ModSkillDmgTaken(26, -60); -- flying kick
|
|
e.self:ModSkillDmgTaken(28, -60); -- hand to hand
|
|
e.self:ModSkillDmgTaken(30, -60); -- kick
|
|
e.self:ModSkillDmgTaken(36, -60); -- piercing
|
|
e.self:ModSkillDmgTaken(38, -60); -- round kick
|
|
e.self:ModSkillDmgTaken(52, -60); -- tiger claw
|
|
e.self:ModSkillDmgTaken(74, -60); -- frenzy
|
|
end
|
|
|
|
function event_encounter_load(e)
|
|
eq.register_npc_event(Event.spawn, 340616, sensei_spawn) -- #Storm_Reach_Sensei
|
|
eq.register_npc_event(Event.timer, 340616, sensei_timer)
|
|
eq.register_npc_event(Event.spawn, 340624, deceit_spawn)
|
|
eq.register_npc_event(Event.spawn, 340619, beetle_spawn)
|
|
eq.register_npc_event(Event.spawn, 340618, steel_spawn)
|
|
|
|
for _, wave in pairs(waves) do
|
|
for _, npc in pairs(wave) do
|
|
eq.register_npc_event(Event.timer, npc.id, enemy_timer)
|
|
end
|
|
end
|
|
end
|