├── .gitignore
├── module.json
├── manifest.json
├── README.md
├── index.js
└── settings_migrator.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .eslintrc.json
2 | manifest-generator.bat
3 | manifest-generator.js
--------------------------------------------------------------------------------
/module.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Boss-Helper NG",
3 | "options": {
4 | "niceName": "BH",
5 | "guiName": "Boss-Helper NG",
6 | "settingsFile": "config.json",
7 | "settingsMigrator": "settings_migrator.js",
8 | "settingsVersion": 27
9 | },
10 | "author": "HSDN / ZC / mopihu / Owyn / Lambda11",
11 | "description": "Displays information and notifications about spawning of merchants, world bosses and event monsters.",
12 | "servers": ["https://raw.githubusercontent.com/hsdn/Boss-Helper/master/"],
13 | "supportUrl": "https://github.com/hsdn/Boss-Helper/issues",
14 | "dependencies": {
15 | "notifier": "https://raw.githubusercontent.com/SerenTera/tera-notifier/master/module.json"
16 | },
17 | "version": "110.3",
18 | "disableAutoUpdate": false
19 | }
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | "README.md": "ce11a384a0ed48da95fd999f6f677458805767b9dcab5ec91b2f0d886422d661",
4 | "index.js": "b3c6c9c398bf851ce1c2bc7fbe219433829b37337c0b8f539754c22e283c18a7",
5 | "module.json": "38715825f56f990735141472712bd3763e9779324dcedf623fed34836aaec7af",
6 | "settings_migrator.js": "5b973624efdc41630a3d8a8d4f56930b88cbee90eb05bf0560e029dc087cbf7f"
7 | },
8 | "defs": {
9 | "C_CHAT": 1,
10 | "C_PLAYER_LOCATION": 5,
11 | "S_ADMIN_HOLD_CHARACTER": 2,
12 | "S_BOSS_GAGE_INFO": 3,
13 | "S_CHAT": 3,
14 | "S_CLEAR_ALL_HOLDED_ABNORMALITY": 1,
15 | "S_CURRENT_CHANNEL": 2,
16 | "S_DESPAWN_DROPITEM": 4,
17 | "S_DESPAWN_NPC": 3,
18 | "S_DUNGEON_EVENT_MESSAGE": 2,
19 | "S_EACH_SKILL_RESULT": 14,
20 | "S_INSTANT_MOVE": 3,
21 | "S_NOTIFY_GUILD_QUEST_URGENT": 1,
22 | "S_NPC_STATUS": 2,
23 | "S_SPAWN_DROPITEM": [8,9],
24 | "S_SPAWN_NPC": 11,
25 | "S_SYSTEM_MESSAGE": 1
26 | }
27 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Boss-Helper NG (Mystery Merchant Helper)
2 |
3 | Displays information and notifications about spawning of the specified NPCs in the zone (event monster/world boss/guild boss/merchant).
4 | When an NPC spawnins in the visible range, it will be marked with a marker. There are functions of teleport and automatic search for NPCs. Available English and Russian languages (detects automatically).
5 |
6 | Отображение информации и уведомлений о появлении указанных NPC в зоне (ивентовый монстр/мировой босс/гильдийный босс/торговец).
7 | При появлении NPC в видимом диапазоне, он будет отмечен маркером. Имеются функции телепортации и автоматического поиска NPC. Поддерживаются Русский и Английский языки (определяются автоматически).
8 |
9 | ### Safety / Безопасность
10 |
11 | The automatic search function (the **scan** command) is easily detectable, therefore, if detected, you can get banned. Please use this feature at your own risk!
12 |
13 | Функция автоматического поиска (команда **scan**) является легко детектируемой, следовательно при обнаружении вы можете быть забанены. Используйте данную функцию на свой страх и риск!
14 |
15 | ## Module Commands / Команды
16 | Toolbox(/8) | Command Description | Описание команды
17 | --- | --- | ---
18 | **bh** | Enable/disable module (enabled by default). | Включить/выключить модуль (по умолчанию включен).
19 | **bh alert** | Enable/disable on-screen warning messages. | Включить/выключить сообщения предупреждения на экране.
20 | **bh notice** | Enable/disable on-screen notification messages. | Включить/выключить сообщения уведомления на экране.
21 | **bh message** | Enable/disable chat messages. | Включить/выключить сообщения в чате.
22 | **bh party** | Enable/disable send messages to party members. | Включить/выключить отправку сообщений членам группы.
23 | **bh marker** | Enable/disable NPC Markers. | Включить/выключить маркеры NPC.
24 | **bh clear** | Remove marker from NPC. | Удалить маркер с NPC.
25 | **bh teleport** | Enable/disable instant teleport. | Включить/выключить мгновенный телепорт.
26 | **bh hpbar** | Enable/disable BAM-HP-Bar feature. | Включить/выключить отображение HP рейдовых боссов.
27 |
28 | ### Mystery Merchants
29 | Toolbox(/8) | Command Description | Описание команды
30 | --- | --- | ---
31 | **mm** | Display respawn times of Mystery Merchants. | Отобразить время появления тайных торговцев.
32 | **mm scan** | Search for Mystery Merchants in current zone. | Запустить поиск торговцев в текущей зоне.
33 | **mm stop** | Stop search. | Остановить поиск.
34 | **mm loc** | Display Mystery Merchants locations of current zone. | Отобразить список позиций тайных торговцев для текущей зоны.
35 | **mm to <id>** | Teleport to specified location. | Переместиться в указанную позицию.
36 |
37 | * Display respawn times of Mystery Merchants (the **mm** command):
38 | 
39 |
40 | ### World Bosses
41 | Toolbox(/8) | Command Description | Описание команды
42 | --- | --- | ---
43 | **wb** | Display respawn times of World Bosses. | Отобразить время появления мировых боссов.
44 | **wb scan** | Search for World Bosses in current zone. | Запустить поиск мировых боссов в текущей зоне.
45 | **wb stop** | Stop search. | Остановить поиск.
46 | **wb loc** | Display World Bosses locations of current zone. | Отобразить список позиций мировых боссов для текущей зоны.
47 | **wb to <id>** | Teleport to specified location. | Переместиться в указанную позицию.
48 |
49 | * Display respawn times of World Bosses (the **wb** command):
50 | 
51 |
52 | ### Raid Bosses
53 | Toolbox(/8) | Command Description | Описание команды
54 | --- | --- | ---
55 | **rb** | Display respawn times of Raid Bosses. | Отобразить время появления рейдовых боссов.
56 |
57 | * Display respawn times of Raid Bosses (the **rb** command):
58 | 
59 |
60 | ## More Information / Информация
61 |
62 | * The module has built-in functionality of Lambda11's BAM-HP-Bar module (with **fixed Ortan**):
63 | 
64 | * Merchant NPC with marker (easily visible in the distance):
65 | 
66 | * All NPC identifiers: https://github.com/neowutran/TeraDpsMeterData/tree/master/monsters
67 | * Merchant spawn locations: https://home.gamer.com.tw/creationCategory.php?owner=d0305011&c=444485
68 |
69 | ## Credits
70 | - **[ZC](https://github.com/tera-mod)** - Original developer of the Boss-Helper module
71 | - **[Owyn](https://github.com/Owyn)** - Author of the field-boss_time module
72 | - **[Lambda11](https://github.com/Lambda11)** - Author of the bam-hp-bar module
73 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-param-reassign */
2 | const os = require("os");
3 | const Vec3 = require("tera-vec3");
4 |
5 | const strings = {
6 | "ru": {
7 | "Enable/disable chat messages": "Включить/выключить сообщения в чате",
8 | "Enable/disable on-screen warning messages": "Включить/выключить сообщения предупреждения на экране",
9 | "Enable/disable on-screen notification messages": "Включить/выключить сообщения уведомления на экране",
10 | "Enable/disable send messages to party members": "Включить/выключить отправку сообщений членам группы",
11 | "Enable/disable NPC Markers": "Включить/выключить маркеры NPC",
12 | "Enable/disable instant teleport": "Включить/выключить мгновенный телепорт",
13 | "Enable/disable BAM-HP-Bar feature": "Включить/выключить отображение HP рейдовых боссов",
14 | "Remove marker from NPC": "Удалить маркер с NPC",
15 | "Display the spawn times of Mystery Merchants": "Отобразить время появления тайных торговцев",
16 | "Display Mystery Merchants locations of current zone": "Отобразить список позиций тайных торговцев для текущей зоны",
17 | "Search for Mystery Merchants in current zone": "Запустить поиск торговцев в текущей зоне",
18 | "Display the spawn times of World Bosses": "Отобразить время появления мировых боссов",
19 | "Display World Bosses locations of current zone": "Отобразить список позиций мировых боссов для текущей зоны",
20 | "Search for World Bosses in current zone": "Запустить поиск мировых боссов в текущей зоне",
21 | "Display the spawn times of Raid Bosses": "Отобразить время появления рейдовых боссов",
22 | "Display Raid Bosses locations of current zone": "Отобразить список позиций рейдовых боссов для текущей зоны",
23 | "Search for Raid Bosses in current zone": "Запустить поиск рейдовых боссов в текущей зоне",
24 | "Stop search": "Остановить поиск",
25 | "Teleport to specified location": "Переместиться в указанную позицию",
26 | "Use command": "Используйте команду",
27 | "or racial skill for teleport there": "или расовый скил для телепортации туда",
28 | "Enabled": "Вкл.",
29 | "Disabled": "Выкл.",
30 | "Alert messages": "Предупреждения",
31 | "Notice messages": "Уведомления",
32 | "Party messages": "Сообщения в группу",
33 | "Spawn messages": "Сообщения о появлении",
34 | "Instant teleport": "Мгновенный телепорт",
35 | "BAM-HP-Bar feature": "Отображение HP рейдовых боссов",
36 | "Position markers": "Отметка позиции",
37 | "Position markers cleared": "Очищена отметка позиции",
38 | "Unknown parameter": "Неверный параметр",
39 | "Raid Boss": "Рейдовый босс",
40 | "Guild Boss": "Гильдийный босс",
41 | "World Boss": "Мировой босс",
42 | "Merchant": "Торговец",
43 | "Goblin": "Гоблин",
44 | "Found": "Найден",
45 | "Spawned": "Появился",
46 | "Refreshed": "Обновлен",
47 | "Location": "Локация",
48 | "Scan started": "Сканирование начато",
49 | "Scan stopped": "Сканирование закончено",
50 | "No positions for this zone": "Нет позиций для этой зоны",
51 | "NPC is not found": "NPC не найден",
52 | "channel": "канал",
53 | "no data": "нет данных",
54 | "spawned at": "появился в",
55 | "last": "послед. в",
56 | "next": "след. в"
57 | }
58 | };
59 |
60 | Number.prototype.leadZero = function(i = 1) {
61 | return this < 10 * i ? "0".repeat(i) + this : this;
62 | };
63 |
64 | function arrayShuffle(array) {
65 | for (let i = array.length - 1; i > 0; i--) {
66 | const j = Math.floor(Math.random() * (i + 1));
67 | [array[i], array[j]] = [array[j], array[i]];
68 | }
69 | return array;
70 | }
71 |
72 | module.exports = function BossHelper(mod) {
73 | const notifier = mod.require.notifier;
74 | const MSG = new TeraMessage(mod);
75 | const bamHp = new BamHpBar(mod);
76 |
77 | const configuredNpcs = [];
78 | const spawnedNpcs = new Map();
79 | const obtainedMerchants = {};
80 | const playerLocation = new Vec3(0, 0, 0);
81 | const commands = {
82 | "world_bosses": "wb",
83 | "raid_bosses": "rb",
84 | "merchants": "mm"
85 | };
86 | const defaultLanguage = "en";
87 |
88 | let language = defaultLanguage;
89 | let serverId = null;
90 | let party = false;
91 | let fallProtect = false;
92 | let zoneLocations = {};
93 | let searchZoneLocations = {};
94 | let playerChannel = 0;
95 | let playerTime = 0;
96 | let lastPos = null;
97 | let seekPos = 0;
98 |
99 | ["world_bosses", "raid_bosses", "others", "merchants", "goblins"].forEach(type =>
100 | mod.settings[type].regions.forEach(region =>
101 | region.npcs.forEach(entry =>
102 | configuredNpcs.push({ type, "region": Object.fromEntries(Object.entries(region).filter(x => x[0] !== "npcs")), ...entry })
103 | )
104 | )
105 | );
106 |
107 | mod.command.add(["bh", "boss"], {
108 | "help": () => {
109 | MSG.chat(`${MSG.BLU("bh message")} - ${M("Enable/disable chat messages")}`);
110 | MSG.chat(`${MSG.BLU("bh alert")} - ${M("Enable/disable on-screen warning messages")}`);
111 | MSG.chat(`${MSG.BLU("bh notice")} - ${M("Enable/disable on-screen notification messages")}`);
112 | MSG.chat(`${MSG.BLU("bh party")} - ${M("Enable/disable send messages to party members")}`);
113 | MSG.chat(`${MSG.BLU("bh marker")} - ${M("Enable/disable NPC Markers")}`);
114 | MSG.chat(`${MSG.BLU("bh clear")} - ${M("Remove marker from NPC")}`);
115 | MSG.chat(`${MSG.BLU("bh teleport")} - ${M("Enable/disable instant teleport")}`);
116 | MSG.chat(`${MSG.BLU("bh hpbar")} - ${M("Enable/disable BAM-HP-Bar feature")}`);
117 | MSG.chat("========");
118 | MSG.chat(`${MSG.BLU("mm")} - ${M("Display the spawn times of Mystery Merchants")}`);
119 | MSG.chat(`${MSG.BLU("mm loc")} - ${M("Display Mystery Merchants locations of current zone")}`);
120 | MSG.chat(`${MSG.BLU("mm scan")} - ${M("Search for Mystery Merchants in current zone")}`);
121 | MSG.chat(`${MSG.BLU("mm stop")} - ${M("Stop search")}`);
122 | MSG.chat(`${MSG.BLU("mm to ") + MSG.YEL("id")} - ${M("Teleport to specified location")}`);
123 | MSG.chat("========");
124 | MSG.chat(`${MSG.BLU("rb")} - ${M("Display the spawn times of Raid Bosses")}`);
125 | MSG.chat(`${MSG.BLU("rb loc")} - ${M("Display Raid Bosses locations of current zone")}`);
126 | MSG.chat(`${MSG.BLU("rb scan")} - ${M("Search for Raid Bosses in current zone")}`);
127 | MSG.chat(`${MSG.BLU("rb stop")} - ${M("Stop search")}`);
128 | MSG.chat(`${MSG.BLU("rb to ") + MSG.YEL("id")} - ${M("Teleport to specified location")}`);
129 | MSG.chat("========");
130 | MSG.chat(`${MSG.BLU("wb")} - ${M("Display the spawn times of World Bosses")}`);
131 | MSG.chat(`${MSG.BLU("wb loc")} - ${M("Display World Bosses locations of current zone")}`);
132 | MSG.chat(`${MSG.BLU("wb scan")} - ${M("Search for World Bosses in current zone")}`);
133 | MSG.chat(`${MSG.BLU("wb stop")} - ${M("Stop search")}`);
134 | MSG.chat(`${MSG.BLU("wb to ") + MSG.YEL("id")} - ${M("Teleport to specified location")}`);
135 | },
136 | "alert": () => {
137 | mod.settings.alert = !mod.settings.alert;
138 | MSG.chat(`${M("Alert messages")}: ${mod.settings.alert ? MSG.BLU(M("Enabled")) : MSG.YEL(M("Disabled"))}`);
139 | },
140 | "notice": () => {
141 | mod.settings.notice = !mod.settings.notice;
142 | MSG.chat(`${M("Notice messages")}: ${mod.settings.notice ? MSG.BLU(M("Enabled")) : MSG.YEL(M("Disabled"))}`);
143 | },
144 | "party": () => {
145 | party = !party;
146 | MSG.chat(`${M("Party messages")}: ${party ? MSG.BLU(M("Enabled")) : MSG.YEL(M("Disabled"))}`);
147 | },
148 | "message": () => {
149 | mod.settings.message = !mod.settings.message;
150 | MSG.chat(`${M("Spawn messages")}: ${mod.settings.message ? MSG.BLU(M("Enabled")) : MSG.YEL(M("Disabled"))}`);
151 | },
152 | "marker": () => {
153 | mod.settings.marker = !mod.settings.marker;
154 | MSG.chat(`${M("Position markers")}: ${mod.settings.marker ? MSG.BLU(M("Enabled")) : MSG.YEL(M("Disabled"))}`);
155 | },
156 | "clear": () => {
157 | MSG.chat(`Boss-Helper: ${MSG.TIP(M("Position markers cleared"))}`);
158 | spawnedNpcs.forEach(key => despawnMarker(key));
159 | },
160 | "teleport": () => {
161 | mod.settings.teleport = !mod.settings.teleport;
162 | MSG.chat(`${M("Instant teleport")}: ${mod.settings.teleport ? MSG.BLU(M("Enabled")) : MSG.YEL(M("Disabled"))}`);
163 | },
164 | "hpbar": () => {
165 | mod.settings.hpbar = !mod.settings.hpbar;
166 | MSG.chat(`${M("BAM-HP-Bar feature")}: ${mod.settings.hpbar ? MSG.BLU(M("Enabled")) : MSG.YEL(M("Disabled"))}`);
167 | if (!mod.settings.hpbar) {
168 | bamHp.unload();
169 | }
170 | },
171 | "$none": () => {
172 | mod.settings.enabled = !mod.settings.enabled;
173 | MSG.chat(mod.settings.enabled ? MSG.BLU(M("Enabled")) : MSG.YEL(M("Disabled")));
174 | if (!mod.settings.enabled) {
175 | spawnedNpcs.forEach(key => despawnMarker(key));
176 | spawnedNpcs.clear();
177 | stopScan();
178 | }
179 | },
180 | "$default": () => MSG.chat(`${MSG.RED(M("Unknown parameter"))}. ${M("Use command")}: ${MSG.BLU("bh help")}`)
181 | });
182 |
183 | mod.command.add(commands["merchants"], {
184 | "to": arg => toZoneLocation("merchants", arg),
185 | "loc": () => listZoneLocations("merchants"),
186 | "scan": () => startScan("merchants"),
187 | "stop": () => stopScan(),
188 | "$none": () => {
189 | MSG.chat(`======== ${M("Goblin").toUpperCase()} ========`);
190 |
191 | mod.settings.goblins.regions.forEach(region => {
192 | if (region.logDiff === undefined) return;
193 | const name = getName(region);
194 |
195 | if (!mod.settings.goblins.logTime[serverId]) {
196 | MSG.chat(` ${MSG.BLU(name)} ${MSG.GRY(M("no data"))}`);
197 | } else {
198 | let nextTime = mod.settings.goblins.logTime[serverId] + region.logDiff * 1000;
199 |
200 | while (Date.now() > nextTime) {
201 | nextTime += 24 * 60 * 60 * 1000;
202 | }
203 |
204 | const lastTime = nextTime - 24 * 60 * 60 * 1000;
205 |
206 | if (lastTime < Date.now() && lastTime + 5 * 60 * 1000 >= Date.now()) {
207 | MSG.chat(` ${MSG.PIK(name)} ${M("spawned at")} ${MSG.TIP(getTime(lastTime))}`);
208 | } else {
209 | MSG.chat(` ${MSG.BLU(name)} ${M("next")} ${MSG.TIP(getTime(nextTime))}`);
210 | }
211 | }
212 | });
213 |
214 | MSG.chat(`======== ${M("Merchant").toUpperCase()} ========`);
215 | const regionGroups = [];
216 |
217 | mod.settings.merchants.regions.forEach(region => {
218 | if (region.zoneId === undefined) return;
219 |
220 | let group = undefined;
221 |
222 | if (region.logDiff !== undefined) {
223 | group = regionGroups.find(m => m.logDiff == region.logDiff);
224 |
225 | if (group) {
226 | group.zoneIds.push(region.zoneId);
227 | group.fullName.push(getName(region));
228 | }
229 | }
230 |
231 | if (region.logTime !== undefined || !group) {
232 | regionGroups.push({ ...region, "zoneIds": [region.zoneId], "fullName": [getName(region)] });
233 | }
234 | });
235 |
236 | regionGroups.forEach(group => {
237 | const name = group.fullName.join(" / ");
238 | const obtainedName = obtainedMerchants[serverId][Object.keys(obtainedMerchants[serverId]).filter(x => group.zoneIds.includes(Number(x)))];
239 |
240 | if (group.logTime !== undefined && group.logIntervalMin !== undefined && group.logIntervalMax !== undefined) {
241 | if (!group.logTime[serverId]) {
242 | MSG.chat(` ${MSG.BLU(name)} ${MSG.GRY(M("no data"))}`);
243 | } else {
244 | const nextTimeMin = group.logTime[serverId] + group.logIntervalMin * 1000;
245 | const nextTimeMax = group.logTime[serverId] + group.logIntervalMax * 1000;
246 |
247 | if (group.logTime[serverId] < Date.now() && group.logTime[serverId] + 30 * 60 * 1000 >= Date.now()) {
248 | MSG.chat(` ${MSG.PIK(obtainedName ? obtainedName.fullName : name)} ${M("spawned at")} ${MSG.TIP(getTime(group.logTime[serverId]))}`);
249 | } else if (Date.now() < nextTimeMax) {
250 | if (nextTimeMin == nextTimeMax) {
251 | MSG.chat(` ${MSG.BLU(name)} ${M("next")} ${MSG.TIP(getTime(nextTimeMin))}`);
252 | } else {
253 | MSG.chat(` ${MSG.BLU(name)} ${M("next")} ${MSG.TIP(getTime(nextTimeMin, nextTimeMax))}`);
254 | }
255 | } else {
256 | MSG.chat(` ${MSG.BLU(name)} ${M("last")} ${MSG.GRY(getTime(group.logTime[serverId]))}`);
257 | }
258 | }
259 | } else if (group.logDiff !== undefined) {
260 | let logTime = mod.settings.merchants.logTime[serverId];
261 |
262 | if (group.logTime !== undefined) {
263 | logTime = group.logTime[serverId];
264 | }
265 |
266 | if (!logTime) {
267 | MSG.chat(` ${MSG.BLU(name)} ${MSG.GRY(M("no data"))}`);
268 | } else {
269 | let nextTime = logTime + group.logDiff * 1000;
270 |
271 | while (Date.now() > nextTime) {
272 | nextTime += 5 * 60 * 60 * 1000;
273 | }
274 |
275 | const lastTime = nextTime - 5 * 60 * 60 * 1000;
276 |
277 | if (lastTime < Date.now() && lastTime + 30 * 60 * 1000 >= Date.now()) {
278 | MSG.chat(` ${MSG.PIK(obtainedName ? obtainedName.fullName : name)} ${M("spawned at")} ${MSG.TIP(getTime(lastTime))}`);
279 | } else {
280 | MSG.chat(` ${MSG.BLU(name)} ${M("next")} ${MSG.TIP(getTime(nextTime))}`);
281 | }
282 | }
283 | }
284 | });
285 | },
286 | "$default": () => MSG.chat(`${MSG.RED(M("Unknown parameter"))}. ${M("Use command")}: ${MSG.BLU("bh help")}`)
287 | });
288 |
289 | mod.command.add(commands["world_bosses"], {
290 | "to": arg => toZoneLocation("world_bosses", arg),
291 | "loc": () => listZoneLocations("world_bosses"),
292 | "scan": () => startScan("world_bosses"),
293 | "stop": () => stopScan(),
294 | "$none": () => {
295 | MSG.chat(`======== ${M("World Boss").toUpperCase()} ========`);
296 |
297 | mod.settings.world_bosses.regions.forEach(region =>
298 | region.npcs.forEach(boss => {
299 | if (boss.logTime === undefined) return;
300 |
301 | const name = getName(boss);
302 |
303 | if (!boss.logTime[serverId]) {
304 | MSG.chat(` ${MSG.BLU(name)} ${MSG.GRY(M("no data"))}`);
305 | } else {
306 | const nextTime = boss.logTime[serverId] + 5 * 60 * 60 * 1000;
307 |
308 | if (Date.now() < nextTime) {
309 | MSG.chat(` ${MSG.BLU(name)} ${M("next")} ${MSG.TIP(getTime(nextTime))}`);
310 | } else {
311 | MSG.chat(` ${MSG.BLU(name)} ${M("last")} ${MSG.GRY(getTime(boss.logTime[serverId]))}`);
312 | }
313 | }
314 | })
315 | );
316 | },
317 | "$default": () => MSG.chat(`${MSG.RED(M("Unknown parameter"))}. ${M("Use command")}: ${MSG.BLU("bh help")}`)
318 | });
319 |
320 | mod.command.add(commands["raid_bosses"], {
321 | "to": arg => toZoneLocation("raid_bosses", arg),
322 | "loc": () => listZoneLocations("raid_bosses"),
323 | "scan": () => startScan("raid_bosses"),
324 | "stop": () => stopScan(),
325 | "$none": () => {
326 | MSG.chat(`======== ${M("Raid Boss").toUpperCase()} ========`);
327 |
328 | mod.settings.raid_bosses.regions.forEach(region =>
329 | region.npcs.forEach(boss => {
330 | if (boss.logTime === undefined) return;
331 |
332 | const name = getName(boss);
333 |
334 | if (!boss.logTime[serverId]) {
335 | MSG.chat(` ${MSG.BLU(name)} ${MSG.GRY(M("no data"))}`);
336 | } else {
337 | const nextTimeMax = boss.logTime[serverId] + (5 * 60 * 60 + 30 * 60) * 1000;
338 | const nextTimeMin = nextTimeMax - 60 * 60 * 1000;
339 |
340 | if (Date.now() < nextTimeMax) {
341 | MSG.chat(` ${MSG.BLU(name)} ${M("next")} ${MSG.TIP(getTime(nextTimeMin, nextTimeMax))}`);
342 | } else {
343 | MSG.chat(` ${MSG.BLU(name)} ${M("last")} ${MSG.GRY(getTime(boss.logTime[serverId]))}`);
344 | }
345 | }
346 | })
347 | );
348 | },
349 | "$default": () => MSG.chat(`${MSG.RED(M("Unknown parameter"))}. ${M("Use command")}: ${MSG.BLU("bh help")}`)
350 | });
351 |
352 | mod.game.on("enter_game", () => {
353 | if (!mod.settings.language || mod.settings.language == "auto") {
354 | language = { "0": "en", "1": "kr", "3": "jp", "4": "de", "5": "fr", "7": "tw", "8": "ru" }[mod.game.language] || defaultLanguage;
355 | } else {
356 | language = mod.settings.language;
357 | }
358 |
359 | serverId = mod.game.me.serverId;
360 |
361 | if (obtainedMerchants[serverId] === undefined) {
362 | obtainedMerchants[serverId] = {};
363 | }
364 |
365 | migrateConfiguration();
366 | });
367 |
368 | mod.game.me.on("change_zone", () => {
369 | spawnedNpcs.clear();
370 | updateZoneLocations();
371 | });
372 |
373 | mod.game.on("leave_loading_screen", () => {
374 | stopScan();
375 | });
376 |
377 | mod.hook("S_CURRENT_CHANNEL", 2, event => {
378 | playerChannel = Number(event.channel);
379 | });
380 |
381 | mod.hook("C_PLAYER_LOCATION", 5, event => {
382 | if (!mod.settings.enabled) return;
383 |
384 | let correctedTime = false;
385 |
386 | if (playerTime > event.time) {
387 | event.time = (playerTime + 75);
388 | correctedTime = true;
389 | }
390 |
391 | Object.assign(playerLocation, event.dest);
392 |
393 | if (fallProtect && (event.type == 2 || event.type == 10)) {
394 | fallProtect = false;
395 |
396 | return false;
397 | }
398 |
399 | if (correctedTime) {
400 | return true;
401 | }
402 | });
403 |
404 | mod.hook("S_SPAWN_NPC", mod.majorPatchVersion >= 101 ? 12 : 11, event => {
405 | if (!mod.settings.enabled) return;
406 |
407 | const npc = getNpc(event.huntingZoneId, event.templateId);
408 | let mapLink = null;
409 |
410 | if (npc) {
411 | spawnedNpcs.set(event.gameId, npc);
412 |
413 | if (npc.type === "merchants" && npc.region.zoneId !== undefined) {
414 | obtainedMerchants[serverId][npc.region.zoneId] = npc;
415 | }
416 |
417 | if (searchZoneLocations[npc.type] !== undefined && searchZoneLocations[npc.type][seekPos - 1] !== undefined) {
418 | mapLink = getMapLink(searchZoneLocations[npc.type][seekPos - 1].map, event.loc, npc.fullName);
419 |
420 | MSG.chat(`${MSG.BLU(M("Found"))} ${mapLink} ${M("Location")} ${MSG.YEL(searchZoneLocations[npc.type][seekPos - 1].index + 1)}`);
421 |
422 | if (!mod.settings.teleport) {
423 | MSG.chat(`${M("Use command")} ${MSG.BLU(`${commands[npc.type]} to ${searchZoneLocations[npc.type][seekPos - 1].index + 1}`)} ${M("or racial skill for teleport there")}.`);
424 | }
425 |
426 | stopScan();
427 | }
428 |
429 | if (mod.settings.marker && (npc.marker === undefined || npc.marker)) {
430 | mod.setTimeout(() => spawnMarker(event.gameId, event.loc), 1000);
431 | }
432 |
433 | if (mod.settings.alert && (npc.alert === undefined || npc.alert)) {
434 | MSG.alert((`${M("Found")} ${npc.fullName}`), 44);
435 | }
436 |
437 | if (party) {
438 | mod.send("C_CHAT", 1, {
439 | "channel": 21,
440 | "message": (`${playerChannel} ${M("channel")} ${mapLink || npc.fullName}`)
441 | });
442 | } else if (mod.settings.notice) {
443 | MSG.raids(`${M("Found")} ${npc.fullName}`);
444 | }
445 |
446 | if (mod.settings.hpbar && npc.type === "raid_bosses") {
447 | return bamHp.spawnNpc(event);
448 | }
449 |
450 | updateZoneLocations();
451 | }
452 | });
453 |
454 | mod.hook("S_DESPAWN_NPC", 3, { "order": -100 }, event => {
455 | if (!mod.settings.enabled) return;
456 |
457 | const npc = spawnedNpcs.get(event.gameId);
458 |
459 | if (!npc) return;
460 |
461 | if (npc.type === "world_bosses") {
462 | saveTime(npc);
463 | }
464 |
465 | despawnMarker(event.gameId);
466 | spawnedNpcs.delete(event.gameId);
467 | });
468 |
469 | mod.hook("S_NOTIFY_GUILD_QUEST_URGENT", 1, event => {
470 | if (!mod.settings.enabled || !mod.settings.message) return;
471 |
472 | let npc = undefined;
473 |
474 | switch (event.quest) {
475 | case "@GuildQuest:10005001":
476 | npc = getNpc(event.zoneId, 2001);
477 | break;
478 |
479 | case "@GuildQuest:10006001":
480 | npc = getNpc(event.zoneId, 2002);
481 | break;
482 |
483 | case "@GuildQuest:10007001":
484 | npc = getNpc(event.zoneId, 2003);
485 | break;
486 | }
487 |
488 | if (!npc) return;
489 |
490 | if (event.type == 0) {
491 | MSG.chat(`${MSG.BLU(M("Guild Boss"))} ${MSG.RED(npc.fullName)}`);
492 | notificationafk(`${M("Guild Boss")} ${npc.fullName}`);
493 | }
494 |
495 | if (event.type == 1) {
496 | MSG.chat(`${MSG.BLU(M("Refreshed"))} ${MSG.TIP(npc.fullName)}`);
497 | notificationafk(`${M("Refreshed")} ${npc.fullName}`);
498 | }
499 | });
500 |
501 | mod.hook("S_SYSTEM_MESSAGE", 1, event => {
502 | if (!mod.settings.enabled) return;
503 |
504 | const sysMsg = mod.parseSystemMessage(event.message);
505 | const npcName = sysMsg.tokens.npcName || sysMsg.tokens.npcname;
506 | let npc = undefined;
507 |
508 | if (npcName) {
509 | const npcId = npcName.match(/\d+/g);
510 | npc = getNpc(parseInt(npcId[0]), parseInt(npcId[1]));
511 | }
512 |
513 | if (!npc) return;
514 |
515 | switch (sysMsg.id) {
516 | case "SMT_FIELDBOSS_APPEAR":
517 | if (mod.settings.message) {
518 | MSG.chat(`${MSG.BLU(M("Spawned"))} ${MSG.RED(npc.fullName)}`);
519 | notificationafk(`${M("Spawned")} ${npc.fullName}`);
520 | }
521 | break;
522 |
523 | case "SMT_FIELDBOSS_DIE_GUILD":
524 | case "SMT_FIELDBOSS_DIE_NOGUILD":
525 | if (mod.settings.message) {
526 | const nextTimeMax = Date.now() + (5 * 60 * 60 + 30 * 60) * 1000;
527 | const nextTimeMin = nextTimeMax - 60 * 60 * 1000;
528 |
529 | MSG.chat(`${MSG.RED(npc.fullName)} ${M("next")} ${MSG.TIP(getTime(nextTimeMin, nextTimeMax))}`);
530 | }
531 |
532 | saveTime(npc);
533 | break;
534 |
535 | case "SMT_WORLDSPAWN_NOTIFY_SPAWN":
536 | if (mod.settings.message) {
537 | if (npc.type === "goblins") {
538 | MSG.party(`${M("Spawned")} ${npc.fullName}`);
539 | } else {
540 | MSG.chat(`${MSG.BLU(M("Spawned"))} ${MSG.PIK(npc.fullName)}`);
541 | }
542 | notificationafk(`${M("Spawned")} ${npc.fullName}`);
543 | }
544 |
545 | if (npc.type === "merchants" && npc.region.zoneId !== undefined) {
546 | obtainedMerchants[serverId][npc.region.zoneId] = npc;
547 | }
548 |
549 | updateZoneLocations();
550 | saveTime(npc);
551 | break;
552 |
553 | case "SMT_WORLDSPAWN_NOTIFY_DESPAWN":
554 | if (npc.type === "merchants" && npc.region.zoneId !== undefined) {
555 | delete obtainedMerchants[serverId][npc.region.zoneId];
556 | }
557 |
558 | updateZoneLocations();
559 | saveTime(npc, true);
560 | break;
561 | }
562 | });
563 |
564 | function toZoneLocation(npcType, to) {
565 | if (to && isNumber(to) && zoneLocations[npcType] !== undefined && zoneLocations[npcType][to - 1] !== undefined) {
566 | teleport(zoneLocations[npcType][to - 1], true);
567 | }
568 | }
569 |
570 | function listZoneLocations(npcType) {
571 | updateZoneLocations();
572 |
573 | if (zoneLocations[npcType] !== undefined && zoneLocations[npcType].length > 0) {
574 | Object.keys(zoneLocations[npcType]).forEach(key => {
575 | const mapLink = getMapLink(zoneLocations[npcType][key].map, zoneLocations[npcType][key], zoneLocations[npcType][key].name);
576 | const found = zoneLocations[npcType][key].search ? ` (${M("Found")})` : "";
577 | const here = isNearLocation(zoneLocations[npcType][key]) ? " ***" : "";
578 |
579 | MSG.chat(`${MSG.YEL(Number(key) + 1)} - ${mapLink + found + here}`);
580 | });
581 | } else {
582 | MSG.chat(MSG.RED(M("No positions for this zone")));
583 | }
584 | }
585 |
586 | function updateZoneLocations() {
587 | const indexes = {};
588 | zoneLocations = {};
589 | searchZoneLocations = {};
590 |
591 | configuredNpcs.forEach(entry => {
592 | if (entry.locations !== undefined && entry.region !== undefined && entry.region.zoneId == mod.game.me.zone) {
593 | const search = obtainedMerchants[serverId][mod.game.me.zone] !== undefined && obtainedMerchants[serverId][mod.game.me.zone].huntingZoneId == entry.huntingZoneId;
594 |
595 | if (zoneLocations[entry.type] === undefined) {
596 | zoneLocations[entry.type] = [];
597 | indexes[entry.type] = 0;
598 | }
599 |
600 | if (searchZoneLocations[entry.type] === undefined) {
601 | searchZoneLocations[entry.type] = [];
602 | }
603 |
604 | entry.locations.forEach(location => {
605 | if (search && !isNearLocation(location)) {
606 | searchZoneLocations[entry.type].push({ "name": getName(entry), "index": indexes[entry.type], ...location });
607 | }
608 |
609 | zoneLocations[entry.type].push({ "name": getName(entry), "index": indexes[entry.type], search, ...location });
610 |
611 | indexes[entry.type]++;
612 | });
613 | }
614 | });
615 |
616 | Object.keys(searchZoneLocations).forEach(type => {
617 | if (searchZoneLocations[type].length < 1 && zoneLocations[type] !== undefined) {
618 | searchZoneLocations[type] = [...zoneLocations[type]];
619 | }
620 |
621 | if (type === "merchants") {
622 | arrayShuffle(searchZoneLocations[type]);
623 | }
624 | });
625 | }
626 |
627 | function saveTime(npc, despawn = false) {
628 | switch (npc.type) {
629 | case "merchants":
630 | mod.settings[npc.type].regions.forEach(region => {
631 | if (region.npcs.find(b => b.huntingZoneId == npc.huntingZoneId && b.templateId == npc.templateId)) {
632 | if (region.logDiff !== undefined) {
633 | mod.settings[npc.type].logTime[serverId] = Date.now() - region.logDiff * 1000 - (despawn ? 30 * 60 * 1000 : 0);
634 | }
635 |
636 | if (region.logTime !== undefined) {
637 | region.logTime[serverId] = Date.now() - (despawn ? 30 * 60 * 1000 : 0);
638 | }
639 | }
640 | });
641 | break;
642 |
643 | case "goblins":
644 | mod.settings[npc.type].regions.forEach(region => {
645 | if (region.npcs.find(b => b.huntingZoneId == npc.huntingZoneId && b.templateId == npc.templateId) && region.logDiff !== undefined) {
646 | mod.settings[npc.type].logTime[serverId] = Date.now() - region.logDiff * 1000;
647 | }
648 | });
649 | break;
650 |
651 | case "world_bosses":
652 | case "raid_bosses":
653 | mod.settings[npc.type].regions.forEach(region => {
654 | const boss = region.npcs.find(b => b.huntingZoneId == npc.huntingZoneId && b.templateId == npc.templateId);
655 |
656 | if (boss && boss.logTime !== undefined) {
657 | boss.logTime[serverId] = Date.now();
658 | }
659 | });
660 | }
661 | }
662 |
663 | function spawnMarker(gameId, loc) {
664 | const itemLoc = { ...loc };
665 |
666 | itemLoc.z -= 100;
667 |
668 | mod.send("S_SPAWN_DROPITEM", mod.majorPatchVersion >= 99 ? 9 : 8, {
669 | "gameId": gameId * 10n,
670 | "loc": itemLoc,
671 | "item": mod.settings.itemId,
672 | "amount": 1,
673 | "expiry": 0,
674 | "owners": []
675 | });
676 | }
677 |
678 | function despawnMarker(gameId) {
679 | if (!spawnedNpcs.has(gameId)) return;
680 |
681 | mod.send("S_DESPAWN_DROPITEM", 4, {
682 | "gameId": gameId * 10n
683 | });
684 | }
685 |
686 | function startScan(npcType) {
687 | if (!mod.settings.enabled) return;
688 |
689 | updateZoneLocations();
690 | stopScan();
691 |
692 | if (searchZoneLocations[npcType] != undefined && searchZoneLocations[npcType].length > 0) {
693 | if (lastPos) seekPos = lastPos;
694 |
695 | MSG.chat(`${M("Scan started")} (${searchZoneLocations[npcType].length})...`);
696 | mod.setInterval(searchNpc, 5000, npcType);
697 | holdCharacter();
698 | } else {
699 | MSG.chat(MSG.RED(M("No positions for this zone")));
700 | stopScan();
701 | }
702 | }
703 |
704 | function searchNpc(npcType) {
705 | seekPos++;
706 |
707 | if (searchZoneLocations[npcType] != undefined && seekPos <= searchZoneLocations[npcType].length) {
708 | MSG.chat(`${M("Location")} [${seekPos}/${searchZoneLocations[npcType].length}]: ${MSG.BLU(searchZoneLocations[npcType][seekPos - 1].name)}`);
709 |
710 | teleport(searchZoneLocations[npcType][seekPos - 1], mod.settings.teleport);
711 | holdCharacter();
712 |
713 | lastPos = seekPos;
714 | } else {
715 | MSG.chat(MSG.RED(M("NPC is not found")));
716 | stopScan();
717 | }
718 | }
719 |
720 | function stopScan() {
721 | if (lastPos) {
722 | MSG.chat(`${M("Scan stopped")}`);
723 | }
724 |
725 | mod.clearAllIntervals();
726 | unholdCharacter();
727 |
728 | lastPos = null;
729 | seekPos = 0;
730 | }
731 |
732 | function holdCharacter() {
733 | mod.send("S_ADMIN_HOLD_CHARACTER", 2, {
734 | "hold": true
735 | });
736 | }
737 |
738 | function unholdCharacter() {
739 | mod.send("S_CLEAR_ALL_HOLDED_ABNORMALITY", 1, {});
740 | mod.send("S_ADMIN_HOLD_CHARACTER", 2, {
741 | "hold": false
742 | });
743 | }
744 |
745 | function teleport(newLoc, instant = false) {
746 | if (!mod.settings.enabled) return;
747 |
748 | let currTime = os.uptime() * 1000 + new Date().getMilliseconds() + 150;
749 |
750 | if (currTime < playerTime) {
751 | currTime = playerTime + 50;
752 | }
753 |
754 | fallProtect = true;
755 | playerTime = currTime;
756 |
757 | const direction = Math.atan2(newLoc.y - playerLocation.y, newLoc.x - playerLocation.x);
758 | const modLoc = new Vec3(0, 0, 0);
759 |
760 | Object.assign(modLoc, newLoc);
761 | Object.assign(playerLocation, modLoc);
762 |
763 | modLoc.z += 10;
764 |
765 | mod.send("C_PLAYER_LOCATION", 5, {
766 | "loc": modLoc,
767 | "w": direction,
768 | "lookdirection": direction,
769 | "dest": modLoc,
770 | "type": 7,
771 | "jumpDistance": 0,
772 | "inShuttle": false,
773 | "time": playerTime
774 | });
775 |
776 | if (instant) {
777 | mod.send("S_INSTANT_MOVE", 3, {
778 | "gameId": mod.game.me.gameId,
779 | "loc": modLoc,
780 | "w": direction
781 | });
782 | }
783 | }
784 |
785 | function isNearLocation(loc, d = 50) {
786 | return playerLocation.dist3D(new Vec3(loc.x, loc.y, loc.z)) / 25 <= d;
787 | }
788 |
789 | function getNpc(huntingZoneId, templateId) {
790 | let npc = undefined;
791 |
792 | if (configuredNpcs.find(b => b.huntingZoneId == huntingZoneId && b.templateId == templateId)) {
793 | npc = { ...configuredNpcs.find(b => b.huntingZoneId == huntingZoneId && b.templateId == templateId) };
794 | }
795 |
796 | if (npc) {
797 | npc.fullName = getName(npc.region) ? `[${getName(npc.region)}] ${getName(npc)}` : getName(npc);
798 | }
799 |
800 | return npc;
801 | }
802 |
803 | function getMapLink(map, loc, text) {
804 | return `<${text}>`;
805 | }
806 |
807 | function getName(entry) {
808 | return entry[`name_${language.toUpperCase()}`] || entry[`name_${language}`] || entry.name;
809 | }
810 |
811 | function getTime(thisTimeOne, thisTimeTwo = null) {
812 | if (thisTimeTwo === null) {
813 | thisTimeTwo = thisTimeOne;
814 | }
815 |
816 | const timeOne = new Date(thisTimeOne);
817 | const timeTwo = new Date(thisTimeTwo);
818 | const timeStringOne = `${(timeOne.getMonth() + 1).leadZero()}/${timeOne.getDate().leadZero()} ${timeOne.getHours().leadZero()}:${timeOne.getMinutes().leadZero()}`;
819 | const timeStringTwo = `${(timeTwo.getMonth() + 1).leadZero()}/${timeTwo.getDate().leadZero()} ${timeTwo.getHours().leadZero()}:${timeTwo.getMinutes().leadZero()}`;
820 |
821 | if (thisTimeOne === thisTimeTwo) {
822 | return timeOne > Date.now() && timeOne < Date.now() + 30 * 60 * 1000 ? MSG.YEL(timeStringOne) : timeStringOne;
823 | } else if (timeOne > Date.now() && timeOne < Date.now() + 30 * 60 * 1000 || timeTwo > Date.now() && timeTwo < Date.now() + 30 * 60 * 1000) {
824 | return MSG.YEL(`${timeStringOne} ~ ${timeStringTwo}`);
825 | } else {
826 | return `${timeStringOne} ~ ${timeStringTwo}`;
827 | }
828 | }
829 |
830 | function isNumber(value) {
831 | return !isNaN(parseFloat(value)) && !isNaN(value - 0);
832 | }
833 |
834 | function M(string) {
835 | return strings[language] && strings[language][string] ? strings[language][string] : string;
836 | }
837 |
838 | function notificationafk(msg) {
839 | notifier.notifyafk({
840 | "title": "TERA AFK-Notification",
841 | "message": msg,
842 | "wait": false,
843 | "sound": "Notification.IM"
844 | });
845 | }
846 |
847 | function migrateConfiguration() {
848 | if (mod.settings.merchants.logTime !== undefined && isNumber(mod.settings.merchants.logTime)) {
849 | mod.settings.merchants.logTime = { [serverId]: mod.settings.merchants.logTime };
850 | }
851 |
852 | mod.settings.goblins.regions.forEach(region => {
853 | if (mod.settings.goblins.logTime !== undefined && isNumber(mod.settings.goblins.logTime)) {
854 | mod.settings.goblins.logTime = { [serverId]: mod.settings.goblins.logTime };
855 | }
856 | });
857 |
858 | mod.settings.merchants.regions.forEach((region, regionIndex) => {
859 | const entry = mod.settings.merchants.regions[regionIndex];
860 |
861 | if (entry.logTime !== undefined && isNumber(entry.logTime)) {
862 | entry.logTime = { [serverId]: entry.logTime };
863 | }
864 | });
865 |
866 | mod.settings.world_bosses.regions.forEach((region, regionIndex) =>
867 | region.npcs.forEach((boss, bossIndex) => {
868 | const entry = mod.settings.world_bosses.regions[regionIndex].npcs[bossIndex];
869 |
870 | if (entry.logTime !== undefined && isNumber(entry.logTime)) {
871 | entry.logTime = { [serverId]: entry.logTime };
872 | }
873 | })
874 | );
875 |
876 | mod.settings.raid_bosses.regions.forEach((region, regionIndex) =>
877 | region.npcs.forEach((boss, bossIndex) => {
878 | const entry = mod.settings.raid_bosses.regions[regionIndex].npcs[bossIndex];
879 |
880 | if (entry.logTime !== undefined && isNumber(entry.logTime)) {
881 | entry.logTime = { [serverId]: entry.logTime };
882 | }
883 | })
884 | );
885 | }
886 | };
887 |
888 | class BamHpBar {
889 | constructor(mod, hook = false) {
890 | this.mod = mod;
891 | this.msg = new TeraMessage(mod);
892 | this.hooks = new Set();
893 |
894 | this.gageInfo = {
895 | "id": 0n,
896 | "huntingZoneId": 0,
897 | "templateId": 0,
898 | "target": 0n,
899 | "unk1": 0,
900 | "unk2": 0,
901 | "curHp": 16000000000n,
902 | "maxHp": 16000000000n,
903 | "unk3": 1
904 | };
905 |
906 | if (hook) {
907 | mod.hook("S_SPAWN_NPC", mod.majorPatchVersion >= 101 ? 12 : 11, event => this.spawnNpc(event));
908 | }
909 | }
910 |
911 | updateHp() {
912 | this.mod.send("S_BOSS_GAGE_INFO", 3, this.gageInfo);
913 | }
914 |
915 | // 0: 0% <= hp < 20%, 1: 20% <= hp < 40%, 2: 40% <= hp < 60%, 3: 60% <= hp < 80%, 4: 80% <= hp < 100%, 5: 100% hp
916 | correctHp(stage) {
917 | const bossHpStage = BigInt(20 * (1 + stage));
918 |
919 | if (this.gageInfo.curHp * 100n / this.gageInfo.maxHp > bossHpStage) {
920 | this.gageInfo.curHp = this.gageInfo.maxHp * bossHpStage / 100n;
921 | this.updateHp();
922 |
923 | this.msg.chat(`HP ${this.msg.YEL(String(bossHpStage))}%`);
924 | }
925 | }
926 |
927 | spawnNpc(e) {
928 | if (e.walkSpeed != 240) return;
929 |
930 | switch (e.templateId) {
931 | case 5001: // Ortan
932 | e.shapeId = 303730;
933 | e.huntingZoneId = 994;
934 | e.templateId = 1000; // Nightmare Birchback
935 | this.load(e);
936 | return true;
937 |
938 | case 501: // Hazard
939 | e.shapeId = 303740;
940 | e.huntingZoneId = 777;
941 | e.templateId = 77730; // Kerkion
942 | this.load(e);
943 | return true;
944 |
945 | case 4001: // Cerrus
946 | e.shapeId = 303750;
947 | e.huntingZoneId = 994;
948 | e.templateId = 1000; // Nightmare Birchback
949 | this.load(e);
950 | return true;
951 | }
952 | }
953 |
954 | load(e) {
955 | this.gageInfo.id = e.gameId;
956 | this.gageInfo.curHp = this.gageInfo.maxHp;
957 |
958 | this.correctHp(e.hpLevel);
959 |
960 | if (e.mode) {
961 | // this.msg.chat(`You missed ~ ${this.msg.YEL(Math.round((99999999 - e.remainingEnrageTime) / 1000))} sec. of the fight`);
962 | }
963 |
964 | if (e.hpLevel == 5) {
965 | this.msg.chat("HP 100%");
966 | } else if (e.hpLevel == 0) {
967 | this.msg.chat(`HP < ${this.msg.RED("20%")} !!!`);
968 | }
969 |
970 | if (this.hooks.size == 0) {
971 | this.mod.setTimeout(() => this.updateHp(), 1000);
972 |
973 | this.hook("S_NPC_STATUS", 2, event => {
974 | if (event.gameId === this.gageInfo.id) {
975 | this.correctHp(event.hpLevel);
976 | }
977 | });
978 |
979 | this.hook("S_EACH_SKILL_RESULT", this.mod.majorPatchVersion >= 110 ? 15 : 14, event => {
980 | if (event.target === this.gageInfo.id && event.type === 1) {
981 | this.gageInfo.curHp -= event.value;
982 | this.updateHp();
983 | }
984 | });
985 |
986 | this.hook("S_DESPAWN_NPC", 3, event => {
987 | if (event.gameId === this.gageInfo.id) {
988 | this.unload();
989 | }
990 | });
991 | }
992 | }
993 |
994 | unload() {
995 | this.hooks.forEach(h => this.mod.unhook(h));
996 | this.hooks.clear();
997 | }
998 |
999 | hook() {
1000 | this.hooks.add(this.mod.hook(...arguments));
1001 | }
1002 | }
1003 |
1004 | class TeraMessage {
1005 | constructor(mod) {
1006 | this.mod = mod;
1007 | }
1008 |
1009 | clr(text, hexColor) {
1010 | return `${text}`;
1011 | }
1012 |
1013 | RED(text) {
1014 | return `${text}`;
1015 | }
1016 |
1017 | BLU(text) {
1018 | return `${text}`;
1019 | }
1020 |
1021 | YEL(text) {
1022 | return `${text}`;
1023 | }
1024 |
1025 | TIP(text) {
1026 | return `${text}`;
1027 | }
1028 |
1029 | GRY(text) {
1030 | return `${text}`;
1031 | }
1032 |
1033 | PIK(text) {
1034 | return `${text}`;
1035 | }
1036 |
1037 | chat(msg) {
1038 | this.mod.command.message(msg);
1039 | }
1040 |
1041 | party(msg) {
1042 | this.mod.send("S_CHAT", this.mod.majorPatchVersion >= 108 ? 4 : 3, {
1043 | "channel": 21,
1044 | "message": msg
1045 | });
1046 | }
1047 |
1048 | raids(msg) {
1049 | this.mod.send("S_CHAT", this.mod.majorPatchVersion >= 108 ? 4 : 3, {
1050 | "channel": 25,
1051 | "message": msg
1052 | });
1053 | }
1054 |
1055 | alert(msg, type) {
1056 | this.mod.send("S_DUNGEON_EVENT_MESSAGE", 2, {
1057 | "type": type,
1058 | "chat": false,
1059 | "channel": 0,
1060 | "message": msg
1061 | });
1062 | }
1063 | }
--------------------------------------------------------------------------------
/settings_migrator.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-param-reassign */
2 | const DefaultSettings = {
3 | "enabled": true,
4 | "alert": true,
5 | "notice": true,
6 | "message": true,
7 | "marker": true,
8 | "teleport": false,
9 | "hpbar": true,
10 | "itemId": 88704,
11 | "language": "auto",
12 |
13 | /* =============================== WORLD BOSSES =============================== */
14 | "world_bosses": {
15 | "regions": [
16 | {
17 | "name": "Ostgarath",
18 | "name_RU": "Остгарат",
19 | "zoneId": 7004,
20 | "npcs": [
21 | {
22 | "logTime": {},
23 | "huntingZoneId": 10,
24 | "templateId": 99,
25 | "name": "Divine Reaver (Serpentis Isle)",
26 | "name_RU": "Божественный грабитель (Остров Серпентис)",
27 | "locations": [
28 | {
29 | "map": "1_6_58",
30 | "x": 77865.546875,
31 | "y": 90398.1796875,
32 | "z": 1485.700439453125
33 | },
34 | {
35 | "map": "1_6_58",
36 | "x": 74800.8203125,
37 | "y": 95632.515625,
38 | "z": 1409.962158203125
39 | }
40 | ]
41 | },
42 | {
43 | "logTime": {},
44 | "huntingZoneId": 4,
45 | "templateId": 5011,
46 | "name": "Tempest Kanash (Fyrmount)",
47 | "name_RU": "Бурный канаш (Фирмаунт)",
48 | "locations": [
49 | {
50 | "map": "1_6_56",
51 | "x": 105778.8359375,
52 | "y": 68078.3359375,
53 | "z": 3210.118408203125
54 | },
55 | {
56 | "map": "1_6_56",
57 | "x": 113024.9453125,
58 | "y": 68483.8671875,
59 | "z": 4283.421875
60 | },
61 | {
62 | "map": "1_6_56",
63 | "x": 111394.5234375,
64 | "y": 81552.5078125,
65 | "z": 6620.1884765625
66 | },
67 | {
68 | "map": "1_6_56",
69 | "x": 111367.078125,
70 | "y": 89668.9296875,
71 | "z": 7605.767578125
72 | },
73 | {
74 | "map": "1_6_56",
75 | "x": 119100.828125,
76 | "y": 88552.7109375,
77 | "z": 6412.33349609375
78 | }
79 | ]
80 | }
81 | ]
82 | },
83 | {
84 | "name": "Val Elenium",
85 | "name_RU": "Вал-Элениум",
86 | "zoneId": 7014,
87 | "npcs": [
88 | {
89 | "logTime": {},
90 | "huntingZoneId": 38,
91 | "templateId": 35,
92 | "name": "Nyxarras (Sienna Canyon)",
93 | "name_RU": "Никсаррас (Каньон Сиенна)",
94 | "locations": [
95 | {
96 | "map": "1_11_113",
97 | "x": -89890,
98 | "y": -14445.447265625,
99 | "z": 2001.6065673828125
100 | },
101 | {
102 | "map": "1_11_113",
103 | "x": -93309.3203125,
104 | "y": -8923.47265625,
105 | "z": 1535.0086669921875
106 | },
107 | {
108 | "map": "1_11_113",
109 | "x": -83349.4296875,
110 | "y": 506.79229736328125,
111 | "z": 1282.063232421875
112 | }
113 | ]
114 | }
115 | ]
116 | },
117 | {
118 | "name": "Sylvanoth",
119 | "name_RU": "Силванот",
120 | "zoneId": 7021,
121 | "npcs": [
122 | {
123 | "logTime": {},
124 | "huntingZoneId": 57,
125 | "templateId": 33,
126 | "name": "Betsael (Amena Quatla)",
127 | "name_RU": "Бетсаэль (Амена-Кватла)",
128 | "locations": [
129 | {
130 | "map": "1_20_226",
131 | "x": -54613.984375,
132 | "y": 41646.12109375,
133 | "z": 2783.886962890625
134 | },
135 | {
136 | "map": "1_20_226",
137 | "x": -50985.4375,
138 | "y": 49416.66015625,
139 | "z": 4229.94775390625
140 | },
141 | {
142 | "map": "1_20_226",
143 | "x": -59299.30859375,
144 | "y": 53569.3046875,
145 | "z": 4173.20263671875
146 | },
147 | {
148 | "map": "1_20_226",
149 | "x": -68672.4453125,
150 | "y": 48410.7890625,
151 | "z": 4418.21826171875
152 | },
153 | {
154 | "map": "1_20_226",
155 | "x": -71237.3359375,
156 | "y": 56477.3359375,
157 | "z": 4288.76171875
158 | }
159 | ]
160 | }
161 | ]
162 | },
163 | {
164 | "name": "Lorcada",
165 | "name_RU": "Лоркада",
166 | "zoneId": 7022,
167 | "npcs": [
168 | {
169 | "logTime": {},
170 | "huntingZoneId": 51,
171 | "templateId": 7011,
172 | "name": "Linyphi (Vale of Spires)",
173 | "name_RU": "Линифи (Долина пиков)",
174 | "locations": [
175 | {
176 | "map": "1_19_207",
177 | "x": 6778.56884765625,
178 | "y": 48135.4375,
179 | "z": 7111.451171875
180 | },
181 | {
182 | "map": "1_19_207",
183 | "x": 2881.635986328125,
184 | "y": 52862.07421875,
185 | "z": 6607.40576171875
186 | },
187 | {
188 | "map": "1_19_207",
189 | "x": 10062.0693359375,
190 | "y": 53097.66015625,
191 | "z": 7785.58837890625
192 | },
193 | {
194 | "map": "1_19_207",
195 | "x": 9971.68359375,
196 | "y": 61297.21484375,
197 | "z": 7764.48486328125
198 | }
199 | ]
200 | },
201 | {
202 | "logTime": {},
203 | "huntingZoneId": 52,
204 | "templateId": 9050,
205 | "name": "Yunaras Snaggletooth (Plain of the Damned)",
206 | "name_RU": "Юнарас (Долина Проклятых)",
207 | "locations": [
208 | {
209 | "map": "1_19_208",
210 | "x": -3337.882080078125,
211 | "y": 33507.6328125,
212 | "z": 5413.1103515625
213 | },
214 | {
215 | "map": "1_19_208",
216 | "x": -3870.1279296875,
217 | "y": 50643.453125,
218 | "z": 6306.4638671875
219 | },
220 | {
221 | "map": "1_19_208",
222 | "x": 2417.086669921875,
223 | "y": 60137.69140625,
224 | "z": 6119.5859375
225 | },
226 | {
227 | "map": "1_19_208",
228 | "x": 1419.126953125,
229 | "y": 69333.484375,
230 | "z": 7087.1298828125
231 | }
232 | ]
233 | }
234 | ]
235 | }
236 | ]
237 | },
238 |
239 | /* =============================== RAID BOSSES =============================== */
240 | "raid_bosses": {
241 | "regions": [
242 | {
243 | "name": "Essenia",
244 | "name_RU": "Эссения",
245 | "zoneId": 7011,
246 | "npcs": [
247 | {
248 | "huntingZoneId": 26,
249 | "templateId": 5001,
250 | "logTime": {},
251 | "name": "Ortan (Blessing Basin)",
252 | "name_RU": "Ортан (Блаженное озеро)"
253 | }
254 | ]
255 | },
256 | {
257 | "name": "Val Palrada",
258 | "name_RU": "Вал-Палрада",
259 | "zoneId": 7013,
260 | "npcs": [
261 | {
262 | "huntingZoneId": 39,
263 | "templateId": 501,
264 | "logTime": {},
265 | "name": "Hazard (Quarantine Zone)",
266 | "name_RU": "Хазар (Зона карантина)"
267 | }
268 | ]
269 | },
270 | {
271 | "name": "Lorcada",
272 | "name_RU": "Лоркада",
273 | "zoneId": 7022,
274 | "npcs": [
275 | {
276 | "huntingZoneId": 51,
277 | "templateId": 4001,
278 | "logTime": {},
279 | "name": "Cerrus",
280 | "name_RU": "Кэлос"
281 | }
282 | ]
283 | }
284 | ]
285 | },
286 |
287 | /* =============================== OTHER BOSSES & NPCS =============================== */
288 | "others": {
289 | "regions": [
290 | {
291 | "npcs": [
292 | // Guild boss
293 | {
294 | "huntingZoneId": 29,
295 | "templateId": 2001,
296 | "name": "Anansha (Timeless Woods)",
297 | "name_RU": "Ананша (Извечный лес)"
298 | },
299 | {
300 | "huntingZoneId": 34,
301 | "templateId": 2002,
302 | "name": "Frygaras (Frost Reach)",
303 | "name_RU": "Фрайгарас (Морозный предел)"
304 | },
305 | {
306 | "huntingZoneId": 34,
307 | "templateId": 2003,
308 | "name": "Sabranak (Frost Reach)",
309 | "name_RU": "Сабранак (Морозный предел)"
310 | },
311 | {
312 | "huntingZoneId": 152,
313 | "templateId": 2001,
314 | "name": "Anansha (Велика: Эпоха смуты)",
315 | "name_RU": "Ананша (Велика: Эпоха смуты)"
316 | },
317 | {
318 | "huntingZoneId": 152,
319 | "templateId": 2002,
320 | "name": "Frygaras (Велика: Эпоха смуты)",
321 | "name_RU": "Фрайгарас (Велика: Эпоха смуты)"
322 | },
323 | {
324 | "huntingZoneId": 152,
325 | "templateId": 2003,
326 | "name": "Sabranak (Велика: Эпоха смуты)",
327 | "name_RU": "Сабранак (Велика: Эпоха смуты)"
328 | },
329 | {
330 | "huntingZoneId": 152,
331 | "templateId": 7001,
332 | "name": "Anansha (Велика: Эпоха смуты)",
333 | "name_RU": "Ананша (Велика: Эпоха смуты)"
334 | },
335 | {
336 | "huntingZoneId": 152,
337 | "templateId": 7002,
338 | "name": "Frygaras (Велика: Эпоха смуты)",
339 | "name_RU": "Фрайгарас (Велика: Эпоха смуты)"
340 | },
341 | {
342 | "huntingZoneId": 152,
343 | "templateId": 7003,
344 | "name": "Sabranak (Велика: Эпоха смуты)",
345 | "name_RU": "Сабранак (Велика: Эпоха смуты)"
346 | },
347 |
348 | // Exodor elite mobs
349 | {
350 | "huntingZoneId": 2020,
351 | "templateId": 1100,
352 | "name": "[Exodor] Supreme Draakon Pursuer",
353 | "name_RU": "[Эксодор] Элитный высший дракон-преследователь"
354 | },
355 | {
356 | "huntingZoneId": 2020,
357 | "templateId": 1101,
358 | "name": "[Exodor] Draakon Guard-Captain",
359 | "name_RU": "[Эксодор] Элитный высший дракон-советник"
360 | },
361 | {
362 | "huntingZoneId": 2020,
363 | "templateId": 1102,
364 | "name": "[Exodor] Supreme Draakon Dominator",
365 | "name_RU": "[Эксодор] Элитный высший дракон-покоритель"
366 | },
367 | {
368 | "huntingZoneId": 2020,
369 | "templateId": 1200,
370 | "name": "[Exodor] Elite Naga Priest",
371 | "name_RU": "[Эксодор] Элитный наг-жрец"
372 | },
373 | {
374 | "huntingZoneId": 2020,
375 | "templateId": 1201,
376 | "name": "[Exodor] Elite Naga Knight",
377 | "name_RU": "[Эксодор] Элитный наг-рыцарь"
378 | },
379 | {
380 | "huntingZoneId": 2020,
381 | "templateId": 1202,
382 | "name": "[Exodor] Elite Naga Executioner",
383 | "name_RU": "[Эксодор] Элитный наг-пристав"
384 | },
385 | {
386 | "huntingZoneId": 2020,
387 | "templateId": 1300,
388 | "name": "[Exodor] Manuk",
389 | "name_RU": "[Эксодор] Манук"
390 | },
391 | {
392 | "huntingZoneId": 2020,
393 | "templateId": 1400,
394 | "name": "[Exodor] Axcellob",
395 | "name_RU": "[Эксодор] Аксилоп"
396 | },
397 | {
398 | "huntingZoneId": 2020,
399 | "templateId": 1500,
400 | "name": "[Exodor] Zemces",
401 | "name_RU": "[Эксодор] Джемезис"
402 | },
403 | {
404 | "huntingZoneId": 2020,
405 | "templateId": 1600,
406 | "name": "[Exodor] Взбешенный красный лишайник",
407 | "name_RU": "[Эксодор] Взбешенный красный лишайник"
408 | },
409 | {
410 | "huntingZoneId": 2020,
411 | "templateId": 1601,
412 | "name": "[Exodor] Thorny Eyestalker",
413 | "name_RU": "[Эксодор] Циклоп"
414 | },
415 | {
416 | "huntingZoneId": 2020,
417 | "templateId": 1700,
418 | "name": "[Exodor] Vekes",
419 | "name_RU": "[Эксодор] Бэкис"
420 | },
421 |
422 | // Exodor monsters
423 | {
424 | "huntingZoneId": 2020,
425 | "templateId": 7000,
426 | "name": "[Exodor] Tomb Robber Bari",
427 | "name_RU": "[Эксодор] Гробокопатель Бари"
428 | },
429 | {
430 | "huntingZoneId": 2020,
431 | "templateId": 7001,
432 | "name": "[Exodor] Tomb Robber Robin",
433 | "name_RU": "[Эксодор] Гробокопатель Робин"
434 | },
435 | {
436 | "huntingZoneId": 2020,
437 | "templateId": 7002,
438 | "name": "[Exodor] Tomb Robber Gibi",
439 | "name_RU": "[Эксодор] Гробокопатель Гиби"
440 | },
441 |
442 | // Caiman
443 | {
444 | "huntingZoneId": 26,
445 | "templateId": 6001,
446 | "name": "Caiman Wanderer",
447 | "name_RU": "Кайман-бродяга"
448 | },
449 | {
450 | "huntingZoneId": 27,
451 | "templateId": 6001,
452 | "name": "Caiman Wanderer",
453 | "name_RU": "Кайман-бродяга"
454 | },
455 | {
456 | "huntingZoneId": 29,
457 | "templateId": 6002,
458 | "name": "Caiman Wanderer",
459 | "name_RU": "Кайман-бродяга"
460 | },
461 | {
462 | "huntingZoneId": 30,
463 | "templateId": 6001,
464 | "name": "Caiman Wanderer",
465 | "name_RU": "Кайман-бродяга"
466 | },
467 | {
468 | "huntingZoneId": 38,
469 | "templateId": 6001,
470 | "name": "Caiman Wanderer",
471 | "name_RU": "Кайман-бродяга"
472 | },
473 | {
474 | "huntingZoneId": 181,
475 | "templateId": 6001,
476 | "name": "Caiman Wanderer",
477 | "name_RU": "Кайман-бродяга"
478 | },
479 |
480 | // Event
481 | {
482 | "huntingZoneId": 1023,
483 | "templateId": 200002,
484 | "name": "(Event) Grumpy Santa",
485 | "name_RU": "(Ивент) Санта-король"
486 | },
487 | {
488 | "huntingZoneId": 1023,
489 | "templateId": 200003,
490 | "name": "(Event) Tiny Santa",
491 | "name_RU": "(Ивент) Крошечный Санта"
492 | },
493 | {
494 | "huntingZoneId": 1023,
495 | "templateId": 240035,
496 | "name": "(Event) Argon Vanguard",
497 | "name_RU": "(Ивент) Аргонский передовой отряд"
498 | },
499 | {
500 | "huntingZoneId": 1023,
501 | "templateId": 240036,
502 | "name": "(Event) Argon Vanguard",
503 | "name_RU": "(Ивент) Аргонский передовой отряд"
504 | },
505 |
506 | // Event Menma's
507 | {
508 | "huntingZoneId": 54,
509 | "templateId": 101,
510 | "name": "(Event) Sakura Ghilliedhu",
511 | "name_RU": "(Ивент) Древень Сакуры"
512 | },
513 | // Habere
514 | {
515 | "huntingZoneId": 52,
516 | "templateId": 99999997,
517 | "name": "(Event) Santa",
518 | "name_RU": "(Ивент) Полный санта"
519 | },
520 | {
521 | "huntingZoneId": 52,
522 | "templateId": 99999998,
523 | "name": "(Event) Mini santa",
524 | "name_RU": "(Ивент) Крошечный санта"
525 | },
526 | // Acarum
527 | {
528 | "huntingZoneId": 32,
529 | "templateId": 99999997,
530 | "name": "(Event) Santa",
531 | "name_RU": "(Ивент) Полный санта"
532 | },
533 | {
534 | "huntingZoneId": 32,
535 | "templateId": 99999998,
536 | "name": "(Event) Mini santa",
537 | "name_RU": "(Ивент) Крошечный санта"
538 | },
539 | // Bleakrock
540 | {
541 | "huntingZoneId": 34,
542 | "templateId": 99999997,
543 | "name": "(Event) Santa",
544 | "name_RU": "(Ивент) Полный санта"
545 | },
546 | {
547 | "huntingZoneId": 34,
548 | "templateId": 99999998,
549 | "name": "(Event) Mini santa",
550 | "name_RU": "(Ивент) Крошечный санта"
551 | }
552 | ]
553 | }
554 | ]
555 | },
556 |
557 | /* =============================== MERCHANT GOBLINS =============================== */
558 | "goblins": {
559 | "logTime": {},
560 | "regions": [
561 | // Allemantheia
562 | {
563 | "logDiff": 10800,
564 | "name": "Allemantheia",
565 | "name_RU": "Аллемантея",
566 | "npcs": [
567 | {
568 | "huntingZoneId": 72,
569 | "templateId": 1276,
570 | "name": "Mystery Market Coin Vendor (Allemantheia)",
571 | "name_RU": "Гоблин, ответственный за доставку (Аллемантея)"
572 | }
573 | ]
574 | },
575 | // Highwatch
576 | {
577 | "logDiff": 32400,
578 | "name": "Highwatch",
579 | "name_RU": "Верхний Дозор",
580 | "npcs": [
581 | {
582 | "huntingZoneId": 183,
583 | "templateId": 1276,
584 | "name": "Mystery Market Coin Vendor (Highwatch)",
585 | "name_RU": "Гоблин, ответственный за доставку (Верхний Дозор)"
586 | }
587 | ]
588 | },
589 | // Velika
590 | {
591 | "logDiff": 54000,
592 | "name": "Velika",
593 | "name_RU": "Велика",
594 | "npcs": [
595 | {
596 | "huntingZoneId": 63,
597 | "templateId": 1276,
598 | "name": "Mystery Market Coin Vendor (Velika)",
599 | "name_RU": "Гоблин, ответственный за доставку (Велика)"
600 | },
601 | {
602 | "huntingZoneId": 63,
603 | "templateId": 1284,
604 | "name": "Mystery Market Coin Vendor (Velika)",
605 | "name_RU": "Гоблин, ответственный за доставку (Велика)"
606 | }
607 | ]
608 | },
609 | // Kaiator
610 | {
611 | "logDiff": 75600,
612 | "name": "Kaiator",
613 | "name_RU": "Кайатор",
614 | "npcs": [
615 | {
616 | "huntingZoneId": 84,
617 | "templateId": 1276,
618 | "name": "Mystery Market Coin Vendor (Kaiator)",
619 | "name_RU": "Гоблин, ответственный за доставку (Кайатор)"
620 | }
621 | ]
622 | }
623 | ]
624 | },
625 |
626 | /* =============================== MERCHANTS =============================== */
627 | "merchants": {
628 | "logTime": {},
629 | "regions": [
630 | // Velika
631 | {
632 | "logTime": {},
633 | "logIntervalMin": 16200,
634 | "logIntervalMax": 18000,
635 | "name": "Around the Velika",
636 | "name_RU": "Окрестности Велики",
637 | "zoneId": 7005,
638 | "npcs": [
639 | {
640 | "huntingZoneId": 63,
641 | "templateId": 1278,
642 | "name": "Veracun",
643 | "name_RU": "Веракун",
644 | "locations": [
645 | { // I
646 | "map": "1_1_2",
647 | "x": -8196.37890625,
648 | "y": -3969.462646484375,
649 | "z": 576.148193359375
650 | },
651 | { // II
652 | "map": "1_1_2",
653 | "x": -11027.6904296875,
654 | "y": -6868.259765625,
655 | "z": 507.8714599609375
656 | },
657 | { // III
658 | "map": "1_1_2",
659 | "x": 12666.140625,
660 | "y": 7537.5966796875,
661 | "z": 973.897216796875
662 | },
663 | { // IV
664 | "map": "1_1_2",
665 | "x": 5113.50732421875,
666 | "y": -10953.166015625,
667 | "z": 447.5827941894531
668 | },
669 | { // V
670 | "map": "1_1_2",
671 | "x": 12789.47265625,
672 | "y": -6044.18603515625,
673 | "z": 465.1011047363281
674 | },
675 | { // VI
676 | "map": "1_1_2",
677 | "x": 18762.041015625,
678 | "y": 1844.900146484375,
679 | "z": 2073.96142578125
680 | },
681 | { // VII
682 | "map": "1_1_2",
683 | "x": 18514.875,
684 | "y": 6306.50927734375,
685 | "z": 2074.3603515625
686 | },
687 | { // VIII
688 | "map": "1_1_2",
689 | "x": 22064.4765625,
690 | "y": 13501.96875,
691 | "z": 619.4597778320312
692 | },
693 | { // IX
694 | "map": "1_1_2",
695 | "x": -4914.10693359375,
696 | "y": -18774.08984375,
697 | "z": -268.321044921875
698 | },
699 | { // X
700 | "map": "1_1_2",
701 | "x": 11998.9326171875,
702 | "y": 18841.560546875,
703 | "z": 961.3118896484375
704 | }
705 | ]
706 | }
707 | ]
708 | },
709 | // Allemantheia
710 | {
711 | "logTime": {},
712 | "logIntervalMin": 16200,
713 | "logIntervalMax": 18000,
714 | "name": "Around the Allemantheia",
715 | "name_RU": "Окрестности Аллемантеи",
716 | "zoneId": 2,
717 | "npcs": [
718 | {
719 | "huntingZoneId": 72,
720 | "templateId": 1278,
721 | "name": "Alluman",
722 | "name_RU": "Аллума",
723 | "locations": [
724 | { // I
725 | "map": "1_10_102",
726 | "x": -20180.287109375,
727 | "y": 17139.005859375,
728 | "z": 2417.02734375
729 | },
730 | { // II
731 | "map": "1_10_102",
732 | "x": -5922.76513671875,
733 | "y": 24888.5859375,
734 | "z": 4589.75927734375
735 | },
736 | { // III
737 | "map": "1_10_102",
738 | "x": 3014.513671875,
739 | "y": 21300.3359375,
740 | "z": 5453.880859375
741 | },
742 | { // IV
743 | "map": "1_10_102",
744 | "x": 9504.1015625,
745 | "y": 10871.46875,
746 | "z": 5015.65673828125
747 | },
748 | { // V
749 | "map": "1_10_102",
750 | "x": 10178.01953125,
751 | "y": -14576.3486328125,
752 | "z": 6138.36279296875
753 | },
754 | { // VI
755 | "map": "1_10_103",
756 | "x": 3148.271728515625,
757 | "y": -29629.748046875,
758 | "z": 1488.6566162109375
759 | },
760 | { // VII
761 | "map": "1_10_103",
762 | "x": -3562.05224609375,
763 | "y": -37224.5078125,
764 | "z": 710.3406372070312
765 | },
766 | { // VIII
767 | "map": "1_10_103",
768 | "x": -14008.767578125,
769 | "y": -23920.705078125,
770 | "z": -524.1255493164062
771 | }
772 | ]
773 | }
774 | ]
775 | },
776 | // Kaiator
777 | {
778 | "logTime": {},
779 | "logIntervalMin": 16200,
780 | "logIntervalMax": 18000,
781 | "name": "Around the Kaiator",
782 | "name_RU": "Окрестности Кайатора",
783 | "zoneId": 3,
784 | "npcs": [
785 | {
786 | "huntingZoneId": 84,
787 | "templateId": 1278,
788 | "name": "Kaidera",
789 | "name_RU": "Кай Тера",
790 | "locations": [
791 | { // I
792 | "map": "1_18_202",
793 | "x": -3189.919921875,
794 | "y": -10930.4052734375,
795 | "z": 3134.489501953125
796 | },
797 | { // II
798 | "map": "1_18_202",
799 | "x": -23993.099609375,
800 | "y": -4188.8486328125,
801 | "z": 3942.721435546875
802 | },
803 | { // III
804 | "map": "1_18_202",
805 | "x": -15876.1689453125,
806 | "y": -5627.4453125,
807 | "z": 4589.9462890625
808 | },
809 | { // IV
810 | "map": "1_18_202",
811 | "x": -17723.96875,
812 | "y": 12506.95703125,
813 | "z": 4960.28857421875
814 | },
815 | { // V
816 | "map": "1_18_202",
817 | "x": -55.379817962646484,
818 | "y": 7207.630859375,
819 | "z": 3984.510009765625
820 | },
821 | { // VI
822 | "map": "1_18_202",
823 | "x": 6303.4697265625,
824 | "y": 9831.658203125,
825 | "z": 5025.73291015625
826 | },
827 | { // VII
828 | "map": "1_18_202",
829 | "x": 7308.0908203125,
830 | "y": 20434.470703125,
831 | "z": 5853.220703125
832 | },
833 | { // VIII
834 | "map": "1_18_202",
835 | "x": 11659.8466796875,
836 | "y": 23423.890625,
837 | "z": 5539.515625
838 | }
839 | ]
840 | }
841 | ]
842 | },
843 | // Island of Dawn
844 | {
845 | "logTime": {},
846 | "logIntervalMin": 10800,
847 | "logIntervalMax": 14400,
848 | "name": "Island of Dawn",
849 | "name_RU": "Остров Зари",
850 | "zoneId": 13,
851 | "npcs": [
852 | {
853 | "huntingZoneId": 13,
854 | "templateId": 1271,
855 | "name": "Vardung",
856 | "name_RU": "Бардун",
857 | "locations": [
858 | { // Город
859 | "map": "1_2_8",
860 | "x": 92205.84375,
861 | "y": -88367.578125,
862 | "z": -4571.03564453125
863 | },
864 | { // Верх
865 | "map": "1_2_7",
866 | "x": 81205.796875,
867 | "y": -80864.703125,
868 | "z": -4410.46240234375
869 | },
870 | { // Лево
871 | "map": "1_2_7",
872 | "x": 64286.90625,
873 | "y": -84473.1640625,
874 | "z": -3793.819091796875
875 | },
876 | { // Архив (скрытая)
877 | "map": "1_2_7",
878 | "x": 70335.46875,
879 | "y": -77825.5625,
880 | "z": -2312.162841796875
881 | },
882 | { // Пик (скрытая)
883 | "map": "1_2_7",
884 | "x": 73421.296875,
885 | "y": -73028.4609375,
886 | "z": -1225.211669921875
887 | },
888 | { // Гидраты (скрытая)
889 | "map": "1_2_7",
890 | "x": 64155.69921875,
891 | "y": -64051.18359375,
892 | "z": -4148.3046875
893 | },
894 | { // Низ
895 | "map": "1_2_7",
896 | "x": 52726.37890625,
897 | "y": -69800.0859375,
898 | "z": -5661.28271484375
899 | },
900 | { // Наги (скрытая)
901 | "map": "1_2_7",
902 | "x": 58768.55078125,
903 | "y": -75743.46875,
904 | "z": -5727.46240234375
905 | },
906 | { // Святилище (скрытая)
907 | "map": "1_2_7",
908 | "x": 55455.19140625,
909 | "y": -82052.3125,
910 | "z": -4196.96240234375
911 | },
912 | { // Камень (скрытая)
913 | "map": "1_2_7",
914 | "x": 56862.08203125,
915 | "y": -86059.0546875,
916 | "z": -3718.2373046875
917 | }
918 | ]
919 | }
920 | ]
921 | },
922 | // Val Oriyn
923 | {
924 | "logTime": {},
925 | "logIntervalMin": 18000,
926 | "logIntervalMax": 18000,
927 | "name": "Val Oriyn",
928 | "name_RU": "Вал-Орин",
929 | "zoneId": 7031,
930 | "npcs": [
931 | {
932 | "huntingZoneId": 172,
933 | "templateId": 1271,
934 | "name": "Varrek (Savage Reach)",
935 | "name_RU": "Варэку (Дикарский Предел)",
936 | "locations": [
937 | { // Низ
938 | "map": "1_24_172001",
939 | "x": -10132.833984375,
940 | "y": -18499.970703125,
941 | "z": -652.809814453125
942 | },
943 | { // Скрытая
944 | "map": "1_24_172001",
945 | "x": -9207.423828125,
946 | "y": -35376.453125,
947 | "z": 194.09144592285156
948 | },
949 | { // Сайрекс
950 | "map": "1_24_172001",
951 | "x": -3314.078369140625,
952 | "y": -12210.3681640625,
953 | "z": 1206.2315673828125
954 | }
955 | ]
956 | },
957 | {
958 | "huntingZoneId": 181,
959 | "templateId": 1271,
960 | "name": "Varrek (Ex Prima)",
961 | "name_RU": "Варэку (Экс-Прима)",
962 | "locations": [
963 | { // Лево
964 | "map": "1_24_181001",
965 | "x": 27799.720703125,
966 | "y": -10077.744140625,
967 | "z": 3633.656982421875
968 | },
969 | { // ГХ (скрытая)
970 | "map": "1_24_181001",
971 | "x": 30891.29296875,
972 | "y": -26839.0234375,
973 | "z": 1891.73828125
974 | }
975 | ]
976 | },
977 | {
978 | "huntingZoneId": 182,
979 | "templateId": 1271,
980 | "name": "Varrek (Spring Valley)",
981 | "name_RU": "Варэку (Долина Источников)",
982 | "locations": [
983 | { // Город
984 | "map": "1_24_182003",
985 | "x": 6463.14306640625,
986 | "y": -15095.6123046875,
987 | "z": 1433.274658203125
988 | },
989 | { // Право (скрытая)
990 | "map": "1_24_182001",
991 | "x": 12190.7568359375,
992 | "y": -5131.7783203125,
993 | "z": 2547.6083984375
994 | }
995 | ]
996 | },
997 | {
998 | "huntingZoneId": 183,
999 | "templateId": 1278,
1000 | "name": "Varrek (Highwatch)",
1001 | "name_RU": "Варэку (Верхний Дозор)",
1002 | "locations": [
1003 | {
1004 | "map": "1_24_183002",
1005 | "x": 30793.97265625,
1006 | "y": 1986.8927001953125,
1007 | "z": 5203.45068359375
1008 | }
1009 | ]
1010 | },
1011 | {
1012 | "huntingZoneId": 191,
1013 | "templateId": 1271,
1014 | "name": "Varrek (Arx Umbra)",
1015 | "name_RU": "Варэку (Аркс-умбра)",
1016 | "locations": [
1017 | { // Лево
1018 | "map": "1_24_191001",
1019 | "x": 32139.62890625,
1020 | "y": 17056.46484375,
1021 | "z": 8330.5302734375
1022 | },
1023 | { // Мясо (скрытая)
1024 | "map": "1_24_191002",
1025 | "x": 40485.33984375,
1026 | "y": 36437.5078125,
1027 | "z": 9349.9697265625
1028 | }
1029 | ]
1030 | }
1031 | ]
1032 | },
1033 | // Arcadia (0:50)
1034 | {
1035 | "logDiff": 3000,
1036 | "name": "Arcadia",
1037 | "name_RU": "Аркадия",
1038 | "zoneId": 7001,
1039 | "npcs": [
1040 | {
1041 | "huntingZoneId": 2,
1042 | "templateId": 1271,
1043 | "name": "Arcun (Fey Forest / Lumbertown)",
1044 | "name_RU": "Аркун (Дивный лес / Деревня лесорубов)",
1045 | "locations": [
1046 | { // Город
1047 | "map": "1_3_13",
1048 | "x": 93044.4140625,
1049 | "y": -5436.291015625,
1050 | "z": 396.69873046875
1051 | },
1052 | { // Низ
1053 | "map": "1_3_13",
1054 | "x": 100194.3046875,
1055 | "y": -22028.08984375,
1056 | "z": 2315.01953125
1057 | },
1058 | { // Верх
1059 | "map": "1_3_13",
1060 | "x": 109847.015625,
1061 | "y": -10226.634765625,
1062 | "z": 2881.866455078125
1063 | }
1064 | ]
1065 | },
1066 | {
1067 | "huntingZoneId": 3,
1068 | "templateId": 1271,
1069 | "name": "Arcun (Oblivion Woods / Crescentia)",
1070 | "name_RU": "Аркун (Леса Забвения / Кресцентия)",
1071 | "locations": [
1072 | { // Город
1073 | "map": "1_3_11",
1074 | "x": 96464.84375,
1075 | "y": 17514.734375,
1076 | "z": 2927.4052734375
1077 | },
1078 | { // Застава
1079 | "map": "1_3_14",
1080 | "x": 107575.5546875,
1081 | "y": 16301.0498046875,
1082 | "z": 2716.98828125
1083 | }
1084 | ]
1085 | },
1086 | {
1087 | "huntingZoneId": 5,
1088 | "templateId": 1271,
1089 | "name": "Arcun (Tuwangi Mire)",
1090 | "name_RU": "Аркун (Трясина Туванги)",
1091 | "locations": [
1092 | { // Верх
1093 | "map": "1_3_15",
1094 | "x": 70988.234375,
1095 | "y": -9251.5810546875,
1096 | "z": 60.94783401489258
1097 | },
1098 | { // Низ
1099 | "map": "1_3_15",
1100 | "x": 51398.296875,
1101 | "y": -3308.03759765625,
1102 | "z": 75.64033508300781
1103 | }
1104 | ]
1105 | },
1106 | {
1107 | "huntingZoneId": 6,
1108 | "templateId": 1271,
1109 | "name": "Arcun (Valley of Titans)",
1110 | "name_RU": "Аркун (Долина титанов)",
1111 | "locations": [
1112 | { // Верх
1113 | "map": "1_3_16",
1114 | "x": 83740.84375,
1115 | "y": 7556.40283203125,
1116 | "z": 1294.02099609375
1117 | },
1118 | { // Центр
1119 | "map": "1_3_16",
1120 | "x": 72945.7265625,
1121 | "y": 16278.111328125,
1122 | "z": 2020.2679443359375
1123 | },
1124 | { // Низ
1125 | "map": "1_3_16",
1126 | "x": 62181.96875,
1127 | "y": 16616.408203125,
1128 | "z": 1879.3477783203125
1129 | }
1130 | ]
1131 | },
1132 | {
1133 | "huntingZoneId": 7,
1134 | "templateId": 1271,
1135 | "name": "Arcun (Celestial Hills)",
1136 | "name_RU": "Аркун (Небесные холмы)",
1137 | "locations": [
1138 | { // Центр
1139 | "map": "1_3_17",
1140 | "x": 61041.21875,
1141 | "y": 3119.500244140625,
1142 | "z": 1414.24755859375
1143 | },
1144 | { // Низ
1145 | "map": "1_3_17",
1146 | "x": 38529.76953125,
1147 | "y": -327.1700744628906,
1148 | "z": 510.364013671875
1149 | }
1150 | ]
1151 | }
1152 | ]
1153 | },
1154 | // Val Aureum (0:50)
1155 | {
1156 | "logDiff": 3000,
1157 | "name": "Val Aureum",
1158 | "name_RU": "Вал-Аурэум",
1159 | "zoneId": 7003,
1160 | "npcs": [
1161 | {
1162 | "huntingZoneId": 18,
1163 | "templateId": 1271,
1164 | "name": "Viadu (Colossal Ruins)",
1165 | "name_RU": "Виадун (Исполинские развалины)",
1166 | "locations": [
1167 | { // Право
1168 | "map": "1_5_41",
1169 | "x": -43639.37109375,
1170 | "y": -48491.7578125,
1171 | "z": 1466.1429443359375
1172 | },
1173 | { // Лево
1174 | "map": "1_5_41",
1175 | "x": -39723.99609375,
1176 | "y": -61915.80078125,
1177 | "z": 2258.112548828125
1178 | }
1179 | ]
1180 | },
1181 | {
1182 | "huntingZoneId": 19,
1183 | "templateId": 1271,
1184 | "name": "Viadu (Freeholds)",
1185 | "name_RU": "Виадун (Вольноземье)",
1186 | "locations": [
1187 | { // Бамарама
1188 | "map": "1_5_43",
1189 | "x": -25299.423828125,
1190 | "y": -25092.638671875,
1191 | "z": 179.30308532714844
1192 | },
1193 | { // Право
1194 | "map": "1_5_43",
1195 | "x": -26470.27734375,
1196 | "y": -1462.779052734375,
1197 | "z": 1887.4139404296875
1198 | }
1199 | ]
1200 | },
1201 | {
1202 | "huntingZoneId": 21,
1203 | "templateId": 1271,
1204 | "name": "Viadu (Basilisk Crag / Chebika)",
1205 | "name_RU": "Виадун (Утес Василисков / Чебика)",
1206 | "locations": [
1207 | { // Город
1208 | "map": "1_5_42",
1209 | "x": -32931.6328125,
1210 | "y": -21048.478515625,
1211 | "z": 595.482666015625
1212 | },
1213 | { // Центр
1214 | "map": "1_5_44",
1215 | "x": -49011.0703125,
1216 | "y": -10259.0283203125,
1217 | "z": 667.399169921875
1218 | }
1219 | ]
1220 | },
1221 | {
1222 | "huntingZoneId": 24,
1223 | "templateId": 1271,
1224 | "name": "Viadu (Aurum Road / Tulufan)",
1225 | "name_RU": "Виадун (Аурумская дорога / Тулуфан)",
1226 | "locations": [
1227 | { // Город
1228 | "map": "1_5_48",
1229 | "x": -55456.73828125,
1230 | "y": -38819.69140625,
1231 | "z": 2494.914306640625
1232 | },
1233 | { // Собор
1234 | "map": "1_5_49",
1235 | "x": -85476.3828125,
1236 | "y": -38920.67578125,
1237 | "z": 3447.2333984375
1238 | },
1239 | { // Центр
1240 | "map": "1_5_49",
1241 | "x": -65573.421875,
1242 | "y": -60034.58203125,
1243 | "z": 238.13490295410156
1244 | },
1245 | { // Лево
1246 | "map": "1_5_49",
1247 | "x": -64087.5703125,
1248 | "y": -75826.6875,
1249 | "z": 105.74998474121094
1250 | }
1251 | ]
1252 | }
1253 | ]
1254 | },
1255 | // Ostgarath (1:40)
1256 | {
1257 | "logDiff": 6000,
1258 | "name": "Ostgarath",
1259 | "name_RU": "Остгарат",
1260 | "zoneId": 7004,
1261 | "npcs": [
1262 | {
1263 | "huntingZoneId": 4,
1264 | "templateId": 1271,
1265 | "name": "Eteral (Fyrmount)",
1266 | "name_RU": "Иторо (Фирмаунт)",
1267 | "locations": [
1268 | { // Центр
1269 | "map": "1_6_56",
1270 | "x": 103183.8125,
1271 | "y": 66322.71875,
1272 | "z": 2727.681640625
1273 | },
1274 | { // Верх
1275 | "map": "1_6_56",
1276 | "x": 116370.984375,
1277 | "y": 74023.5078125,
1278 | "z": 4778.73046875
1279 | },
1280 | { // СБ
1281 | "map": "1_6_56",
1282 | "x": 115522.6796875,
1283 | "y": 96796.1015625,
1284 | "z": 7201.72216796875
1285 | }
1286 | ]
1287 | },
1288 | {
1289 | "huntingZoneId": 9,
1290 | "templateId": 1271,
1291 | "name": "Eteral (Ascension Valley / Castanica)",
1292 | "name_RU": "Иторо (Долина Вознесения / Кастаника)",
1293 | "locations": [
1294 | { // Город
1295 | "map": "1_6_57",
1296 | "x": 86424.5625,
1297 | "y": 74314.328125,
1298 | "z": 1973.4549560546875
1299 | },
1300 | { // Долина
1301 | "map": "1_6_64",
1302 | "x": 71918.71875,
1303 | "y": 67340.5234375,
1304 | "z": 321.7499694824219
1305 | }
1306 | ]
1307 | },
1308 | {
1309 | "huntingZoneId": 10,
1310 | "templateId": 1271,
1311 | "name": "Eteral (Serpentis Isle)",
1312 | "name_RU": "Иторо (Остров Серпентис)",
1313 | "locations": [
1314 | { // Пляж
1315 | "map": "1_6_58",
1316 | "x": 100213.46875,
1317 | "y": 92361.1953125,
1318 | "z": 129.3750457763672
1319 | },
1320 | { // Причал
1321 | "map": "1_6_58",
1322 | "x": 75068.609375,
1323 | "y": 106985.46875,
1324 | "z": 74.06303405761719
1325 | }
1326 | ]
1327 | },
1328 | {
1329 | "huntingZoneId": 11,
1330 | "templateId": 1271,
1331 | "name": "Eteral (Jagged Coast / Cutthroat Harbor)",
1332 | "name_RU": "Иторо (Изрезанный берег / Гавань Головорезов)",
1333 | "locations": [
1334 | { // Город
1335 | "map": "1_6_68",
1336 | "x": 51911.2421875,
1337 | "y": 69361.1484375,
1338 | "z": 500.06610107421875
1339 | },
1340 | { // Скрытая
1341 | "map": "1_6_70",
1342 | "x": 60873.55859375,
1343 | "y": 51690.76953125,
1344 | "z": 2983.725830078125
1345 | }
1346 | ]
1347 | },
1348 | {
1349 | "huntingZoneId": 12,
1350 | "templateId": 1271,
1351 | "name": "Eteral (Mistmoor Island)",
1352 | "name_RU": "Иторо (Остров Мистмур)",
1353 | "locations": [
1354 | { // Верх
1355 | "map": "1_6_69",
1356 | "x": 40286.28515625,
1357 | "y": 90096.5390625,
1358 | "z": 399.1529541015625
1359 | },
1360 | { // Низ
1361 | "map": "1_6_69",
1362 | "x": 26230.146484375,
1363 | "y": 99483.125,
1364 | "z": 419.85015869140625
1365 | }
1366 | ]
1367 | }
1368 | ]
1369 | },
1370 | // Poporia (1:40)
1371 | {
1372 | "logDiff": 6000,
1373 | "name": "Poporia",
1374 | "name_RU": "Попория",
1375 | "zoneId": 7002,
1376 | "npcs": [
1377 | {
1378 | "huntingZoneId": 15,
1379 | "templateId": 1271,
1380 | "name": "Foretta (Cliffs of Insanity)",
1381 | "name_RU": "Форета (Утесы Безумия)",
1382 | "locations": [
1383 | { // Верх
1384 | "map": "1_4_22",
1385 | "x": -7437.9111328125,
1386 | "y": 48186.27734375,
1387 | "z": 3255.10888671875
1388 | },
1389 | { // Лево
1390 | "map": "1_4_22",
1391 | "x": -19527.8359375,
1392 | "y": 37132.4296875,
1393 | "z": 3047.96044921875
1394 | },
1395 | { // Низ
1396 | "map": "1_4_22",
1397 | "x": -24456.248046875,
1398 | "y": 44578.4921875,
1399 | "z": 2444.213623046875
1400 | }
1401 | ]
1402 | },
1403 | {
1404 | "huntingZoneId": 16,
1405 | "templateId": 1271,
1406 | "name": "Foretta (Vale of the Fang)",
1407 | "name_RU": "Форета (Долина Клыка)",
1408 | "locations": [
1409 | {
1410 | "map": "1_4_25",
1411 | "x": -9749.55859375,
1412 | "y": 71364.484375,
1413 | "z": 3157.77197265625
1414 | }
1415 | ]
1416 | },
1417 | {
1418 | "huntingZoneId": 17,
1419 | "templateId": 1271,
1420 | "name": "Foretta (Paraanon Ravine / Popolion)",
1421 | "name_RU": "Форета (Ущелье Параанон / Пополион)",
1422 | "locations": [
1423 | { // Город
1424 | "map": "1_4_23",
1425 | "x": -16543.533203125,
1426 | "y": 49571.921875,
1427 | "z": 3002.508056640625
1428 | },
1429 | { // Лево
1430 | "map": "1_4_21",
1431 | "x": -20661.447265625,
1432 | "y": 55508.52734375,
1433 | "z": 2464.549072265625
1434 | },
1435 | { // Право
1436 | "map": "1_4_21",
1437 | "x": -27623.068359375,
1438 | "y": 67319.34375,
1439 | "z": 2240.25439453125
1440 | }
1441 | ]
1442 | },
1443 | {
1444 | "huntingZoneId": 23,
1445 | "templateId": 1271,
1446 | "name": "Foretta (Lake of Tears / Pora Elinu)",
1447 | "name_RU": "Форета (Озеро слез / Пора-Элину)",
1448 | "locations": [
1449 | { // Город
1450 | "map": "1_4_24",
1451 | "x": -34980.07421875,
1452 | "y": 34122.32421875,
1453 | "z": 2078.931396484375
1454 | },
1455 | { // Академия (скрытая)
1456 | "map": "1_4_26",
1457 | "x": -26794.345703125,
1458 | "y": 20765.435546875,
1459 | "z": 2568.2802734375
1460 | },
1461 | { // Рида (скрытая)
1462 | "map": "1_4_26",
1463 | "x": -33893.56640625,
1464 | "y": 57480.47265625,
1465 | "z": 871.17578125
1466 | },
1467 | { // Низ
1468 | "map": "1_4_26",
1469 | "x": -44089.828125,
1470 | "y": 42648.8359375,
1471 | "z": 1418.7498779296875
1472 | }
1473 | ]
1474 | }
1475 | ]
1476 | },
1477 | // Essenia (2:30)
1478 | {
1479 | "logDiff": 9000,
1480 | "name": "Essenia",
1481 | "name_RU": "Эссения",
1482 | "zoneId": 7011,
1483 | "npcs": [
1484 | {
1485 | "huntingZoneId": 26,
1486 | "templateId": 1271,
1487 | "name": "Ezart (Blessing Basin / Tralion)",
1488 | "name_RU": "Эсат (Блаженное озеро / Тралион)",
1489 | "locations": [
1490 | { // Город
1491 | "map": "1_13_141",
1492 | "x": 55758.81640625,
1493 | "y": -38961.2265625,
1494 | "z": 3035
1495 | },
1496 | { // Верх
1497 | "map": "1_13_142",
1498 | "x": 48969.76953125,
1499 | "y": -63509.4296875,
1500 | "z": 4835
1501 | },
1502 | { // Низ
1503 | "map": "1_13_142",
1504 | "x": 40484.62890625,
1505 | "y": -53543.68359375,
1506 | "z": 1843
1507 | },
1508 | { // Фонтан (скрытая)
1509 | "map": "1_13_142",
1510 | "x": 57959.8828125,
1511 | "y": -70377.2265625,
1512 | "z": 5237
1513 | }
1514 | ]
1515 | },
1516 | {
1517 | "huntingZoneId": 27,
1518 | "templateId": 1271,
1519 | "name": "Ezart (Essenian Crest)",
1520 | "name_RU": "Эсат (Эссенийский хребет)",
1521 | "locations": [
1522 | { // Лево
1523 | "map": "1_13_146",
1524 | "x": 63834.4375,
1525 | "y": -9977.0654296875,
1526 | "z": 639.3399658203125
1527 | },
1528 | { // ГПХ
1529 | "map": "1_13_146",
1530 | "x": 70642.171875,
1531 | "y": 21521.02734375,
1532 | "z": 1321.0185546875
1533 | }
1534 | ]
1535 | },
1536 | {
1537 | "huntingZoneId": 28,
1538 | "templateId": 1271,
1539 | "name": "Ezart (Blightwood)",
1540 | "name_RU": "Эсат (Гибельный лес)",
1541 | "locations": [
1542 | { // Низ
1543 | "map": "1_13_145",
1544 | "x": 39226.75390625,
1545 | "y": -11628.1513671875,
1546 | "z": 4644.06884765625
1547 | }
1548 | ]
1549 | },
1550 | {
1551 | "huntingZoneId": 29,
1552 | "templateId": 1271,
1553 | "name": "Ezart (Timeless Woods 1)",
1554 | "name_RU": "Эсат (Извечный лес 1)",
1555 | "locations": [
1556 | { // Верх (скрытая)
1557 | "map": "1_13_144",
1558 | "x": 56780.75,
1559 | "y": 5600.54296875,
1560 | "z": 4513
1561 | },
1562 | { // Низ (скрытая)
1563 | "map": "1_13_144",
1564 | "x": 30985.80078125,
1565 | "y": 12471.6611328125,
1566 | "z": 5449
1567 | },
1568 | { // Право
1569 | "map": "1_13_144",
1570 | "x": 54932.10546875,
1571 | "y": 23602.6328125,
1572 | "z": 3115
1573 | }
1574 | ]
1575 | },
1576 | {
1577 | "huntingZoneId": 29,
1578 | "templateId": 1272,
1579 | "name": "Ezart (Timeless Woods 2)",
1580 | "name_RU": "Эсат (Извечный лес 2)"
1581 | }
1582 | ]
1583 | },
1584 | // Westonia (2:30)
1585 | {
1586 | "logDiff": 9000,
1587 | "name": "Westonia",
1588 | "name_RU": "Вестония",
1589 | "zoneId": 7012,
1590 | "npcs": [
1591 | {
1592 | "huntingZoneId": 31,
1593 | "templateId": 1271,
1594 | "name": "Storan (Tempest Reach)",
1595 | "name_RU": "Сторан (Предел Бурь)",
1596 | "locations": [
1597 | { // Право
1598 | "map": "1_12_121",
1599 | "x": 1778.47021484375,
1600 | "y": -73254.015625,
1601 | "z": 1973.3394775390625
1602 | },
1603 | { // Корабль (скрытая)
1604 | "map": "1_12_121",
1605 | "x": 20548.578125,
1606 | "y": -83943.84375,
1607 | "z": -234.6809844970703
1608 | },
1609 | { // Лево
1610 | "map": "1_12_121",
1611 | "x": 17504.060546875,
1612 | "y": -102604.921875,
1613 | "z": 138.74998474121094
1614 | },
1615 | { // Шпиль (скрытая)
1616 | "map": "1_12_121",
1617 | "x": 13059.08984375,
1618 | "y": -119481.578125,
1619 | "z": 2468.78857421875
1620 | }
1621 | ]
1622 | },
1623 | {
1624 | "huntingZoneId": 32,
1625 | "templateId": 1271,
1626 | "name": "Storan (Mount Tyrannas / Acarum)",
1627 | "name_RU": "Сторан (Гора Тираннас / Акарум)",
1628 | "locations": [
1629 | { // Город
1630 | "map": "1_12_122",
1631 | "x": -8447.71484375,
1632 | "y": -71035.1015625,
1633 | "z": 2651.450439453125
1634 | },
1635 | { // Цитадель
1636 | "map": "1_12_124",
1637 | "x": -17741.205078125,
1638 | "y": -69973.7734375,
1639 | "z": 4895.931640625
1640 | },
1641 | { // Низ
1642 | "map": "1_12_124",
1643 | "x": -28223.212890625,
1644 | "y": -87539.3984375,
1645 | "z": 5895.60986328125
1646 | }
1647 | ]
1648 | },
1649 | {
1650 | "huntingZoneId": 34,
1651 | "templateId": 1271,
1652 | "name": "Storan (Frost Reach / Bleakrock)",
1653 | "name_RU": "Сторан (Морозный предел / Блеклый камень)",
1654 | "locations": [
1655 | { // Город
1656 | "map": "1_12_123",
1657 | "x": -41704.4375,
1658 | "y": -75046.171875,
1659 | "z": 1221.9237060546875
1660 | },
1661 | { // Лево
1662 | "map": "1_12_125",
1663 | "x": -62952.67578125,
1664 | "y": -85857.5625,
1665 | "z": 364.09033203125
1666 | },
1667 | { // Право
1668 | "map": "1_12_125",
1669 | "x": -61355.01953125,
1670 | "y": -68138.9140625,
1671 | "z": 431.9928283691406
1672 | },
1673 | { // Скрытая
1674 | "map": "1_12_125",
1675 | "x": -70943.953125,
1676 | "y": -49216.4453125,
1677 | "z": 3224.0458984375
1678 | }
1679 | ]
1680 | }
1681 | ]
1682 | },
1683 | // Veritas District (3:20)
1684 | {
1685 | "logDiff": 12000,
1686 | "name": "Veritas District",
1687 | "name_RU": "Район Веритас",
1688 | "zoneId": 7015,
1689 | "npcs": [
1690 | {
1691 | "huntingZoneId": 30,
1692 | "templateId": 1271,
1693 | "name": "Versa (Balder's Refuge / Bastion)",
1694 | "name_RU": "Вэлса (Убежище Балдера / Бастион)",
1695 | "locations": [
1696 | { // Город
1697 | "map": "1_15_176",
1698 | "x": -13603.8447265625,
1699 | "y": 54415.6484375,
1700 | "z": 627
1701 | },
1702 | { // Лево-верх
1703 | "map": "1_15_177",
1704 | "x": 11239.0009765625,
1705 | "y": 58747.88671875,
1706 | "z": 2287
1707 | },
1708 | { // Лево-низ
1709 | "map": "1_15_177",
1710 | "x": -2733.03759765625,
1711 | "y": 57698.8828125,
1712 | "z": 1122
1713 | },
1714 | { // Лестница (скрытая)
1715 | "map": "1_15_177",
1716 | "x": -10603.32421875,
1717 | "y": 70493.8359375,
1718 | "z": 491
1719 | },
1720 | { // Озеро (скрытая)
1721 | "map": "1_15_177",
1722 | "x": 151.13494873046875,
1723 | "y": 51830.8125,
1724 | "z": 1435
1725 | },
1726 | { // Право-верх
1727 | "map": "1_15_177",
1728 | "x": 5058.39599609375,
1729 | "y": 83043.03125,
1730 | "z": -777
1731 | },
1732 | { // Право-низ (каналы)
1733 | "map": "1_15_177",
1734 | "x": -13637.3466796875,
1735 | "y": 80936.3125,
1736 | "z": -1107
1737 | },
1738 | { // Рыбалка
1739 | "map": "1_15_177",
1740 | "x": -2754.078369140625,
1741 | "y": 88493.9921875,
1742 | "z": -1340
1743 | },
1744 | { // Хран. (скрытая)
1745 | "map": "1_15_177",
1746 | "x": 11072.7470703125,
1747 | "y": 69147.2734375,
1748 | "z": 2641
1749 | }
1750 | ]
1751 | }
1752 | ]
1753 | },
1754 | // Val Elenium (3:20)
1755 | {
1756 | "logDiff": 12000,
1757 | "name": "Val Elenium",
1758 | "name_RU": "Вал-Элениум",
1759 | "zoneId": 7014,
1760 | "npcs": [
1761 | {
1762 | "huntingZoneId": 35,
1763 | "templateId": 1271,
1764 | "name": "Viace (Wyrmgorge / Elenea)",
1765 | "name_RU": "Виас (Вирмовое ущелье / Эления)",
1766 | "locations": [
1767 | { // Город
1768 | "map": "1_11_106",
1769 | "x": -57174.09765625,
1770 | "y": -11030.224609375,
1771 | "z": 2261.380615234375
1772 | },
1773 | { // Верх
1774 | "map": "1_11_107",
1775 | "x": -39645.9296875,
1776 | "y": -32920.984375,
1777 | "z": 5747
1778 | },
1779 | { // Низ
1780 | "map": "1_11_107",
1781 | "x": -54117.34375,
1782 | "y": -33036.95703125,
1783 | "z": 3641
1784 | },
1785 | { // Право
1786 | "map": "1_11_107",
1787 | "x": -48233.5078125,
1788 | "y": -15323.9140625,
1789 | "z": 2335
1790 | }
1791 | ]
1792 | },
1793 | {
1794 | "huntingZoneId": 36,
1795 | "templateId": 1271,
1796 | "logDiff": 12000,
1797 | "name": "Viace (Tor Exsul)",
1798 | "name_RU": "Виас (Тор-Эксул)",
1799 | "locations": [
1800 | { // Верх
1801 | "map": "1_11_108",
1802 | "x": -42415.36328125,
1803 | "y": -2268.126953125,
1804 | "z": 1358
1805 | },
1806 | { // Низ
1807 | "map": "1_11_108",
1808 | "x": -54771.265625,
1809 | "y": 5073.52783203125,
1810 | "z": 532
1811 | },
1812 | { // Стенка (скрытая)
1813 | "map": "1_11_108",
1814 | "x": -46189.1015625,
1815 | "y": 21588.0078125,
1816 | "z": 2124
1817 | },
1818 | { // Центр
1819 | "map": "1_11_108",
1820 | "x": -48526.40234375,
1821 | "y": 10940.763671875,
1822 | "z": 1224
1823 | }
1824 | ]
1825 | },
1826 | {
1827 | "huntingZoneId": 38,
1828 | "templateId": 1271,
1829 | "name": "Viace (Sienna Canyon)",
1830 | "name_RU": "Виас (Каньон Сиенна)",
1831 | "locations": [
1832 | { // Лево
1833 | "map": "1_11_113",
1834 | "x": -78563.046875,
1835 | "y": -17928.904296875,
1836 | "z": 760
1837 | },
1838 | { // Нога (скрытая)
1839 | "map": "1_11_113",
1840 | "x": -83303.9140625,
1841 | "y": -2022.65673828125,
1842 | "z": 1640
1843 | },
1844 | { // Право
1845 | "map": "1_11_113",
1846 | "x": -87275.515625,
1847 | "y": 8747.421875,
1848 | "z": 1681
1849 | },
1850 | { // Стенка (скрытая)
1851 | "map": "1_11_113",
1852 | "x": -98762.34375,
1853 | "y": 4458.322265625,
1854 | "z": -76
1855 | }
1856 | ]
1857 | }
1858 | ]
1859 | },
1860 | // Val Palrada (3:20)
1861 | {
1862 | "logDiff": 12000,
1863 | "name": "Val Palrada",
1864 | "name_RU": "Вал-Палрада",
1865 | "zoneId": 7013,
1866 | "npcs": [
1867 | {
1868 | "huntingZoneId": 40,
1869 | "templateId": 1271,
1870 | "name": "Vaneva (Quarantine Zone / Frontera)",
1871 | "name_RU": "Ваннева (Зона карантина / Фронтера)",
1872 | "locations": [
1873 | { // Город
1874 | "map": "1_14_156",
1875 | "x": -42640.51171875,
1876 | "y": 57220.22265625,
1877 | "z": 211
1878 | },
1879 | { // Верх
1880 | "map": "1_14_157",
1881 | "x": -29464.81640625,
1882 | "y": 54129.65234375,
1883 | "z": -1335
1884 | },
1885 | { // Лево
1886 | "map": "1_14_157",
1887 | "x": -42874.5859375,
1888 | "y": 40590.69140625,
1889 | "z": 798
1890 | },
1891 | { // Телега (скрытая)
1892 | "map": "1_14_157",
1893 | "x": -57433.78515625,
1894 | "y": 53246.94140625,
1895 | "z": 769
1896 | }
1897 | ]
1898 | },
1899 | {
1900 | "huntingZoneId": 41,
1901 | "templateId": 1271,
1902 | "name": "Vaneva (Feral Valley)",
1903 | "name_RU": "Ваннева (Свирепая долина)",
1904 | "locations": [
1905 | { // Верх
1906 | "map": "1_14_158",
1907 | "x": -18690.990234375,
1908 | "y": 62581.203125,
1909 | "z": 890
1910 | },
1911 | { // ЛПН
1912 | "map": "1_14_158",
1913 | "x": -60612.07421875,
1914 | "y": 60869.015625,
1915 | "z": 1046
1916 | },
1917 | { // Низ
1918 | "map": "1_14_158",
1919 | "x": -45236.69921875,
1920 | "y": 72467.7734375,
1921 | "z": 1300
1922 | },
1923 | { // Храм
1924 | "map": "1_14_158",
1925 | "x": -64208.40234375,
1926 | "y": 56272.61328125,
1927 | "z": 1867
1928 | },
1929 | { // Центр
1930 | "map": "1_14_158",
1931 | "x": -32422.732421875,
1932 | "y": 65711.203125,
1933 | "z": 1730
1934 | }
1935 | ]
1936 | }
1937 | ]
1938 | },
1939 | // Lorcada (4:10)
1940 | {
1941 | "logDiff": 15000,
1942 | "name": "Lorcada",
1943 | "name_RU": "Лоркада",
1944 | "zoneId": 7022,
1945 | "npcs": [
1946 | {
1947 | "huntingZoneId": 51,
1948 | "templateId": 1271,
1949 | "name": "Loahcun (Vale of Spires)",
1950 | "name_RU": "Лоакун (Долина пиков)",
1951 | "locations": [
1952 | { // Центр
1953 | "map": "1_19_207",
1954 | "x": 5400.9296875,
1955 | "y": 54325.875,
1956 | "z": 7058.24853515625
1957 | },
1958 | { // Пещера (скрытая)
1959 | "map": "1_19_207",
1960 | "x": 15448.6904296875,
1961 | "y": 44000.99609375,
1962 | "z": 7773.88916015625
1963 | },
1964 | { // Келс (скрытая)
1965 | "map": "1_19_207",
1966 | "x": 8363.1474609375,
1967 | "y": 34555.359375,
1968 | "z": 9075.857421875
1969 | }
1970 | ]
1971 | },
1972 | {
1973 | "huntingZoneId": 52,
1974 | "templateId": 1271,
1975 | "name": "Locarnum (Plain of the Damned / Habere)",
1976 | "name_RU": "Лоакун (Долина Проклятых / Хабере)",
1977 | "locations": [
1978 | { // Город
1979 | "map": "1_19_206",
1980 | "x": -6838.263671875,
1981 | "y": 61654.515625,
1982 | "z": 5999.72802734375
1983 | },
1984 | { // Право
1985 | "map": "1_19_208",
1986 | "x": 7181.7470703125,
1987 | "y": 68015.5390625,
1988 | "z": 7168.74853515625
1989 | },
1990 | { // Лево
1991 | "map": "1_19_208",
1992 | "x": -13143.005859375,
1993 | "y": 35963.203125,
1994 | "z": 4441.07421875
1995 | },
1996 | { // Центр
1997 | "map": "1_19_208",
1998 | "x": -4465.12451171875,
1999 | "y": 44090.390625,
2000 | "z": 4910.76171875
2001 | },
2002 | { // Кузня (скрытая)
2003 | "map": "1_19_208",
2004 | "x": -20198.322265625,
2005 | "y": 43151.140625,
2006 | "z": 2915.336669921875
2007 | },
2008 | { // Низ
2009 | "map": "1_19_208",
2010 | "x": -21198.37109375,
2011 | "y": 52498.8515625,
2012 | "z": 3827.3037109375
2013 | }
2014 | ]
2015 | }
2016 | ]
2017 | },
2018 | // Sylvanoth (4:10)
2019 | {
2020 | "logDiff": 15000,
2021 | "name": "Sylvanoth",
2022 | "name_RU": "Силванот",
2023 | "zoneId": 7021,
2024 | "npcs": [
2025 | {
2026 | "huntingZoneId": 54,
2027 | "templateId": 1271,
2028 | "name": "Silvette (Seeliewood / Scythera Fae)",
2029 | "name_RU": "Силвета (Силивуд / Скитера-Фэй)",
2030 | "locations": [
2031 | { // Город
2032 | "map": "1_20_221",
2033 | "x": -57687.92578125,
2034 | "y": -26859.861328125,
2035 | "z": 2502.43310546875
2036 | },
2037 | { // Низ
2038 | "map": "1_20_223",
2039 | "x": -86778.46875,
2040 | "y": -5051.73876953125,
2041 | "z": 1604.75
2042 | }
2043 | ]
2044 | },
2045 | {
2046 | "huntingZoneId": 55,
2047 | "templateId": 1271,
2048 | "name": "Silvette (Darkquaver Woods / Dragonfall)",
2049 | "name_RU": "Силвета (Дрожащий лес / Дрэгонфолл)",
2050 | "locations": [
2051 | { // Город
2052 | "map": "1_20_224",
2053 | "x": -38736.12109375,
2054 | "y": 10156.4462890625,
2055 | "z": 4880.25
2056 | },
2057 | { // Центр
2058 | "map": "1_20_222",
2059 | "x": -58015.640625,
2060 | "y": -5387.87451171875,
2061 | "z": 1528.18212890625
2062 | },
2063 | { // МЛХ
2064 | "map": "1_20_222",
2065 | "x": -66340.5234375,
2066 | "y": 13907.9697265625,
2067 | "z": 2663.639404296875
2068 | }
2069 | ]
2070 | },
2071 | {
2072 | "huntingZoneId": 56,
2073 | "templateId": 1271,
2074 | "name": "Silvette (Susurrus Woods)",
2075 | "name_RU": "Силвета (Шепчущие леса)",
2076 | "locations": [
2077 | { // Верх
2078 | "map": "1_20_225",
2079 | "x": -43959.62109375,
2080 | "y": 18201.412109375,
2081 | "z": 2487.75
2082 | },
2083 | { // Низ
2084 | "map": "1_20_225",
2085 | "x": -70955.5859375,
2086 | "y": 33496.3984375,
2087 | "z": 2151.25
2088 | }
2089 | ]
2090 | },
2091 | {
2092 | "huntingZoneId": 57,
2093 | "templateId": 1271,
2094 | "name": "Silvette (Amena Quatla)",
2095 | "name_RU": "Силвета (Амена-Кватла)",
2096 | "locations": [
2097 | { // Верх
2098 | "map": "1_20_226",
2099 | "x": -49302.51171875,
2100 | "y": 34425.53515625,
2101 | "z": 3024.778076171875
2102 | },
2103 | { // Низ
2104 | "map": "1_20_226",
2105 | "x": -63893.0625,
2106 | "y": 51037.66796875,
2107 | "z": 4075.749755859375
2108 | }
2109 | ]
2110 | }
2111 | ]
2112 | },
2113 | // Val Tirkai (5:00)
2114 | {
2115 | "logDiff": 18000,
2116 | "name": "Val Tirkai",
2117 | "name_RU": "Вал-Тиркай",
2118 | "zoneId": 7023,
2119 | "npcs": [
2120 | {
2121 | "huntingZoneId": 45,
2122 | "templateId": 1271,
2123 | "name": "Lotica (Thrallhold / Pathfinder Post)",
2124 | "name_RU": "Лотика (Питомник аргонов / Аванпост следопытов)",
2125 | "locations": [
2126 | { // Город
2127 | "map": "1_21_231",
2128 | "x": 13430.0751953125,
2129 | "y": -26734.95703125,
2130 | "z": 155.32815551757812
2131 | },
2132 | { // Три башни (скрытая)
2133 | "map": "1_21_233",
2134 | "x": 25913.83203125,
2135 | "y": -27882.630859375,
2136 | "z": -400.2799072265625
2137 | },
2138 | { // Лестница (скрытая)
2139 | "map": "1_21_233",
2140 | "x": 20208.1640625,
2141 | "y": -54620.21875,
2142 | "z": -2731.306640625
2143 | },
2144 | { // Низ
2145 | "map": "1_21_233",
2146 | "x": 3705.982177734375,
2147 | "y": -49702.00390625,
2148 | "z": -4742.25
2149 | },
2150 | { // Верх
2151 | "map": "1_21_233",
2152 | "x": 23733.25,
2153 | "y": -44887.078125,
2154 | "z": -3897.24951171875
2155 | }
2156 | ]
2157 | },
2158 | {
2159 | "huntingZoneId": 47,
2160 | "templateId": 1271,
2161 | "name": "Lotica (Tirkai Forest)",
2162 | "name_RU": "Лотика (Лес Тиркай)",
2163 | "locations": [
2164 | { // Низ
2165 | "map": "1_21_232",
2166 | "x": -10827.015625,
2167 | "y": -33385.9765625,
2168 | "z": -1672.0228271484375
2169 | },
2170 | { // Верх
2171 | "map": "1_21_232",
2172 | "x": 707.2947387695312,
2173 | "y": -26388.091796875,
2174 | "z": 612.3023071289062
2175 | },
2176 | { // Вышка (скрытая)
2177 | "map": "1_21_232",
2178 | "x": -7782.90673828125,
2179 | "y": -38698.0234375,
2180 | "z": -1178.329833984375
2181 | },
2182 | { // Пещера (скрытая)
2183 | "map": "1_21_232",
2184 | "x": 9427.1396484375,
2185 | "y": -40902.36328125,
2186 | "z": -3532.749755859375
2187 | },
2188 | { // Гора (скрытая)
2189 | "map": "1_21_232",
2190 | "x": -22306.478515625,
2191 | "y": -21575.310546875,
2192 | "z": 1417.809326171875
2193 | }
2194 | ]
2195 | }
2196 | ]
2197 | },
2198 | // Helkan District (5:00)
2199 | {
2200 | "logDiff": 18000,
2201 | "name": "Helkan District",
2202 | "name_RU": "Район Хелкан",
2203 | "zoneId": 8001,
2204 | "npcs": [
2205 | {
2206 | "huntingZoneId": 48,
2207 | "templateId": 1271,
2208 | "name": "Hecurn (Khanovar Front / Zulfikar Fortress)",
2209 | "name_RU": "Хелкун (Хановарские предместья / Зульфикарская крепость)",
2210 | "locations": [
2211 | { // Город
2212 | "map": "1_23_251",
2213 | "x": 33935.94921875,
2214 | "y": -5965.1923828125,
2215 | "z": 1148.5855712890625
2216 | },
2217 | { // Цитадель
2218 | "map": "1_23_252",
2219 | "x": 42889.9765625,
2220 | "y": 2017.087158203125,
2221 | "z": 1306.2498779296875
2222 | },
2223 | { // Крепость
2224 | "map": "1_23_252",
2225 | "x": 72426.734375,
2226 | "y": -8776.748046875,
2227 | "z": 199.7501220703125
2228 | },
2229 | { // Крепость (скрытая)
2230 | "map": "1_23_252",
2231 | "x": 68642.59375,
2232 | "y": 4652.01513671875,
2233 | "z": 775.7406616210938
2234 | },
2235 | { // Право-верх (скрытая)
2236 | "map": "1_23_252",
2237 | "x": 62926.95703125,
2238 | "y": 2724.40576171875,
2239 | "z": -13.853206634521484
2240 | },
2241 | { // Тунель (скрытая)
2242 | "map": "1_23_252",
2243 | "x": 59167.234375,
2244 | "y": 11119.01953125,
2245 | "z": 2375.974609375
2246 | },
2247 | { // Кабина (скрытая)
2248 | "map": "1_23_252",
2249 | "x": 49638.2578125,
2250 | "y": -7478.734375,
2251 | "z": 55.05164337158203
2252 | },
2253 | { // Право-низ (скрытая)
2254 | "map": "1_23_252",
2255 | "x": 38892.48828125,
2256 | "y": 9808.236328125,
2257 | "z": 1024.17333984375
2258 | }
2259 | ]
2260 | }
2261 | ]
2262 | },
2263 | // Helkan District (5:00)
2264 | {
2265 | "logDiff": 18000,
2266 | "name": "Val Kaeli",
2267 | "name_RU": "Вал-Кэли",
2268 | "zoneId": 8001,
2269 | "npcs": [
2270 | {
2271 | "huntingZoneId": 49,
2272 | "templateId": 1271,
2273 | "name": "Locarnum (Argonea / Kanstria)",
2274 | "name_RU": "Тезлуар (Аргония / Канстрия)",
2275 | "locations": [
2276 | { // Город
2277 | "map": "1_22_247",
2278 | "x": 47661.6640625,
2279 | "y": 17904.271484375,
2280 | "z": 2330.981689453125
2281 | },
2282 | { // Наверху (скрытая)
2283 | "map": "1_22_249",
2284 | "x": 50990.90234375,
2285 | "y": 35466.44921875,
2286 | "z": 4776.86279296875
2287 | },
2288 | { // Внизу (скрытая)
2289 | "map": "1_22_249",
2290 | "x": 56034.31640625,
2291 | "y": 30144.595703125,
2292 | "z": 629.7499389648438
2293 | },
2294 | { // Юж. платформа
2295 | "map": "1_22_249",
2296 | "x": 44283.98046875,
2297 | "y": 27693.04296875,
2298 | "z": 2355.5380859375
2299 | },
2300 | { // Портал (скрытая)
2301 | "map": "1_22_249",
2302 | "x": 75651.5859375,
2303 | "y": 27657.955078125,
2304 | "z": 4205.9619140625
2305 | },
2306 | { // Портал верх (скрытая)
2307 | "map": "1_22_249",
2308 | "x": 66116.96875,
2309 | "y": 25654.060546875,
2310 | "z": 4922.0546875
2311 | }
2312 | ]
2313 | },
2314 | {
2315 | "huntingZoneId": 50,
2316 | "templateId": 1271,
2317 | "name": "Locarnum (Granarkus)",
2318 | "name_RU": "Тезлуар (Гранаркус)",
2319 | "locations": [
2320 | { // Лево
2321 | "map": "1_22_250",
2322 | "x": 45688.25,
2323 | "y": 48044.06640625,
2324 | "z": 4179.2490234375
2325 | },
2326 | { // Право
2327 | "map": "1_22_250",
2328 | "x": 60308.44140625,
2329 | "y": 61926.48828125,
2330 | "z": 5497.2158203125
2331 | },
2332 | { // Спуск (скрытая)
2333 | "map": "1_22_250",
2334 | "x": 36521.13671875,
2335 | "y": 60875.94921875,
2336 | "z": 3416.048828125
2337 | },
2338 | { // Платформа
2339 | "map": "1_22_250",
2340 | "x": 67056.03125,
2341 | "y": 64125.23828125,
2342 | "z": 4695.14306640625
2343 | },
2344 | { // Центр (скрытая)
2345 | "map": "1_22_250",
2346 | "x": 48822.57421875,
2347 | "y": 60260.52734375,
2348 | "z": 4316.35205078125
2349 | }
2350 | ]
2351 | }
2352 | ]
2353 | },
2354 | // Other
2355 | {
2356 | "npcs": [
2357 | // Secret store
2358 | {
2359 | "huntingZoneId": 63,
2360 | "templateId": 1271,
2361 | "name": "Mystery Merchant (Velika 1)",
2362 | "name_RU": "Тайный Магазин (Велика 1)"
2363 | },
2364 | {
2365 | "huntingZoneId": 63,
2366 | "templateId": 1279,
2367 | "name": "Mystery Merchant (Velika 2)",
2368 | "name_RU": "Тайный Магазин (Велика 2)"
2369 | },
2370 | {
2371 | "huntingZoneId": 72,
2372 | "templateId": 1271,
2373 | "name": "Mystery Merchant (Allemantheia)",
2374 | "name_RU": "Тайный Магазин (Аллемантея)"
2375 | },
2376 | {
2377 | "huntingZoneId": 84,
2378 | "templateId": 1271,
2379 | "name": "Mystery Merchant (Kaiator)",
2380 | "name_RU": "Тайный Магазин (Кайатор)"
2381 | },
2382 | {
2383 | "huntingZoneId": 183,
2384 | "templateId": 1271,
2385 | "name": "Mystery Merchant (Highwatch)",
2386 | "name_RU": "Тайный Магазин (Верхний Дозор)"
2387 | }
2388 | ]
2389 | }
2390 | ]
2391 | }
2392 | };
2393 |
2394 |
2395 | module.exports = function MigrateSettings(from_ver, to_ver, settings) {
2396 | if (from_ver === undefined) {
2397 | return { ...DefaultSettings, ...settings };
2398 | } else if (from_ver === null) {
2399 | return DefaultSettings;
2400 | } else {
2401 | if (from_ver + 1 < to_ver) {
2402 | settings = MigrateSettings(from_ver, from_ver + 1, settings);
2403 | return MigrateSettings(from_ver + 1, to_ver, settings);
2404 | }
2405 | const oldsettings = settings;
2406 | settings = Object.assign(DefaultSettings, {});
2407 | switch (to_ver) {
2408 | default:
2409 | for (const option in oldsettings) {
2410 | if (option == "itemId") continue;
2411 | if (["merchants", "goblins", "world_bosses", "raid_bosses", "others"].includes(option) && oldsettings[option] !== undefined) {
2412 | settings[option] = MigrateList(settings[option], oldsettings[option]);
2413 | } else if (settings[option] !== undefined) {
2414 | settings[option] = oldsettings[option];
2415 | }
2416 | }
2417 | break;
2418 | }
2419 | return settings;
2420 | }
2421 | };
2422 |
2423 | function MigrateList(settings, oldsettings) {
2424 | if (Array.isArray(settings)) {
2425 | for (const i of Object.keys(settings)) {
2426 | settings[i] = MigrateList(settings[i], oldsettings[i]);
2427 | }
2428 | return settings;
2429 | }
2430 | if (oldsettings === undefined) {
2431 | oldsettings = settings;
2432 | }
2433 | for (const option of Object.keys(settings)) {
2434 | if (oldsettings[option] !== undefined) {
2435 | if (option == "logTime") {
2436 | settings[option] = oldsettings[option];
2437 | }
2438 | if (Array.isArray(settings[option])) {
2439 | settings[option] = MigrateList(settings[option], oldsettings[option]);
2440 | }
2441 | }
2442 | }
2443 | return settings;
2444 | }
--------------------------------------------------------------------------------