├── .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 | ![](https://i.imgur.com/MRSGHDo.png) 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 | ![](https://i.imgur.com/RPXfTFV.png) 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 | ![](https://i.imgur.com/A6kpUCK.png) 59 | 60 | ## More Information / Информация 61 | 62 | * The module has built-in functionality of Lambda11's BAM-HP-Bar module (with **fixed Ortan**): 63 | ![](https://i.imgur.com/kLNyQJL.png) 64 | * Merchant NPC with marker (easily visible in the distance): 65 | ![](https://i.imgur.com/tdIJKJv.png) 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 | } --------------------------------------------------------------------------------