From c06d209680700a749f66d7ce623d9d90e015a574 Mon Sep 17 00:00:00 2001 From: Skold177 <113406182+Skold177@users.noreply.github.com> Date: Mon, 2 Feb 2026 00:48:58 -0500 Subject: [PATCH] [lua] Uninvited Guests, Mammets Cleanup Fixes the logic to summon additional mammets and unifies code structure for mammets across the code base. --- .../Monarch_Linn/uninvited_guests.lua | 1 - .../Monarch_Linn/mobs/Mammet-19_Epsilon.lua | 73 +++-- .../zones/Monarch_Linn/mobs/Mammet-800.lua | 261 ++++++++---------- .../Sealions_Den/mobs/Mammet-22_Zeta.lua | 7 +- 4 files changed, 149 insertions(+), 193 deletions(-) diff --git a/scripts/battlefields/Monarch_Linn/uninvited_guests.lua b/scripts/battlefields/Monarch_Linn/uninvited_guests.lua index 54de8cd2bfa..d9f654601e8 100644 --- a/scripts/battlefields/Monarch_Linn/uninvited_guests.lua +++ b/scripts/battlefields/Monarch_Linn/uninvited_guests.lua @@ -20,7 +20,6 @@ local content = BattlefieldQuest:new({ quest = xi.quest.id.otherAreas.UNINVITED_GUESTS, requiredVar = 'Quest[4][81]Prog', requiredValue = 1, - experimental = true, }) content.groups = diff --git a/scripts/zones/Monarch_Linn/mobs/Mammet-19_Epsilon.lua b/scripts/zones/Monarch_Linn/mobs/Mammet-19_Epsilon.lua index 279a10dbbaa..9ad3cab7684 100644 --- a/scripts/zones/Monarch_Linn/mobs/Mammet-19_Epsilon.lua +++ b/scripts/zones/Monarch_Linn/mobs/Mammet-19_Epsilon.lua @@ -15,22 +15,29 @@ local forms = local tpMoves = { - -- NOTE: Mammets always have access to: - -- Transmogrification: Absorbs all physical damage for ~30 seconds - -- Tremorous Tread: Low AoE damage with a Stun effect, absorbed by Utsusemi. - [0] = { 487, 540 }, - -- Sword Skills: - -- Velocious Blade: 5-hit attack, high da - -- Sonic Blade: High AoE damage. - -- Scission Thrust: Low conal AoE damage. - [1] = { 347, 419, 422 }, - -- Percussive Foin: Medium directional AoE damage. - -- Gravity Wheel: High AoE damage and Gravity. - -- Microquake: High single-target damage. - [2] = { 441, 447, 457 }, - -- Psychomancy: AoE Aspir, drains 80+ MP. - -- Mind Wall: Gives the Mammet a special Magic Shield effect causing it to absorb offensive magic used against it for ~30 seconds. - [3] = { 464, 471 }, + + [forms.UNARMED] = + { + xi.mobSkill.TRANSMOGRIFICATION, + xi.mobSkill.TREMOROUS_TREAD, + }, + [forms.SWORD] = + { + xi.mobSkill.VELOCIOUS_BLADE, + xi.mobSkill.SONIC_BLADE, + xi.mobSkill.SCISSION_THRUST, + }, + [forms.POLEARM] = + { + xi.mobSkill.PERCUSSIVE_FOIN, + xi.mobSkill.GRAVITY_WHEEL, + xi.mobSkill.MICROQUAKE, + }, + [forms.STAFF] = + { + xi.mobSkill.PSYCHOMANCY, + xi.mobSkill.MIND_WALL, + }, } entity.onMobInitialize = function(mob) @@ -44,13 +51,16 @@ entity.onMobSpawn = function(mob) end entity.onMobFight = function(mob, target) - -- Chages forms after 30-60 seconds randomly + -- Chages forms after 25-60 seconds randomly local timeTracker = mob:getLocalVar('formTimeTracker') local currentTime = mob:getBattleTime() -- NOTE: Yellow Liquid applies xi.effect.FOOD to the Mammets local cannotChangeForm = mob:hasStatusEffect(xi.effect.FOOD) - if currentTime >= timeTracker and not cannotChangeForm then + if + currentTime >= timeTracker and + not cannotChangeForm + then -- Pick a new form -- local rand = math.random(0, 3) mob:setAnimationSub(rand) @@ -86,31 +96,10 @@ entity.onMobFight = function(mob, target) end entity.onMobMobskillChoose = function(mob, target, skillId) - switch (mob:getAnimationSub()): caseof - { - [forms.UNARMED] = function() - local wsChoice = math.random(1, 2) - return tpMoves[forms.UNARMED][wsChoice] - end, - - [forms.SWORD] = function() - local wsChoice = math.random(1, 3) - return tpMoves[forms.SWORD][wsChoice] - end, - - [forms.POLEARM] = function() - local wsChoice = math.random(1, 3) - return tpMoves[forms.POLEARM][wsChoice] - end, - - [forms.STAFF] = function() - local wsChoice = math.random(1, 2) - return tpMoves[forms.STAFF][wsChoice] - end, - } -end + local form = mob:getAnimationSub() + local moves = tpMoves[form] -entity.onMobDeath = function(mob, player, optParams) + return moves[math.random(1, #moves)] end return entity diff --git a/scripts/zones/Monarch_Linn/mobs/Mammet-800.lua b/scripts/zones/Monarch_Linn/mobs/Mammet-800.lua index 4f9a82854c6..8027016f849 100644 --- a/scripts/zones/Monarch_Linn/mobs/Mammet-800.lua +++ b/scripts/zones/Monarch_Linn/mobs/Mammet-800.lua @@ -7,11 +7,6 @@ local monarchLinnID = zones[xi.zone.MONARCH_LINN] ---@type TMobEntity local entity = {} --- TODO: Get staff delay. --- TODO: Tweak attack speeds. --- TODO: Investigate form damage. --- TODO: Get Ninjutsu and Song resistances. - -- Additional battlefield Mammets will be added to keep track for win conditions for each battlefield. local battlefieldMammets = { @@ -20,10 +15,7 @@ local battlefieldMammets = [ monarchLinnID.mob.MAMMET_800 + 20 ] = { }, } --- Run time TP move container. -local tpMoves = { } - -local modes = +local forms = { UNARMED = 0, SWORD = 1, @@ -31,130 +23,65 @@ local modes = STAFF = 3, } -local modeData = +local tpMoves = { - [modes.UNARMED] = + + [forms.UNARMED] = { - tpMoves = - { - xi.mobSkill.TRANSMOGRIFICATION, - xi.mobSkill.TREMOROUS_TREAD, - }, - isCaster = false, - delay = 3040, - damage = 40, + xi.mobSkill.TRANSMOGRIFICATION, + xi.mobSkill.TREMOROUS_TREAD, }, - - [modes.SWORD] = + [forms.SWORD] = { - tpMoves = - { - xi.mobSkill.VELOCIOUS_BLADE, - xi.mobSkill.SCISSION_THRUST, - xi.mobSkill.SONIC_BLADE, - }, - isCaster = false, - delay = 2090, - damage = 40, + xi.mobSkill.VELOCIOUS_BLADE, + xi.mobSkill.SONIC_BLADE, + xi.mobSkill.SCISSION_THRUST, }, - - [modes.POLEARM] = + [forms.POLEARM] = { - tpMoves = - { - xi.mobSkill.MICROQUAKE, - xi.mobSkill.PERCUSSIVE_FOIN, - xi.mobSkill.GRAVITY_WHEEL, - }, - isCaster = false, - delay = 5060, - damage = 75, + xi.mobSkill.PERCUSSIVE_FOIN, + xi.mobSkill.GRAVITY_WHEEL, + xi.mobSkill.MICROQUAKE, }, - - [modes.STAFF] = + [forms.STAFF] = { - tpMoves = - { - xi.mobSkill.PSYCHOMANCY, - xi.mobSkill.MIND_WALL, - }, - isCaster = true, - delay = 4100, - damage = 40, + xi.mobSkill.PSYCHOMANCY, + xi.mobSkill.MIND_WALL, }, } ----------------------------------- --- Switches the mammet to a new battle mode. +-- Spawns additional mammets upon engagement based on player count. ----------------------------------- -local modeSwitch = function(mob, newMode) - -- Avoid the same mode twice in a row. - if not newMode then - local currentMode = mob:getAnimationSub() - - repeat - newMode = math.random(modes.UNARMED, modes.STAFF) - until newMode ~= currentMode +local function spawnAdditionalMammets(mob, target) + local parentID = mob:getID() + local battlefield = mob:getBattlefield() + if not battlefield or not battlefieldMammets[parentID] then + return end - local details = modeData[newMode] - tpMoves = { } + local players = battlefield:getPlayers() + local spawnCount = math.floor((#players - 1) / 2) - -- Add base Mammet move set. - for _, skillID in ipairs(modeData[modes.UNARMED].tpMoves) do - table.insert(tpMoves, skillID) - end + mob:setLocalVar('parentMammet', parentID) + battlefieldMammets[parentID][parentID] = true - -- Add weapon move set. - if newMode ~= modes.UNARMED then - for _, skillID in ipairs(details.tpMoves) do - table.insert(tpMoves, skillID) - end + if spawnCount < 1 then + return end - mob:setAnimationSub(newMode) - mob:setMagicCastingEnabled(details.isCaster) - mob:setDelay(details.delay) - mob:setDamage(details.damage) -end - ------------------------------------ --- Spawns additional mammets upon engagement based on player count. ------------------------------------ -local spawnAdditionalMammets = function(mob, target) - local parentID = mob:getID() - - if battlefieldMammets[parentID] then - local players = mob:getBattlefield():getPlayers() + for i = 1, spawnCount do + local newMobID = parentID + i + local newMob = SpawnMob(newMobID) - mob:setLocalVar('parentMammet', parentID) - battlefieldMammets[parentID][mob:getID()] = true - - for i = 3, #players, 2 do - if i >= 19 then - break - end - - local newMobID = parentID + i - local newMob = SpawnMob(newMobID) - - if newMob then - newMob:setLocalVar('parentMammet', parentID) - battlefieldMammets[parentID][newMobID] = true - newMob:updateEnmity(target) - end + if newMob then + newMob:setLocalVar('parentMammet', parentID) + battlefieldMammets[parentID][newMobID] = true + newMob:updateEnmity(target) end end end ------------------------------------ --- Sets the next form switch time. ------------------------------------ -local scheduleModeSwitch = function(mob, time) - time = time or mob:getBattleTime() - mob:setLocalVar('nextFormTime', time + math.random(50, 60)) -end - ----------------------------------- -- Initial immunities. ----------------------------------- @@ -173,8 +100,6 @@ end -- Will not move or auto-attack for 8 seconds after engagement. ----------------------------------- entity.onMobSpawn = function(mob) - modeSwitch(mob, modes.UNARMED) - mob:setDelay(4070) mob:setLocalVar('engageDelay', 0) mob:setMobMod(xi.mobMod.NO_MOVE, 1) mob:setAutoAttackEnabled(false) @@ -186,34 +111,58 @@ end -- The first mammet will spawn additional mammets based on the number of players. ----------------------------------- entity.onMobEngage = function(mob, target) - mob:setLocalVar('engageDelay', mob:getBattleTime() + 8) + mob:setLocalVar('engageDelay', mob:getBattleTime() + 5) spawnAdditionalMammets(mob, target) - scheduleModeSwitch(mob) end ------------------------------------ --- Form switch controller. ------------------------------------ entity.onMobFight = function(mob, target) + -- Only enable movement and auto-attack after the initial delay. + if mob:getBattleTime() >= mob:getLocalVar('engageDelay') then + mob:setMobMod(xi.mobMod.NO_MOVE, 0) + mob:setAutoAttackEnabled(true) + end + + -- Changes forms after 15-60 seconds randomly + local timeTracker = mob:getLocalVar('formTimeTracker') local currentTime = mob:getBattleTime() - local engageDelay = mob:getLocalVar('engageDelay') + -- NOTE: Yellow Liquid applies xi.effect.FOOD to the Mammets + local cannotChangeForm = mob:hasStatusEffect(xi.effect.FOOD) if - currentTime >= mob:getLocalVar('nextFormTime') and - mob:canUseAbilities() and - not mob:hasStatusEffect(xi.effect.FOOD) -- Yellow Liquid + currentTime >= timeTracker and + not cannotChangeForm then - modeSwitch(mob) - scheduleModeSwitch(mob, currentTime) - end - - -- Initial battle engagement delay. - if engageDelay ~= 0 and currentTime >= engageDelay then - mob:setLocalVar('engageDelay', 0) - mob:setMobMod(xi.mobMod.NO_MOVE, 0) - mob:setAutoAttackEnabled(true) - elseif engageDelay ~= 0 then - mob:setMobMod(xi.mobMod.NO_MOVE, 1) + -- Pick a new form -- + local rand = math.random(0, 3) + mob:setAnimationSub(rand) + switch (rand): caseof + { + [forms.UNARMED] = function() + mob:setMagicCastingEnabled(false) + mob:setDelay(2400) + mob:setMobMod(xi.mobMod.BASE_DAMAGE_MULTIPLIER, 150) + end, + + [forms.SWORD] = function() + mob:setMagicCastingEnabled(false) + mob:setDelay(1200) + mob:setMobMod(xi.mobMod.BASE_DAMAGE_MULTIPLIER, 150) + end, + + [forms.POLEARM] = function() + mob:setMagicCastingEnabled(false) + mob:setDelay(3000) + mob:setMobMod(xi.mobMod.BASE_DAMAGE_MULTIPLIER, 200) + end, + + [forms.STAFF] = function() + mob:setMobMod(xi.mobMod.MAGIC_COOL, 20) + mob:setMagicCastingEnabled(true) + mob:setDelay(2400) + mob:setMobMod(xi.mobMod.BASE_DAMAGE_MULTIPLIER, 100) + end, + } + mob:setLocalVar('formTimeTracker', mob:getBattleTime() + math.random(15, 60)) end end @@ -221,35 +170,51 @@ end -- Select TP moves based on current form. ----------------------------------- entity.onMobMobskillChoose = function(mob, target, skillId) - local selectedMove = math.random(1, #tpMoves) + local form = mob:getAnimationSub() + local moves = tpMoves[form] + + return moves[math.random(1, #moves)] +end - return tpMoves[selectedMove] +entity.onMobSpellChoose = function(mob, target, spellId) + local spellList = + { + xi.magic.spell.STONEGA_III, + xi.magic.spell.WATERGA_III, + xi.magic.spell.AEROGA_III, + xi.magic.spell.FIRAGA_III, + xi.magic.spell.BLIZZAGA_III, + xi.magic.spell.THUNDAGA_III, + } + + return spellList[math.random(#spellList)] end ----------------------------------- -- Check that all of the mobs from the battlefield have been defeated. ----------------------------------- entity.onMobDeath = function(mob, player, optParams) - local parentMammet = mob:getLocalVar('parentMammet') - local battlefieldGroup = battlefieldMammets[parentMammet] - - if not battlefieldGroup then - return - end + if optParams.isKiller or optParams.noKiller then + local parentMammet = mob:getLocalVar('parentMammet') + local battlefieldGroup = battlefieldMammets[parentMammet] - for id, _ in pairs(battlefieldGroup) do - local checkMob = GetMobByID(id) - - if checkMob and checkMob:isAlive() then + if not battlefieldGroup then return end - end - local battlefield = mob:getBattlefield() - battlefieldMammets[parentMammet] = { } + for id, _ in pairs(battlefieldGroup) do + local checkMob = GetMobByID(id) + if checkMob and checkMob:isAlive() then + return + end + end + + local battlefield = mob:getBattlefield() + battlefieldMammets[parentMammet] = { } - if battlefield then - battlefield:setStatus(xi.battlefield.status.WON) + if battlefield then + battlefield:setStatus(xi.battlefield.status.WON) + end end end diff --git a/scripts/zones/Sealions_Den/mobs/Mammet-22_Zeta.lua b/scripts/zones/Sealions_Den/mobs/Mammet-22_Zeta.lua index 519d4436834..8721e368fe3 100644 --- a/scripts/zones/Sealions_Den/mobs/Mammet-22_Zeta.lua +++ b/scripts/zones/Sealions_Den/mobs/Mammet-22_Zeta.lua @@ -55,13 +55,16 @@ entity.onMobEngage = function(mob, target) end entity.onMobFight = function(mob, target) - -- Chages forms after 30-60 seconds randomly + -- Changes forms after 25-60 seconds randomly local timeTracker = mob:getLocalVar('formTimeTracker') local currentTime = GetSystemTime() -- NOTE: Yellow Liquid applies xi.effect.FOOD to the Mammets local cannotChangeForm = mob:hasStatusEffect(xi.effect.FOOD) - if currentTime >= timeTracker and not cannotChangeForm then + if + currentTime >= timeTracker and + not cannotChangeForm + then -- Pick a new form -- local rand = math.random(0, 3) mob:setAnimationSub(rand)