Skip to content

Commit c052573

Browse files
authored
Merge pull request #2 from g9502995/refactor-area-boss-clean-2536730128243536058
Refactor Area Boss to detach Map from EIM and use direct MonsterListener
2 parents 8a766a7 + f0278fa commit c052573

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+9222
-3701
lines changed

boss_scripts.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
gms-server/scripts/event/AreaBossBamboo.js
2+
gms-server/scripts/event/AreaBossCentipede.js
3+
gms-server/scripts/event/AreaBossDeo.js
4+
gms-server/scripts/event/AreaBossDoor1.js
5+
gms-server/scripts/event/AreaBossDoor2.js
6+
gms-server/scripts/event/AreaBossDoor3.js
7+
gms-server/scripts/event/AreaBossDoor4.js
8+
gms-server/scripts/event/AreaBossDoor5.js
9+
gms-server/scripts/event/AreaBossDoor6.js
10+
gms-server/scripts/event/AreaBossDyle.js
11+
gms-server/scripts/event/AreaBossEliza1.js
12+
gms-server/scripts/event/AreaBossFaust1.js
13+
gms-server/scripts/event/AreaBossFaust2.js
14+
gms-server/scripts/event/AreaBossKimera.js
15+
gms-server/scripts/event/AreaBossKingClang.js
16+
gms-server/scripts/event/AreaBossKingSageCat.js
17+
gms-server/scripts/event/AreaBossLeviathan.js
18+
gms-server/scripts/event/AreaBossMano.js
19+
gms-server/scripts/event/AreaBossNineTailedFox.js
20+
gms-server/scripts/event/AreaBossSeruf.js
21+
gms-server/scripts/event/AreaBossSnackBar.js
22+
gms-server/scripts/event/AreaBossStumpy.js
23+
gms-server/scripts/event/AreaBossTaeRoon.js
24+
gms-server/scripts/event/AreaBossTimer1.js
25+
gms-server/scripts/event/AreaBossTimer2.js
26+
gms-server/scripts/event/AreaBossTimer3.js
27+
gms-server/scripts/event/AreaBossZeno.js
Lines changed: 163 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,205 @@
11
/*
2-
This file is part of the OdinMS Maple Story Server
3-
Copyright (C) 2008 Patrick Huy <[email protected]>
4-
Matthias Butz <[email protected]>
5-
Jan Christian Meyer <[email protected]>
6-
7-
This program is free software: you can redistribute it and/or modify
8-
it under the terms of the GNU Affero General Public License as
9-
published by the Free Software Foundation version 3 as published by
10-
the Free Software Foundation. You may not use, modify or distribute
11-
this program under any other version of the GNU Affero General Public
12-
License.
13-
14-
This program is distributed in the hope that it will be useful,
15-
but WITHOUT ANY WARRANTY; without even the implied warranty of
16-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17-
GNU Affero General Public License for more details.
18-
19-
You should have received a copy of the GNU Affero General Public License
20-
along with this program. If not, see <http://www.gnu.org/licenses/>.
21-
*/
22-
23-
const Point = Java.type('java.awt.Point');
2+
* 通用野外BOSS事件脚本模板 (Standalone Area Boss Script Template)
3+
*
4+
* 功能:
5+
* 1. 自动生成指定 BOSS。
6+
* 2. 监听 BOSS 死亡事件。
7+
* 3. 死亡后依指定时间准确重置刷新。
8+
* 4. [新增] 只有当玩家进入地图且冷却时间已到时才生成 (Lazy Spawn)。
9+
*
10+
* 使用说明:
11+
* - 自动更新脚本生成
12+
*/
13+
14+
// ============================================================================
15+
// 配置区域 (Configuration)
16+
// ============================================================================
17+
18+
// 地图 ID (BOSS 生成的地图)
19+
const MapID = 800020120;
20+
21+
// BOSS ID (要生成的怪物 ID)
22+
const BossID = 6090002;
23+
24+
// BOSS 名称 (默认名称,若数据库有读取到会自动更新)
25+
var BossName = "青竹武士";
26+
27+
// 刷新冷却时间 (单位:分钟)
28+
const BossTime = 180;
29+
30+
// BOSS 生成坐标 (X, Y)
31+
const SpawnPoint = new java.awt.Point(Math.floor((Math.random() * 100) + 600), 50);
32+
33+
// BOSS 出现时的全服公告内容
34+
const BossNotice = "青岚自氤氲雾气笼罩的断垣残壁间,骤然现形!";
35+
36+
// ============================================================================
37+
// 核心逻辑 (Core Logic)
38+
// ============================================================================
39+
2440
const LifeFactory = Java.type('org.gms.server.life.LifeFactory');
2541
const PacketCreator = Java.type('org.gms.util.PacketCreator');
2642
const LoggerFactory = Java.type('org.slf4j.LoggerFactory');
43+
2744
var log = null;
2845
var channel = null;
29-
var isinit = false;
3046

31-
var MapID = 800020120;
32-
var BossID = 6090002;
33-
var BossName = "青竹武士";
34-
/**刷新时间,分钟; Generation time in minutes*/
35-
var BossTime = 180;
36-
/**指定Boss刷新的XY坐标位置; Specify the XY coordinate position for Boss refresh*/
37-
var point = new Point(Math.floor((Math.random() * 100) + 600), 50);
38-
var BossNotice= "青岚自氤氲雾气笼罩的断垣残壁间,骤然现形!";
47+
// 事件实例名称 (自动获取,无需修改)
48+
const EventName = "AreaBossBamboo";
3949

40-
const methodName = "start"; //指定当前事件刷新Boss的函数,无需改动
41-
/**
42-
-- Odin JavaScript --------------------------------------------------------------------------------
43-
Zeno Spawner
44-
-- Edited by --------------------------------------------------------------------------------------
45-
ThreeStep - based on xQuasar's King Clang spawner
46-
**/
4750
function init() {
4851
channel = em.getChannelServer().getId();
4952
log = LoggerFactory.getLogger(em.getName());
50-
51-
scheduleNew();
53+
em.registerMapListener(MapID);
54+
scheduleNew(); // [新系统] 注册地图监听器,避免绑定玩家 EIM
5255
}
5356

5457
function scheduleNew() {
55-
setupTask = em.schedule(methodName, 0); //服务器启动时生成。每指定时间,服务器事件会检查boss是否存在,如果不存在,会立即生成boss。
58+
setupEventInstance();
59+
60+
var eim = getOrCreateEventInstance();
61+
// 标记:冷却已就绪 (服务器重启默认为就绪)
62+
eim.setProperty("canSpawn", "true");
63+
64+
// 关键修正:必须启动事件,monsterKilled 等监听器才会生效
65+
try { eim.startEvent(); } catch (e) { }
66+
67+
// 尝试生成
68+
// 如果地图已经有人,立即生成
69+
// 如果地图没人,不做任何事,等待玩家进入
70+
checkAndSpawn(eim);
5671
}
5772

5873
function cancelSchedule() {
59-
if (setupTask != null) {
60-
setupTask.cancel(true);
74+
var eim = em.getInstance(EventName);
75+
if (eim != null) {
76+
eim.dispose();
6177
}
6278
}
6379

64-
function start() {
65-
var graysPrairie = em.getChannelServer().getMapFactory().getMap(MapID);
66-
var Timer = em.getBossTime(BossTime * 60 * 1000); //转为毫秒并加载时间倍率修正
80+
// 辅助函数:获取或创建实例
81+
function getOrCreateEventInstance() {
82+
var eim = em.getInstance(EventName);
83+
if (eim == null) {
84+
try {
85+
eim = em.newInstance(EventName);
86+
} catch (e) {
87+
eim = em.getInstance(EventName);
88+
}
89+
}
90+
return eim;
91+
}
92+
93+
function setupEventInstance() {
94+
var eim = getOrCreateEventInstance();
95+
var map = em.getChannelServer().getMapFactory().getMap(MapID);
96+
// 绑定地图到EIM
97+
map.setEventInstance(eim);
98+
}
99+
100+
// 核心监测函数:替代 playerEntry 的轮询机制
101+
function checkAndSpawn(eim) {
102+
if (eim == null) return;
67103

68-
if (graysPrairie.getMonsterById(BossID) != null) {
69-
em.schedule(methodName, Timer);
104+
var canSpawn = eim.getProperty("canSpawn");
105+
// 如果不能刷(已经刷了,或正在CD),直接停止监测
106+
if (canSpawn != "true") {
70107
return;
71108
}
72-
const BossObj = LifeFactory.getMonster(BossID);
73-
BossName = BossObj.getName() || BossName;
109+
110+
var map = em.getChannelServer().getMapFactory().getMap(MapID);
111+
112+
// 1. 检查人数
113+
if (map.getCharacters().size() > 0) {
114+
// 有人,嘗試生成
115+
spawnBoss(eim, map);
116+
}
117+
}
118+
119+
120+
function spawnBoss(eim, map) {
74121
try {
75-
graysPrairie.spawnMonsterOnGroundBelow(BossObj, point);
76-
if(isinit) {
77-
log.info(`[事件脚本-野外BOSS] ${em.getName()} 已在频道 ${channel}${graysPrairie.getMapName()}(${MapID}) ${point.x} , ${point.y}) 生成 ${BossName}(${BossID}),检测间隔:${Timer / 60 / 1000} 分钟`);
78-
} else {
79-
isinit = true;
122+
// 双重保险:检查 BOSS 是否已存在
123+
if (map.getMonsterById(BossID) != null) {
124+
eim.setProperty("canSpawn", "false");
125+
return;
80126
}
81-
} catch (e) {
82-
console.error(`[事件脚本-野外BOSS] ${em.getName()} 在频道 ${channel}${graysPrairie.getMapName()}(${MapID}) ${point.x} , ${point.y}) 生成 ${BossName}(${BossID}) 时出错`,e);
83-
}
84-
graysPrairie.broadcastMessage(PacketCreator.serverNotice(6, `[野外BOSS] ${BossName} ${BossNotice}`)); //聊天框输出当前地图范围的Boss登场消息
85127

86-
em.schedule(methodName, Timer);
87-
}
128+
// 准备 BOSS 对象
129+
var bossMob = LifeFactory.getMonster(BossID);
130+
if (bossMob.getName()) {
131+
BossName = bossMob.getName();
132+
}
88133

89-
// ---------- FILLER FUNCTIONS ----------
134+
// 生成 BOSS
135+
map.spawnMonsterOnGroundBelow(bossMob, SpawnPoint);
90136

91-
function dispose() {}
137+
// 标记为不可刷新
138+
eim.setProperty("canSpawn", "false");
92139

93-
function setup(eim, leaderid) {}
94140

95-
function monsterValue(eim, mobid) {return 0;}
96141

97-
function disbandParty(eim, player) {}
142+
// 发送公告
143+
map.broadcastMessage(PacketCreator.serverNotice(6, `[野外BOSS] ${BossName} ${BossNotice}`));
98144

99-
function playerDisconnected(eim, player) {}
145+
} catch (e) {
146+
if (log) log.error(`[野外BOSS] ${EventName} 生成失败`, e);
147+
}
148+
}
100149

101-
function playerEntry(eim, player) {}
150+
// ============================================================================
151+
// 事件回调 (Event Hooks)
152+
// ============================================================================
102153

103-
function monsterKilled(mob, eim) {}
154+
/**
155+
* [新系统] 当玩家进入监听地图时触发
156+
*/
157+
function onMapPlayerEnter(em, player, mapId) {
158+
if (mapId != MapID) return;
104159

105-
function scheduledTimeout(eim) {}
160+
var eim = em.getInstance(EventName);
161+
if (eim != null) {
106162

107-
function afterSetup(eim) {}
163+
checkAndSpawn(eim);
164+
}
165+
}
108166

109-
function changedLeader(eim, leader) {}
167+
function monsterKilled(mob, eim) {
168+
// 判断死亡的怪物是否为目标 BOSS
169+
if (mob.getId() == BossID) {
170+
var nextRespawnTime = em.getBossTime(BossTime * 60 * 1000);
110171

111-
function playerExit(eim, player) {}
172+
if (log) log.info(`[野外BOSS] ${BossName}(${BossID}) 被击杀, ${nextRespawnTime / 60000} 分钟后刷新`);
112173

113-
function leftParty(eim, player) {}
174+
// 设定倒数计时,时间到后执行 cooldownFinished
175+
em.schedule("cooldownFinished", nextRespawnTime);
176+
}
177+
}
114178

115-
function clearPQ(eim) {}
179+
// 计时器回调:冷却结束
180+
function cooldownFinished() {
181+
var eim = getOrCreateEventInstance();
182+
// 标记冷却结束
183+
eim.setProperty("canSpawn", "true");
116184

117-
function allMonstersDead(eim) {}
185+
// 启动监测
186+
187+
checkAndSpawn(eim);
188+
}
118189

119-
function playerUnregistered(eim, player) {}
190+
// 必须保留的空函数
191+
function playerEntry(eim, player) { }
192+
function dispose() { }
193+
function setup(eim, leaderid) { }
194+
function monsterValue(eim, mobid) { return 0; }
195+
function disbandParty(eim, player) { }
196+
function playerDisconnected(eim, player) { }
197+
function scheduledTimeout(eim) { }
198+
function afterSetup(eim) { }
199+
function changedLeader(eim, leader) { }
200+
function playerExit(eim, player) { }
201+
function leftParty(eim, player) { }
202+
function clearPQ(eim) { }
203+
function allMonstersDead(eim) { }
204+
function playerUnregistered(eim, player) { }
205+
function playerRevive(eim, player) { return true; }

0 commit comments

Comments
 (0)