diff --git a/scripts/npc/1052107.js b/scripts/npc/1052107.js index f7ea9439ac..b7e381f445 100644 --- a/scripts/npc/1052107.js +++ b/scripts/npc/1052107.js @@ -46,8 +46,12 @@ function action(mode, type, selection) { } if (status == 0) { - cm.sendOk("A small focus of light lighting in the immersive darkness."); - cm.dispose(); + cm.sendAcceptDecline("This is a small lamp with a switch. Would you like to turn it on?"); + return; + } else if (status == 1) { + cm.weakenAreaBoss(5090000, "You have turned the lamp on. Shade's strength will rapidly weaken due to the light."); } + + cm.dispose(); } } diff --git a/scripts/npc/2111025.js b/scripts/npc/2111025.js index 42ef71d0b5..709f4dfc60 100644 --- a/scripts/npc/2111025.js +++ b/scripts/npc/2111025.js @@ -24,7 +24,34 @@ Control Device */ +var status; + function start() { - cm.sendNext("This control device seems to be monitoring something..."); - cm.dispose(); + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && status == 0) { + cm.dispose(); + return; + } + if (mode == 1) { + status++; + } else { + status--; + } + + if (status == 0) { + cm.sendAcceptDecline("You can operate the automated security system using the control unit. Would you like to deactivate the automated security system?"); + return; + } else if (status == 1) { + cm.weakenAreaBoss(7090000, "The automated security system has been deactivated. The intruder alarm will shut down."); + } + + cm.dispose(); + } } \ No newline at end of file diff --git a/scripts/npc/2111026.js b/scripts/npc/2111026.js index 2a9297dda3..76ce5ef8ea 100644 --- a/scripts/npc/2111026.js +++ b/scripts/npc/2111026.js @@ -24,7 +24,34 @@ Incomplete Magic Square */ +var status; + function start() { - cm.sendNext("This chalkboard has some hard-founded studies annotated on it..."); - cm.dispose(); + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && status == 0) { + cm.dispose(); + return; + } + if (mode == 1) { + status++; + } else { + status--; + } + + if (status == 0) { + cm.sendAcceptDecline("This Magic Pentagram is incomplete. Would you like to finish off the drawing of the Magic Pentagram?"); + return; + } else if (status == 1) { + cm.weakenAreaBoss(8090000, "The Magic Pentagram has been completed. The spell to eliminate Deet and Roi has been summoned."); + } + + cm.dispose(); + } } \ No newline at end of file diff --git a/scripts/reactor/2008007.js b/scripts/reactor/2008007.js index a5d0169cd8..77b6951f4e 100644 --- a/scripts/reactor/2008007.js +++ b/scripts/reactor/2008007.js @@ -26,4 +26,6 @@ function hit() { var map = rm.getMap(); map.moveEnvironment("trap" + rm.getReactor().getName()[5], 1); -} \ No newline at end of file +} + +function act() {} \ No newline at end of file diff --git a/scripts/reactor/2119000.js b/scripts/reactor/2119000.js index 1349013a15..3c6f54cb2d 100644 --- a/scripts/reactor/2119000.js +++ b/scripts/reactor/2119000.js @@ -19,8 +19,18 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +/** + Tombstone in Forest of Dead Trees I + MSEA reference: http://mymapleland.blogspot.com/2009/09/kill-lich-at-forest-of-dead-trees-i-to.html +*/ function hit() { - rm.hitMonsterWithReactor(6090000, 14); + if (rm.getReactor().getState() !== 0) { + return + } + + rm.weakenAreaBoss(6090000, "As the tombstone lit up and vanished, Lich lost all his magic abilities.") } -function act() {} \ No newline at end of file +function act() { + // If the chest is destroyed before Riche, killing him should yield no exp +} \ No newline at end of file diff --git a/scripts/reactor/2119001.js b/scripts/reactor/2119001.js index 1349013a15..deb223366e 100644 --- a/scripts/reactor/2119001.js +++ b/scripts/reactor/2119001.js @@ -19,8 +19,18 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +/** + Tombstone in Forest of Dead Trees II + MSEA reference: http://mymapleland.blogspot.com/2009/09/kill-lich-at-forest-of-dead-trees-i-to.html +*/ function hit() { - rm.hitMonsterWithReactor(6090000, 14); + if (rm.getReactor().getState() !== 0) { + return + } + + rm.weakenAreaBoss(6090000, "As the tombstone lit up and vanished, Lich lost all his magic abilities.") } -function act() {} \ No newline at end of file +function act() { + // If the chest is destroyed before Riche, killing him should yield no exp +} \ No newline at end of file diff --git a/scripts/reactor/2119002.js b/scripts/reactor/2119002.js index 1349013a15..918902b8e9 100644 --- a/scripts/reactor/2119002.js +++ b/scripts/reactor/2119002.js @@ -19,8 +19,18 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +/** + Tombstone in Forest of Dead Trees III + MSEA reference: http://mymapleland.blogspot.com/2009/09/kill-lich-at-forest-of-dead-trees-i-to.html +*/ function hit() { - rm.hitMonsterWithReactor(6090000, 14); + if (rm.getReactor().getState() !== 0) { + return + } + + rm.weakenAreaBoss(6090000, "As the tombstone lit up and vanished, Lich lost all his magic abilities.") } -function act() {} \ No newline at end of file +function act() { + // If the chest is destroyed before Riche, killing him should yield no exp +} \ No newline at end of file diff --git a/scripts/reactor/2119003.js b/scripts/reactor/2119003.js index 1349013a15..9e5a5fb93e 100644 --- a/scripts/reactor/2119003.js +++ b/scripts/reactor/2119003.js @@ -19,8 +19,18 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +/** + Tombstone in Forest of Dead Trees IV + MSEA reference: http://mymapleland.blogspot.com/2009/09/kill-lich-at-forest-of-dead-trees-i-to.html +*/ function hit() { - rm.hitMonsterWithReactor(6090000, 14); + if (rm.getReactor().getState() !== 0) { + return + } + + rm.weakenAreaBoss(6090000, "As the tombstone lit up and vanished, Lich lost all his magic abilities.") } -function act() {} \ No newline at end of file +function act() { + // If the chest is destroyed before Riche, killing him should yield no exp +} \ No newline at end of file diff --git a/scripts/reactor/2119004.js b/scripts/reactor/2119004.js index ce19e0bf7f..44a6fb387e 100644 --- a/scripts/reactor/2119004.js +++ b/scripts/reactor/2119004.js @@ -19,7 +19,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -function hit() { - rm.hitMonsterWithReactor(6090001, 4); - rm.getReactor().setEventState(Math.floor(Math.random() * 3)); +function act() { + rm.weakenAreaBoss(6090001, "The light at the altar appeases the hatred of the Snow Witch. The force of the Witch has weakened."); } \ No newline at end of file diff --git a/scripts/reactor/2119005.js b/scripts/reactor/2119005.js index ce19e0bf7f..44a6fb387e 100644 --- a/scripts/reactor/2119005.js +++ b/scripts/reactor/2119005.js @@ -19,7 +19,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -function hit() { - rm.hitMonsterWithReactor(6090001, 4); - rm.getReactor().setEventState(Math.floor(Math.random() * 3)); +function act() { + rm.weakenAreaBoss(6090001, "The light at the altar appeases the hatred of the Snow Witch. The force of the Witch has weakened."); } \ No newline at end of file diff --git a/scripts/reactor/2119006.js b/scripts/reactor/2119006.js index ce19e0bf7f..44a6fb387e 100644 --- a/scripts/reactor/2119006.js +++ b/scripts/reactor/2119006.js @@ -19,7 +19,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -function hit() { - rm.hitMonsterWithReactor(6090001, 4); - rm.getReactor().setEventState(Math.floor(Math.random() * 3)); +function act() { + rm.weakenAreaBoss(6090001, "The light at the altar appeases the hatred of the Snow Witch. The force of the Witch has weakened."); } \ No newline at end of file diff --git a/scripts/reactor/2229009.js b/scripts/reactor/2229009.js index 999646a0e0..7e7e84d4f5 100644 --- a/scripts/reactor/2229009.js +++ b/scripts/reactor/2229009.js @@ -19,6 +19,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -function hit() { - rm.hitMonsterWithReactor(6090003, 1); +function act() { + rm.weakenAreaBoss(6090003, "The grieving Scholar Ghost has been slightly appeased. You may be able to defeat the Scholar Ghost."); } \ No newline at end of file diff --git a/scripts/reactor/2402007.js b/scripts/reactor/2402007.js index 1d1269b40d..cd845b7869 100644 --- a/scripts/reactor/2402007.js +++ b/scripts/reactor/2402007.js @@ -25,6 +25,6 @@ * Neo City Trash Can */ -function hit() { +function act() { rm.dropItems(true, 2, 5, 10, 1); } \ No newline at end of file diff --git a/scripts/reactor/2402008.js b/scripts/reactor/2402008.js index 1d1269b40d..cd845b7869 100644 --- a/scripts/reactor/2402008.js +++ b/scripts/reactor/2402008.js @@ -25,6 +25,6 @@ * Neo City Trash Can */ -function hit() { +function act() { rm.dropItems(true, 2, 5, 10, 1); } \ No newline at end of file diff --git a/scripts/reactor/2612004.js b/scripts/reactor/2612004.js index b5f582bbfe..234fe219a3 100644 --- a/scripts/reactor/2612004.js +++ b/scripts/reactor/2612004.js @@ -26,4 +26,6 @@ function hit() { rm.sprayItems(); -} \ No newline at end of file +} + +function act() {} \ No newline at end of file diff --git a/scripts/reactor/2612005.js b/scripts/reactor/2612005.js index e69ea6b47e..051fe801eb 100644 --- a/scripts/reactor/2612005.js +++ b/scripts/reactor/2612005.js @@ -2,8 +2,6 @@ Yulete's Lab: Making the Reagent */ -function hit() { - if (rm.getReactor().getState() == 4) { - rm.dropItems(); - } +function act() { + rm.dropItems(); } \ No newline at end of file diff --git a/scripts/reactor/2618000.js b/scripts/reactor/2618000.js index 19531a3d7b..46fbde88c4 100644 --- a/scripts/reactor/2618000.js +++ b/scripts/reactor/2618000.js @@ -40,4 +40,6 @@ function hit() { rm.getMap().getReactorByName(reactname).hitReactor(rm.getClient()); } } -} \ No newline at end of file +} + +function act() {} \ No newline at end of file diff --git a/scripts/reactor/2618001.js b/scripts/reactor/2618001.js index b916036c3b..c099df25ae 100644 --- a/scripts/reactor/2618001.js +++ b/scripts/reactor/2618001.js @@ -20,11 +20,11 @@ along with this program. If not, see . */ -/*2618000.js - MagatiaPQ Beaker +/*2618001.js - MagatiaPQ Door *@author Ronan */ -function hit() { +function act() { var eim = rm.getEventInstance(); var isAlcadno = eim.getIntProperty("isAlcadno"); diff --git a/scripts/reactor/2618002.js b/scripts/reactor/2618002.js index f1b7202b31..03a5d9b293 100644 --- a/scripts/reactor/2618002.js +++ b/scripts/reactor/2618002.js @@ -20,11 +20,11 @@ along with this program. If not, see . */ -/*2618000.js - MagatiaPQ Beaker +/*2618002.js - MagatiaPQ Door *@author Ronan */ -function hit() { +function act() { var eim = rm.getEventInstance(); var isAlcadno = eim.getIntProperty("isAlcadno"); diff --git a/scripts/reactor/2619000.js b/scripts/reactor/2619000.js index 07d5b0164b..f2f8701bf7 100644 --- a/scripts/reactor/2619000.js +++ b/scripts/reactor/2619000.js @@ -26,4 +26,9 @@ function hit() { rm.dropItems(); +} + +function act() { + // There's a timeout of 3 seconds to revert back from state 1 to 0. + // Reactor is destroyed (state 2) and triggers this if dropping two Magic Devices at once, which shouldn't really happen. } \ No newline at end of file diff --git a/scripts/reactor/2619003.js b/scripts/reactor/2619003.js new file mode 100644 index 0000000000..f9081e2a1b --- /dev/null +++ b/scripts/reactor/2619003.js @@ -0,0 +1,3 @@ +function act() { + rm.weakenAreaBoss(6090004, "Rurumo has been poisoned. It may finally be defeatable!"); +} \ No newline at end of file diff --git a/scripts/reactor/2619004.js b/scripts/reactor/2619004.js new file mode 100644 index 0000000000..f9081e2a1b --- /dev/null +++ b/scripts/reactor/2619004.js @@ -0,0 +1,3 @@ +function act() { + rm.weakenAreaBoss(6090004, "Rurumo has been poisoned. It may finally be defeatable!"); +} \ No newline at end of file diff --git a/scripts/reactor/2619005.js b/scripts/reactor/2619005.js new file mode 100644 index 0000000000..f9081e2a1b --- /dev/null +++ b/scripts/reactor/2619005.js @@ -0,0 +1,3 @@ +function act() { + rm.weakenAreaBoss(6090004, "Rurumo has been poisoned. It may finally be defeatable!"); +} \ No newline at end of file diff --git a/scripts/reactor/2708000.js b/scripts/reactor/2708000.js index eb1ea8a6c9..8a10c107a4 100644 --- a/scripts/reactor/2708000.js +++ b/scripts/reactor/2708000.js @@ -39,4 +39,6 @@ function hit() { //spawnJrBoss(mapObj.getMonsterById(8820023)); mapObj.killMonster(8820000); -} \ No newline at end of file +} + +function act() {} \ No newline at end of file diff --git a/src/main/java/client/command/CommandsExecutor.java b/src/main/java/client/command/CommandsExecutor.java index 6127052f90..cc35cce603 100644 --- a/src/main/java/client/command/CommandsExecutor.java +++ b/src/main/java/client/command/CommandsExecutor.java @@ -257,6 +257,7 @@ public class CommandsExecutor { addCommand("id", 2, IdCommand.class); addCommand("gachalist", GachaListCommand.class); addCommand("loot", LootCommand.class); + addCommand("mobskill", MobSkillCommand.class); commandsNameDesc.add(levelCommandsCursor); } diff --git a/src/main/java/client/command/commands/gm2/MobSkillCommand.java b/src/main/java/client/command/commands/gm2/MobSkillCommand.java new file mode 100644 index 0000000000..bba008aef7 --- /dev/null +++ b/src/main/java/client/command/commands/gm2/MobSkillCommand.java @@ -0,0 +1,32 @@ +package client.command.commands.gm2; + +import client.Character; +import client.Client; +import client.command.Command; +import server.life.MobSkill; +import server.life.MobSkillFactory; + +import java.util.Collections; + +public class MobSkillCommand extends Command { + { + setDescription("Apply a mob skill to all mobs on the map. Args: "); + } + + @Override + public void execute(Client client, String[] params) { + if (params.length < 2) { + throw new IllegalArgumentException("Mob skill command requires two args: mob skill id and level"); + } + + String skillId = params[0]; + String skillLevel = params[1]; + MobSkill mobSkill = MobSkillFactory.getMobSkill(Integer.parseInt(skillId), Integer.parseInt(skillLevel)); + if (mobSkill == null) { + throw new IllegalArgumentException("Mob skill not found. Id: %s, level: %s".formatted(skillId, skillLevel)); + } + + Character chr = client.getPlayer(); + chr.getMap().getAllMonsters().forEach(monster -> mobSkill.applyEffect(chr, monster, false, Collections.emptyList())); + } +} diff --git a/src/main/java/scripting/AbstractPlayerInteraction.java b/src/main/java/scripting/AbstractPlayerInteraction.java index 86f008b5eb..12a0f34f40 100644 --- a/src/main/java/scripting/AbstractPlayerInteraction.java +++ b/src/main/java/scripting/AbstractPlayerInteraction.java @@ -1185,4 +1185,30 @@ public class AbstractPlayerInteraction { public long getCurrentTime() { return Server.getInstance().getCurrentTime(); } + + public void weakenAreaBoss(int monsterId, String message) { + MapleMap map = c.getPlayer().getMap(); + Monster monster = map.getMonsterById(monsterId); + if (monster == null) { + return; + } + + applySealSkill(monster); + applyReduceAvoid(monster); + sendBlueNotice(map, message); + } + + private void applySealSkill(Monster monster) { + MobSkill sealSkill = MobSkillFactory.getMobSkill(157, 1); + sealSkill.applyEffect(monster); + } + + private void applyReduceAvoid(Monster monster) { + MobSkill reduceAvoidSkill = MobSkillFactory.getMobSkill(155, 2); + reduceAvoidSkill.applyEffect(monster); + } + + private void sendBlueNotice(MapleMap map, String message) { + map.dropMessage(6, message); + } } \ No newline at end of file diff --git a/src/main/java/scripting/reactor/ReactorActionManager.java b/src/main/java/scripting/reactor/ReactorActionManager.java index a2095bbcbe..528ba6c31e 100644 --- a/src/main/java/scripting/reactor/ReactorActionManager.java +++ b/src/main/java/scripting/reactor/ReactorActionManager.java @@ -26,7 +26,6 @@ import client.Client; import client.inventory.Equip; import client.inventory.InventoryType; import client.inventory.Item; -import config.YamlConfig; import constants.inventory.ItemConstants; import scripting.AbstractPlayerInteraction; import server.ItemInformationProvider; @@ -34,12 +33,10 @@ import server.TimerManager; import server.life.LifeFactory; import server.life.Monster; import server.maps.MapMonitor; -import server.maps.MapleMap; import server.maps.Reactor; import server.maps.ReactorDropEntry; import server.partyquest.CarnivalFactory; import server.partyquest.CarnivalFactory.MCSkill; -import tools.PacketCreator; import javax.script.Invocable; import java.awt.*; @@ -284,25 +281,6 @@ public class ReactorActionManager extends AbstractPlayerInteraction { spawnNpc(npcId, pos, reactor.getMap()); } - public void hitMonsterWithReactor(int id, int hitsToKill) { // until someone comes with a better solution, why not? - int customTime = YamlConfig.config.server.MOB_REACTOR_REFRESH_TIME; - if (customTime > 0) { - reactor.setDelay(customTime); - } - - MapleMap map = reactor.getMap(); - Monster mm = map.getMonsterById(id); - if (mm != null) { - int damage = (int) Math.ceil(mm.getMaxHp() / hitsToKill); - Character chr = this.getPlayer(); - - if (chr != null) { - map.damageMonster(chr, mm, damage); - map.broadcastMessage(PacketCreator.damageMonster(mm.getObjectId(), damage)); - } - } - } - public Reactor getReactor() { return reactor; } diff --git a/src/main/java/server/life/MobSkill.java b/src/main/java/server/life/MobSkill.java index 3d0dc67eaa..5cb85cc8c5 100644 --- a/src/main/java/server/life/MobSkill.java +++ b/src/main/java/server/life/MobSkill.java @@ -122,6 +122,10 @@ public class MobSkill { service.registerOverallAction(monster.getMap().getId(), toRun, animationTime); } + public void applyEffect(Monster monster) { + applyEffect(null, monster, false, Collections.emptyList()); + } + public void applyEffect(Character player, Monster monster, boolean skill, List banishPlayers) { Disease disease = null; Map stats = new ArrayMap<>(); @@ -243,6 +247,9 @@ public class MobSkill { case 156: stats.put(MonsterStatus.SPEED, x); break; + case 157: + stats.put(MonsterStatus.SEAL_SKILL, x); + break; case 200: // summon int skillLimit = this.getLimit(); MapleMap map = monster.getMap(); diff --git a/src/main/java/server/life/Monster.java b/src/main/java/server/life/Monster.java index 25de54c4be..a494c01915 100644 --- a/src/main/java/server/life/Monster.java +++ b/src/main/java/server/life/Monster.java @@ -1448,7 +1448,7 @@ public class Monster extends AbstractLoadedLife { } public boolean canUseSkill(MobSkill toUse, boolean apply) { - if (toUse == null) { + if (toUse == null || isBuffed(MonsterStatus.SEAL_SKILL)) { return false; } diff --git a/src/main/java/server/maps/Reactor.java b/src/main/java/server/maps/Reactor.java index d3c73dbc53..2b07d72b4d 100644 --- a/src/main/java/server/maps/Reactor.java +++ b/src/main/java/server/maps/Reactor.java @@ -270,8 +270,11 @@ public class Reactor extends AbstractMapObject { continue; } } - state = stats.getNextState(state, b); - if (stats.getNextState(state, b) == -1) {//end of reactor + + this.state = stats.getNextState(state, b); + byte nextState = stats.getNextState(state, b); + boolean isInEndState = nextState < this.state; + if (isInEndState) {//end of reactor if (reactorType < 100) {//reactor broken if (delay > 0) { map.destroyReactor(getObjectId());