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());