# CRAWL.NEMELEX.CARDS # 사운드 자동 켜기 #$ lab_sound_on = true # 타일 사이즈 변경 tile_use_small_layout = true tile_map_pixels = 32 tile_cell_pixels = 64 # 폰트 사이즈 변경 tile_font_crt_size = 23 tile_font_stat_size = 23 tile_font_msg_size = 23 tile_font_tip_size = 23 tile_font_lbl_size = 23 # 폰트 변경 #tile_font_crt_family = Open Sans #tile_font_stat_family = Open Sans #tile_font_msg_family = Open Sans #tile_font_tip_family = Open Sans #tile_font_lbl_family = Open Sans # Consolas, DejaVu Sans Mono, Arial, Open Sans # vi방식 이동키 비활성화 include = no_vi_command_keys.txt # 스킬 경험치 메뉴얼 설정 default_manual_training = true # 플레이어 타일을 해당 종족으로 변경 (Default : false) tile_show_player_species = false # 메세지가 너무 많이 떴을 때 강제로 more 띄우지 않게 함 (Default : true) show_more = false # 자동탐사 딜레이 (Default : -1) travel_delay = 15 # 휴식 딜레이 rest_delay = -1 # 탐험 딜레이 explore_delay = -1 # 이펙트 딜레이 (Default 500) view_delay = 100 #왜곡무기 장비한 몬스터, 춤추는 왜곡무기 출현 시 more, 화면이 번쩍 효과 force_more_message += It is wielding.*of distortion force_more_message += She is wielding.*of distortion force_more_message += He is wielding.*of distortion force_more_message += wielding.* distortion.* comes? into view flash_screen_message += It is wielding.*of distortion flash_screen_message += She is wielding.*of distortion flash_screen_message += He is wielding.*of distortion flash_screen_message += wielding.* distortion.* comes? into view flash_screen_message += distortion.* comes? into view # HP / MP 알림 { local need_skills_opened = true local previous_hp = 0 local previous_mp = 0 local previous_form = '' local was_berserk_last_turn = false function announce_damage_ko() local current_hp, max_hp = you.hp() local current_mp, max_mp = you.mp() --Things that increase hp/mp temporarily really mess with this local current_form = you.transform() local you_are_berserk = you.berserk() local max_hp_increased = false local max_hp_decreased = false if (current_form ~= previous_form) then if (previous_form:find('dragon') or previous_form:find('statue') or previous_form:find('tree') or previous_form:find('ice')) then max_hp_decreased = true elseif (current_form:find('dragon') or current_form:find('statue') or current_form:find('tree') or current_form:find('ice')) then max_hp_increased = true end end if (was_berserk_last_turn and not you_are_berserk) then max_hp_decreased = true elseif (you_are_berserk and not was_berserk_last_turn) then max_hp_increased = true end --Skips message on initializing game if previous_hp > 0 then local hp_difference = previous_hp - current_hp local mp_difference = previous_mp - current_mp if max_hp_increased or max_hp_decreased then if max_hp_increased then crawl.mpr('Now you have [' .. current_hp .. '/' .. max_hp .. '] HP') else crawl.mpr('Now you have [' .. current_hp .. '/' .. max_hp .. '] HP') end else --체력 잃을때 if (current_hp < previous_hp) then if current_hp <= (max_hp * 0.30) then crawl.mpr('You take ' .. hp_difference .. ' HP, and have [' .. current_hp .. '/' .. max_hp .. '] HP') elseif current_hp <= (max_hp * 0.50) then crawl.mpr('You take ' .. hp_difference .. ' HP, and have [' .. current_hp .. '/' .. max_hp .. '] HP') elseif current_hp <= (max_hp * 0.70) then crawl.mpr('You take ' .. hp_difference .. ' HP, and have [' .. current_hp .. '/' .. max_hp .. '] HP') elseif current_hp <= (max_hp * 0.90) then crawl.mpr('You take ' .. hp_difference .. ' HP, and have [' .. current_hp .. '/' .. max_hp .. '] HP') else crawl.mpr('You take ' .. hp_difference .. ' HP, and have [' .. current_hp .. '/' .. max_hp .. '] HP') end if hp_difference > (max_hp * 0.20) then crawl.mpr('!!!!! HP Warning !!!!!') end end --체력 얻을때 if (current_hp > previous_hp) then --Removes the negative sign local health_inturn = (0 - hp_difference) if (health_inturn > 1) and not (current_hp == max_hp) then if current_hp <= (max_hp * 0.30) then crawl.mpr('You gain ' .. health_inturn .. ' hp, and have [' .. current_hp .. '/' .. max_hp .. '] hp.') elseif current_hp <= (max_hp * 0.50) then crawl.mpr('You gain ' .. health_inturn .. ' HP, and have [' .. current_hp .. '/' .. max_hp .. '] hp.') elseif current_hp <= (max_hp * 0.70) then crawl.mpr('You gain ' .. health_inturn .. ' HP, and have [' .. current_hp .. '/' .. max_hp .. '] HP') elseif current_hp <= (max_hp * 0.90) then crawl.mpr('You gain ' .. health_inturn .. ' HP, and have [' .. current_hp .. '/' .. max_hp ..'] HP') else crawl.mpr('You gain ' .. health_inturn .. ' HP, and have [' .. current_hp .. '/' .. max_hp .. '] HP') end end if (current_hp == max_hp) then crawl.mpr(' HP Full. (' .. current_hp .. ')') end end --마력 얻을때 if (current_mp > previous_mp) then --Removes the negative sign local mp_inturn = (0 - mp_difference) if (mp_inturn > 1) and not (current_mp == max_mp) then if current_mp < (max_mp * 0.25) then crawl.mpr('You gain ' .. mp_inturn .. ' MP, and have [' .. current_mp .. '/' .. max_mp .. '] MP') elseif current_mp < (max_mp * 0.50) then crawl.mpr('You gain ' .. mp_inturn .. ' MP, and have [' .. current_mp .. '/' .. max_mp .. '] MP') else crawl.mpr('You gain ' .. mp_inturn .. ' MP, and have [' .. current_mp .. '/' .. max_mp .. '] MP') end end if (current_mp == max_mp) then crawl.mpr('MP Full (' .. current_mp .. ')') end end --마력 잃을때 if current_mp < previous_mp then if current_mp <= (max_mp * 0.25) then crawl.mpr('You lose ' .. mp_difference .. 'MP, and have [' .. current_mp .. '/' ..max_mp ..'] MP') elseif current_mp <= (max_mp * 0.50) then crawl.mpr('You lose ' .. mp_difference .. 'MP, and have [' .. current_mp .. '/' ..max_mp ..'] MP') else crawl.mpr('You lose ' .. mp_difference .. 'MP, and have [' .. current_mp .. '/' ..max_mp ..'] MP') end end end end --Set previous hp/mp and form at end of turn previous_hp = current_hp previous_mp = current_mp previous_form = current_form was_berserk_last_turn = you_are_berserk end function ready() -- Enable AnnounceDamage. announce_damage_ko() if you.turns() == 0 and need_skills_opened then need_skills_opened = false crawl.sendkeys("m") end end } [템퍼몽키 명령어] // ==UserScript== // @name Webtiles Extend Module Loader // @description Load the WEM from other Webtiles sites as well. // @version 1.0 // @author ASCIIPhilia // @match https://crawl.kelbi.org/* // @match http://crawl.akrasiac.org:8080/* // @match https://crawl.akrasiac.org:8443/* // @match https://underhound.eu:8080/* // @match https://cbro.berotato.org:8443/* // @match http://lazy-life.ddo.jp:8080/* // @match https://crawl.xtahua.com/* // @match https://crawl.project357.org/* // @match http://joy1999.codns.com:8081/* // @namespace https://greasyfork.org/users/663409 // @downloadURL https://update.greasyfork.org/scripts/482991/Webtiles%20Extend%20Module%20Loader.user.js // @updateURL https://update.greasyfork.org/scripts/482991/Webtiles%20Extend%20Module%20Loader.meta.js // ==/UserScript== (function () { 'use strict'; function waitFor(checkFunction, checkDelay = 100) { return new Promise(resolve => { let i = setInterval(_ => { try { let check = checkFunction(); check ? clearInterval(i) || resolve(check) : void 0 } catch (e) {} }, checkDelay); }); } !async function () { await waitFor(_ => unsafeWindow.$, 100); $.getScript("https://dcssem.abstr.net/webtiles_module/release/1.0/script.js"); }(); })(); sound_on = true sound_pack += https://osp.nemelex.cards/build/latest.zip:["init.txt"] one_SDL_sound_channel = true sound_fade_time = 0.5 import DataManager from "./data-manager.js"; import Translator from "./translator.js"; export default class TranslationModule { static name = 'TranslationModule'; static version = '0.1'; static dependencies = ['IOHook', 'RCManager', 'SiteInformation']; static description = '(Beta) This module provides i18n feature.'; sendMessage(text) { const {IOHook} = DWEM.Modules; IOHook.handle_message({ msg: 'msgs', messages: [{text}] }); } escapeHTML(str) { return str.replace(/[&<>"']/g, function (match) { const escapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return escapeMap[match]; }); } loadTranslationFont(language) { document.querySelector('#translation_font')?.remove(); const HEAD = document.head || document.getElementsByTagName('head')[0]; let fontFamily; switch (language) { case 'ko': window.WebFontConfig = { custom: { families: ['Nanum Gothic Coding'], urls: ['https://fonts.googleapis.com/earlyaccess/nanumgothiccoding.css'] } }; fontFamily = '"Nanum Gothic Coding", monospace'; const wf = document.createElement('script'); wf.src = (location.protocol === 'https:' ? 'https' : 'http') + '://ajax.googleapis.com/ajax/libs/webfont/1.4.10/webfont.js'; wf.type = 'text/javascript'; wf.async = true; HEAD.insertBefore(wf, HEAD.firstChild); break; case 'ja': const preconnect = document.createElement('link'); preconnect.rel = 'preconnect'; preconnect.href = 'https://cdn.jsdelivr.net'; HEAD.appendChild(preconnect); const fontFace = document.createElement('style'); fontFace.setAttribute('data-noto-mono', 'true'); fontFace.textContent = ` @font-face{ font-family:"Noto Sans Mono CJK JP"; font-style:normal; font-weight:400; font-display:swap; src:local("NotoSansMonoCJKjp-Regular"), local("Noto Sans Mono CJK JP Regular"), url("https://cdn.jsdelivr.net/gh/notofonts/noto-cjk/Sans/Mono/NotoSansMonoCJKjp-Regular.otf") format("opentype"); } @font-face{ font-family:"Noto Sans Mono CJK JP"; font-style:normal; font-weight:700; font-display:swap; src:local("NotoSansMonoCJKjp-Bold"), local("Noto Sans Mono CJK JP Bold"), url("https://cdn.jsdelivr.net/gh/notofonts/noto-cjk/Sans/Mono/NotoSansMonoCJKjp-Bold.otf") format("opentype"); } `; HEAD.appendChild(fontFace); fontFamily = '"Noto Sans Mono CJK JP", "MS Gothic", monospace'; break; default: return; } const fontStyle = document.createElement('style'); fontStyle.id = 'translation_font'; fontStyle.appendChild( document.createTextNode(`* { font-family: ${fontFamily}; }`) ); fontStyle.appendChild( document.createTextNode(`#stats .bar > *, #stats .bar { height: 1.2em !important; }`) ); HEAD.appendChild(fontStyle); } unloadTranslationFont() { document.querySelector('#translation_font')?.remove?.(); } #getTranslationConfig(rcfile) { const {RCManager} = DWEM.Modules; const language = RCManager.getRCOption(rcfile, 'language', 'string'); const translationLanguage = RCManager.getRCOption(rcfile, 'translation_language', 'string'); const translationFile = RCManager.getRCOption(rcfile, 'translation_file', 'string', 'https://translation.nemelex.cards/build/latest.json'); const useTranslationFont = RCManager.getRCOption(rcfile, 'use_translation_font', 'boolean', true); const translationDebug = RCManager.getRCOption(rcfile, 'translation_debug', 'boolean', false); return { language, translationLanguage, useTranslationFont, translationFile, translationDebug }; } onLoad() { const {IOHook, RCManager} = DWEM.Modules; const {SourceMapperRegistry: SMR} = DWEM; this.DataManager = DataManager; function playerUIInjector() { const original_update_stats_pane = update_stats_pane; update_stats_pane = function () { original_update_stats_pane(); const {TranslationModule} = DWEM.Modules; const language = TranslationModule.config.translationLanguage; if (language) { const translate = (text, category) => TranslationModule.escapeHTML(TranslationModule.translator.translate(text, language, category).translation); if (language === 'ja') { $('#stats_titleline').text(player.title.replace(',', '').trim() + '『' + player.name + '』'); } const wizard_TR = translate("*WIZARD*", 'interface@stats.mode'); const explore_TR = translate("*EXPLORE*", 'interface@stats.mode'); $("#stats_wizmode").text(player.wizard ? wizard_TR : player.explore ? explore_TR : ""); if (player.god != "") { const species_god_TR = translate(player.species + " of " + player.god, 'interface@stats.species_god'); $("#stats_species_god").text(species_god_TR); } const gozag_TR = translate("Gozag", 'interface@stats.gozag'); if (player.god === gozag_TR) { $("#stats_gozag_gold_label").text(" " + translate("Gold: ", 'interface@stats.text')); $("#stats_gozag_gold_label").css("padding-left", "0.5em"); $("#stats_gozag_gold").text(player.gold); $("#stats_piety").text(""); } const hp_TR = translate("HP:", 'ui-panel@stats'); const health_TR = translate("Health:", 'ui-panel@stats'); $("#stats_hpline > .stats_caption").text( (player.real_hp_max != player.hp_max) ? hp_TR : health_TR); $("#stats_mpline > .stats_caption").text(translate("Magic:", 'interface@stats.text')) $("#stats_leftcolumn span:contains('AC:')").text(translate("AC:", 'interface@stats.text')) $("#stats_leftcolumn span:contains('EV:')").text(translate("EV:", 'interface@stats.text')) $("#stats_leftcolumn span:contains('SH:')").text(translate("SH:", 'interface@stats.text')) $("#stats_leftcolumn span:contains('XL:')").text(translate("XL:", 'interface@stats.text')) $("#stats_leftcolumn span:contains('Next:')").text(translate("Next:", 'interface@stats.text')) $("#stats_leftcolumn span:contains('Noise:')").text(translate("Noise:", 'interface@stats.text')) $("#stats_rightcolumn span:contains('Str:')").text(translate("Str:", 'interface@stats.text')) $("#stats_rightcolumn span:contains('Int:')").text(translate("Int:", 'interface@stats.text')) $("#stats_rightcolumn span:contains('Dex:')").text(translate("Dex:", 'interface@stats.text')) $("#stats_rightcolumn span:contains('Place:')").text(translate("Place:", 'interface@stats.text')) $("#stats_rightcolumn span:contains('Time:')").text(translate("Time:", 'interface@stats.text')) $("#stats_rightcolumn span:contains('Gold:')").text(translate("Gold:", 'interface@stats.text')) $("#stats_rightcolumn span.stats_caption:contains('Doom:')").text(translate("Doom:", 'interface@stats.text')) } } } const playerUIMapper = SMR.getSourceMapper('BeforeReturnInjection', `!${playerUIInjector.toString()}()`); SMR.add('./player', playerUIMapper); function mouseControlInjector() { const {TranslationModule} = DWEM.Modules; const language = TranslationModule.config.translationLanguage; if (language) { const translate = (text, category) => TranslationModule.escapeHTML(TranslationModule.translator.translate(text, language, category).translation); handle_cell_tooltip = function handle_cell_tooltip(ev) { var map_cell = map_knowledge.get(ev.cell.x, ev.cell.y); var text = "" // don't show a tooltip unless there's a monster in the square. It might // be good to expand this (local tiles does), but there are a number // of issues. First, I found the tooltips pretty disruptive in my // testing, most of the time you don't care. Second, there are some // challenges in filling tooltip info outside of a narrow slice of // cases where the info is neatly packaged for webtiles. E.g. a tooltip // that tells you whether you can move to a square is very hard to // construct on the client side (see tilerg-dgn.cc:tile_dungeon_tip for // the logic that would be necessary.) // minimal extensions: tooltips for interesting features, like statues, // doors, stairs, altars. if (!map_cell.mon) return; // `game` force-set in game.js because of circular reference issues if (game.can_target() && map_knowledge.visible(map_cell)) { // XX just looking case has weird behavior text += translate("Left click: select target", 'interface@mouse.text'); } // XX a good left click tooltip for click travel is very hard to // construct on the client side... // see tilerg-dgn.cc:tile_dungeon_tip for the logic that would be // necessary. if (game.can_describe()) { // only show right-click desc if there's something else in the // tooltip, it's too disruptive otherwise if (text) text += "
"; // XX something better than
s text += translate("Right click: describe", 'interface@mouse.text'); } if (text) text = "

" + text; text = map_cell.mon.name + text; show_tooltip(text, ev.pageX + 10, ev.pageY + 10); } } } const mouseControlMapper = SMR.getSourceMapper('BeforeReturnInjection', `!${mouseControlInjector.toString()}()`); SMR.add('./mouse_control', mouseControlMapper); function actionPanelInjector() { const {TranslationModule} = DWEM.Modules; const language = TranslationModule.config.translationLanguage; if (language) { const translate = (text, category) => TranslationModule.escapeHTML(TranslationModule.translator.translate(text, language, category).translation); show_tooltip = function show_tooltip(x, y, slot) { if (slot >= filtered_inv.length) { hide_tooltip(); return; } $tooltip.css({ top: y + 10 + "px", left: x + 10 + "px" }); if (slot == -2) { $tooltip.html(`${translate("Left click: minimize", 'interface@action_panel.text')}
` + `${translate("Right click: open settings", 'interface@action_panel.text')}`); } else if (slot == -1 && game.get_input_mode() == enums.mouse_mode.COMMAND) $tooltip.html(`${translate("Left click: show main menu", 'interface@action_panel.text')}`); else { var item = filtered_inv[slot]; $tooltip.empty().text(player.index_to_letter(item.slot) + " - "); $tooltip.append(player.inventory_item_desc(item.slot)); if (game.get_input_mode() == enums.mouse_mode.COMMAND) { if (item.action_verb) $tooltip.append(`
${translate("Left click: " + item.action_verb.toLowerCase(), 'interface@action_panel.text')}` + ""); $tooltip.append(`
${translate("Right click: describe", 'interface@action_panel.text')}`); } } $tooltip.show(); } } } const actionPanelMapper = SMR.getSourceMapper('BeforeReturnInjection', `!${actionPanelInjector.toString()}()`); SMR.add('./action_panel', actionPanelMapper); // const adder = DataManager.makeAdder(s => typeof s === 'string'); RCManager.addHandlers('translation-handler', { onGameInitialize: async (rcfile) => { try { this.config = this.#getTranslationConfig(rcfile); if (this.config.translationLanguage) { if (this.config.useTranslationFont){ this.loadTranslationFont(this.config.translationLanguage); } const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5_000); // 10초 후 abort const { matchers, time, messages } = await fetch(this.config.translationFile, { cache: "no-store", signal: controller.signal }).then((r) => r.json()); clearTimeout(timeoutId); this.matchers = matchers; if (this.config.translationDebug) { console.log('[TranslationModule] Config:', this.config); console.log('[TranslationModule] Build time:', new Date(time)); console.log('[TranslationModule] Messages:', messages); console.log(`[TranslationModule] Matchers file loaded (${this.matchers.length}):`, this.matchers); } this.translator = new Translator(this.matchers, DataManager.functions, this.config.translationDebug); IOHook.handle_message.before.addHandler('translation-handler', (data) => { if (this.config.translationDebug) { console.log('[TranslationModule] data received:', JSON.parse(JSON.stringify(data))); } for (const key in DataManager.processors) { const {match, extract, restore} = DataManager.processors[key]; if (match(data)) { const list = extract(data); const translatedList = list.map((unitText) => { try { return this.translator.translate(unitText, this.config.translationLanguage, key) } catch (e) { if (this.config.translationDebug) { console.error(`[TranslationModule] ErrorKey: ${JSON.stringify(key)}, ErrorText: ${JSON.stringify(unitText)}`); } throw e; } }); if (this.config.translationDebug) { for (let i = 0; i < list.length; i++) { console.log(`%c<${key} [${i}]:ORIGINAL>%c\n${list[i]}\n%c<${key} [${i}]:TRANSLATED>%c\n${translatedList[i].translation}\n%c<${key} [${i}]:RESULT>%c\n%o\n${JSON.stringify(translatedList[i], null, 4)}\nhttps://translation.nemelex.cards/admin/core/matcher/add/?category=${encodeURIComponent(key)}&raw=${encodeURIComponent(list[i])}&priority=0`, 'font-weight: bold; color: red', '', 'font-weight: bold; color: blue', '', 'font-weight: bold; color: grey', '', translatedList[i]); } } restore(data, translatedList.map(result => result.translation)); } } }); if (this.config.language && this.config.language !== 'en') { this.sendMessage(`[TranslationModule] Do not use the "language = ${this.config.language}" option together with the translation_language option, as this may result in incorrect translation.`) } this.sendMessage(`[TranslationModule] ${matchers.length} matcher data loaded successfully. (${new Date(time).toLocaleString()}) / Thanks to ${messages[0]}`) if (this.config.translationDebug) { let stamp = null; const runChangeDetector = async () => { const res = await fetch(this.config.translationFile, { method: "HEAD", mode: "cors", cache: "no-store", }); const lm = res.headers.get("last-modified") ?? ""; const cl = res.headers.get("content-length") ?? ""; const newStamp = `${lm}|${cl}`; if (stamp && newStamp !== stamp) { const { matchers, time, messages } = await fetch(this.config.translationFile, { cache: "no-store", signal: controller.signal }).then((r) => r.json()); this.matchers = matchers; this.translator = new Translator(this.matchers, DataManager.functions, this.config.translationDebug); this.sendMessage(`[TranslationModule] DebugAutoReload: ${matchers.length} matcher data loaded successfully. (${new Date(time).toLocaleString()})`); if (this.debugAutoReloadCallback) { try { this.debugAutoReloadCallback(); } catch (e) { } } } stamp = newStamp; } runChangeDetector(); setInterval(runChangeDetector, 1000); } } } catch (e) { this.translator = {translate: (text) => ({translation: text})}; console.error(e); } }, onGameEnd: () => { this.unloadTranslationFont(); IOHook.handle_message.before.removeHandler('translation-handler'); } }); } } translation_language = ko