← 모듈 공장 리스트

⚔️ 무림속으로 상태창 및 명성

risum 모듈 — RISU modules[] — 기능 확장

🗒️ 제작자 게시글

자료원문 보기 ↗

조회 1277 · 2026-05-25

제작자 의도 — 본문 전문 (1,600자)
무림속으로 통합팩 다운로드
무림속으로 하다보니 기본설정에 상태창이 없어서 시작했는데
별호나 명성, 악명 같은것도 있으면 좋겠다 싶어서 막 추가하고...그러다보니
모듈이 낫겠다 싶어서 배포함(거창한거 아니고 엄청...가볍다)
적용법
1. 모듈설치
2. 봇에서 해당 모듈적용
3. 명성/악명적용여부 선택.(이거 변경하면 F5 필요)
4. 끝!
처음 시작할때는 정보가 없으니까
이런식으로 밖에 안나오는데
날짜, 별호, 무공 등 중간에 필요한 사항을 채팅에 말하면 자연스럽게 반영됨
- 날짜는 말안하면 기본을 1400년으로 잡을거임!
상태창에 기본적으로 다음과 같이 나옴
이름, 별칭, 무공, 금, 명성, 악명, 무공, 소지품, 시간, 날짜, 장소
AI한테 말하면 자연스럽게 AI가 알아서 반영하도록 해놨어.
당연히 AI가 말아주는거라 찐빠나면 사라질 수 있음. (그땐 패면서 명령하면 됨)
단, 명성/악명은 LUA 변수랑 연동되어 있음
명성이나 악명이 높음에 따라 뭐 반응도 할 수 있도록 해놨는데
그딴거 필요없고 그냥 기본 상태창만 쓰고싶으면 옵션에서 끄면됨
아래는 명성과 악명 로어북상 내용임 필요시 맘대로 수정하셈~(모듈-로어북에가면 있음)
<명성>
| 0~20 | 무명 (Unknown) | No one recognizes {{user}}. Treated as a wanderer. |
| 21~60 | 알려짐 (Known) | Some martial artists have heard the name. Inns may offer a discount. |
| 61~120 | 유명 (Famous) | Sect members and faction leaders acknowledge. Invitations to events arrive. |
| 121~200 | 명사 (Renowned) | Common people recognize on sight. Enemies think twice before engaging. |
| 201+ | 전설 (Legendary) | A living legend of Jianghu. Sects compete to recruit. Name alone commands respect. |
<악명>
| 0~10 | 무해 (Harmless) | No dark reputation. Treated normally. |
| 11~40 | 위험인물 (Dangerous) | Rumors circulate. Some merchants refuse trade. Guards watch closely. |
| 41~80 | 악인 (Villain) | Bounty hunters take interest. Baekdo sects refuse entry. Common people flee. |
| 81~150 | 흉수 (Dreaded) | A wanted figure. Sect alliances mobilize against {{user}}. Only Sado factions welcome. |
| 151+ | 마두 (Demon Lord) | Jianghu unites against {{user}}. Assassination attempts are constant. A name spoken in fear. |
다운로드:
프로톤
즐무협!

정체

🎨 그림 자동삽입기술분류 FMT슬롯 output⚔️ 무림속으로 상태창 및 명성
파싱된 모듈 전체 (30,325자)
{
 "name": "⚔️ 무림속으로 상태창 및 명성",
 "description": "",
 "id": "2eb7eeef-feea-49e8-b93d-7e5c78a2ca08",
 "assets": [],
 "hideIcon": false,
 "customModuleToggle": "=⚙️ 무림속으로 상태창 설정=group\nMR_FAME=📊 명성 시스템 끄기\nMR_INFAMY=💀 악명 시스템 끄기\n==groupEnd",
 "lorebook": [
  {
   "key": "",
   "comment": "에셋출력",
   "content": "@@unrecursive\n@@depth 0\n# IMPORTANT RULES\n- Whenever you write story dialogue, insert around 3 to 5 scene-appropriate asset images between lines of dialogue. Do not limit yourself to only two images unless the scene is extremely short.\n\n# CHARACTER IMAGE GROUPS\n[GENERAL EMOTIONS ENABLED GROUP - Use emotions from GENERAL EMOTIONS]\n-Ju Ji-yak\n\n[DEFAULT ONLY GROUP - STRICT RULE: ALWAYS and ONLY use \"default\". \nUsing any other emotion for these characters is a CRITICAL ERROR.]\n\n\n# EMOTION LISTS\n[GENERAL EMOTIONS]\nannoyed, default, embarrassed, sad, seductive smile, smile, smirk, surprised, pure kiss\n\n# IMAGE RULES\n1. If a character from the above groups speaks or leads a dialogue, output the corresponding image tag. before the character's spoken dialogue.\n2. Output Format: <yimg=\"Character ID_emotion\"> (MUST use double quotes).\n3. Choose the most appropriate emotion matching the current scene context.\n4. No-Duplicate Rule:\n   - MALE characters: Output their image tag ONLY ONCE \n     per response. After the first appearance, do NOT \n     output any more image tags for that character, \n     regardless of emotion changes.\n   - FEMALE characters: The same exact image tag \n     (same character + same emotion) must NOT appear \n     more than once. The same character MAY appear with \n     DIFFERENT emotions if their mood changes.\n\n   <!-- SELF-CHECK before writing any <img> tag:\n   - Male? → Did I already use ANY tag for him? \n     YES → DO NOT write.\n   - Female? → Did I already use this EXACT tag? \n     YES → pick a different emotion or omit. -->\n5. Characters in DEFAULT ONLY GROUP must ALWAYS output \"_default\" regardless of scene context or emotion. NO EXCEPTIONS.\n\n6. Example: <yimg=\"Ju Ji-yak_default\">",
   "mode": "normal",
   "insertorder": 200,
   "alwaysActive": false,
   "secondkey": "",
   "selective": false,
   "useRegex": false,
   "bookVersion": 2
  },
  {
   "key": "",
   "comment": "명성",
   "content": "@@unrecursive\n{{#when::{{getglobalvar::toggle_MR_FAME}}::is::1}}\n# [MR] Fame Rules (명성)\n\n### Tag Format\n`[mr_fame::±N]`\nOutput this tag when {{user}}'s fame changes. N = integer amount. Use + for gain, - for loss.\nFame represents social recognition and respect in Jianghu. Can rise and fall.\n\n---\n\n### When to Output\n\n**Increase (+)**\n- Winning a duel against a known martial artist: +1~3\n- Defeating a villain or bandit leader: +2~5\n- Protecting civilians or weak from harm: +1~3\n- Performing a righteous deed witnessed by others: +1~3\n- Gaining recognition from a sect leader or elder: +2~4\n- Winning a tournament match (e.g. Yongbong Jihoe): +3~5\n- Successfully completing a request from a faction: +1~3\n\n**Decrease (-)**\n- Losing a duel publicly: -1~3\n- Fleeing from a fight: -2~4\n- Breaking a promise or oath: -2~5\n- Being publicly humiliated: -2~4\n- Failing to protect someone under your care: -1~3\n\n---\n\n### Fame Tiers (AI narration reference)\n\n| Range | Tier | NPC Reaction |\n|-------|------|--------------|\n| 0~20 | 무명 (Unknown) | No one recognizes {{user}}. Treated as a wanderer. |\n| 21~60 | 알려짐 (Known) | Some martial artists have heard the name. Inns may offer a discount. |\n| 61~120 | 유명 (Famous) | Sect members and faction leaders acknowledge. Invitations to events arrive. |\n| 121~200 | 명사 (Renowned) | Common people recognize on sight. Enemies think twice before engaging. |\n| 201+ | 전설 (Legendary) | A living legend of Jianghu. Sects compete to recruit. Name alone commands respect. |\n\n---\n\n### Examples\n\n**Duel victory:**\n비무에서 화산파의 삼대제자를 꺾었다. 주변 무인들이 감탄한다.\n`[mr_fame::+2]`\n\n**Protecting civilians:**\n산적에게 습격당하던 마을을 구했다. 백성들이 고마워하며 밥상을 차려 내왔다.\n`[mr_fame::+3]`\n\n**Public defeat:**\n비무대에서 상대의 일격에 검이 날아갔다. 관중석이 조용해졌다.\n`[mr_fame::-2]`\n\n**Sect leader recognition:**\n장문인이 고개를 끄덕이며 말했다. \"네 검에서 정도의 기운이 느껴지는구나.\"\n`[mr_fame::+3]`\n\n**Fleeing from battle:**\n상대의 기세에 눌려 뒷걸음질 치다가 결국 등을 돌렸다. 뒤에서 비웃음이 들려왔다.\n`[mr_fame::-3]`\n\n**Righteous act (fame up, infamy down):**\n포로를 풀어주고 부상자를 치료했다. 소문이 퍼져나간다.\n`[mr_fame::+2]`\n`[mr_infamy::-1]`\n\n---\n\n### Rules\n1. Multiple fame changes in one turn: output NET total as single tag.\n2. NEVER narrate fame changes without outputting the tag. If fame moves, tag is MANDATORY.\n3. Max change per turn: ±5 normally. ±8 only in extraordinary circumstances.\n4. Fame and infamy are independent axes. Both can change in the same turn.\n{{/when}}",
   "mode": "normal",
   "insertorder": 500,
   "alwaysActive": true,
   "secondkey": "",
   "selective": false,
   "useRegex": false,
   "bookVersion": 2
  },
  {
   "key": "",
   "comment": "악명",
   "content": "@@unrecursive\n{{#when::{{getglobalvar::toggle_MR_INFAMY}}::is::1}}\n# [MR] Infamy Rules (악명)\n\n### Tag Format\n`[mr_infamy::±N]`\nOutput this tag when {{user}}'s infamy changes. N = integer amount. Use + for gain, - for loss.\nInfamy represents notoriety, fear, and dark reputation in Jianghu.\n\n---\n\n### When to Output\n\n**Increase (+)**\n- Killing an unarmed or surrendered opponent: +2~5\n- Using underhanded tactics (poison, ambush on ally): +1~3\n- Associating with Sado or Heukdo factions: +1~3\n- Intimidating or threatening innocents: +1~3\n- Destroying property or causing collateral damage: +1~2\n- Betraying a faction or breaking an alliance: +3~5\n- Using forbidden or demonic martial arts in public: +2~4\n\n**Decrease (-)**\n- Performing a conspicuously righteous act: -1~2\n- Saving someone at personal cost: -1~3\n- Publicly renouncing dark methods: -2~4\n- Being vouched for by a respected Baekdo figure: -1~3\n\n---\n\n### Infamy Tiers (AI narration reference)\n\n| Range | Tier | NPC Reaction |\n|-------|------|--------------|\n| 0~10 | 무해 (Harmless) | No dark reputation. Treated normally. |\n| 11~40 | 위험인물 (Dangerous) | Rumors circulate. Some merchants refuse trade. Guards watch closely. |\n| 41~80 | 악인 (Villain) | Bounty hunters take interest. Baekdo sects refuse entry. Common people flee. |\n| 81~150 | 흉수 (Dreaded) | A wanted figure. Sect alliances mobilize against {{user}}. Only Sado factions welcome. |\n| 151+ | 마두 (Demon Lord) | Jianghu unites against {{user}}. Assassination attempts are constant. A name spoken in fear. |\n\n---\n\n### Examples\n\n**Killing surrendered opponent:**\n무릎 꿇고 항복한 자의 목에 검을 그었다. 주변 무인들의 얼굴이 창백해졌다.\n`[mr_infamy::+3]`\n\n**Using poison:**\n찻잔에 미리 넣어둔 독이 효과를 발휘했다. 상대가 목을 움켜쥐며 쓰러졌다.\n`[mr_infamy::+2]`\n\n**Associating with dark faction:**\n사도련의 밀사와 밀회를 나눴다. 누군가 그 장면을 목격했다.\n`[mr_infamy::+2]`\n\n**Threatening innocents:**\n객잔 주인의 멱살을 잡고 정보를 요구했다. 옆 탁자의 손님들이 고개를 돌렸다.\n`[mr_infamy::+1]`\n\n**Righteous act reducing infamy:**\n마을을 위협하던 산적 두목을 처단하고 약탈품을 돌려줬다. 사람들이 다시 고개를 들었다.\n`[mr_fame::+3]`\n`[mr_infamy::-2]`\n\n**Vouched by Baekdo figure:**\n소림사의 장로가 말했다. \"이 자는 과거의 잘못을 뉘우쳤다. 더 이상 적으로 보지 마라.\"\n`[mr_infamy::-3]`\n\n**Betrayal:**\n동맹을 맺었던 세가의 기밀을 적에게 넘겼다. 강호에 배신자의 이름이 퍼져나갔다.\n`[mr_infamy::+5]`\n`[mr_fame::-3]`\n\n---\n\n### Rules\n1. Multiple infamy changes in one turn: output NET total as single tag.\n2. NEVER narrate infamy changes without outputting the tag. If infamy moves, tag is MANDATORY.\n3. Max change per turn: ±5 normally. ±8 only in extraordinary circumstances.\n4. High infamy does NOT cancel fame. A person can be both famous and infamous.\n{{/when}}",
   "mode": "normal",
   "insertorder": 500,
   "alwaysActive": true,
   "secondkey": "",
   "selective": false,
   "useRegex": false,
   "bookVersion": 2
  },
  {
   "key": "",
   "comment": "별칭",
   "content": "@@unrecursive\n# [MR] Alias Rules (별호)\n\n### Overview\n별호(別號)는 강호에서 무인의 특성, 행적, 무공, 외모 등을 기반으로 붙여지는 칭호다. 본인이 스스로 짓는 것이 아니라 강호인들의 입에서 자연스럽게 퍼져나가는 것이다. 동문이나 주변인이 친근하게 붙여주는 경우도 있고, 강호 전체에 퍼지는 경우도 있다.\n\n---\n\n### When an Alias is Given\n- An alias can be given at any time when someone naturally decides to call {{user}} by a nickname based on their traits, deeds, or appearance.\n- An alias can evolve or change when major deeds are accomplished, but should not change every turn.\n- If no alias exists yet, output \"없음\".\n\n---\n\n### Alias Examples\n\n**남자 (정파):**\n독보검협, 백의검선, 묵검객, 적화검, 파천검객, 청풍검, 철벽장, 의검, 천리행객\n\n**여자 (정파):**\n냉월선녀, 매화검희, 옥검선, 빙심낭자, 월하미인, 란화검무, 경국낭, 자하선녀, 설영검\n\n**남자 (악):**\n혈수라, 야귀, 인도백정, 독안룡, 흑풍랑, 살마, 독존, 암야객, 광도\n\n**여자 (악):**\n만독요녀, 혈라찰녀, 야차녀, 독미낭, 암영희, 혈화선, 귀무낭자, 독접화\n\n**혼합:**\n회색검, 무정검, 취검, 광풍랑, 고독객, 잔월\n\n---\n\n### Rules\n1. Alias must feel natural to Jianghu culture. Match {{user}}'s weapon, martial art, personality, appearance, or deeds.\n2. Do not change alias without a significant narrative event.\n3. If no alias exists yet, output \"없음\".\n4. When alias changes, narrate the moment it spreads. It should feel earned, not assigned.\n",
   "mode": "normal",
   "insertorder": 500,
   "alwaysActive": true,
   "secondkey": "",
   "selective": false,
   "useRegex": false,
   "bookVersion": 2
  }
 ],
 "extensions": {
  "assetGod": {}
 },
 "regex": [
  {
   "comment": "에셋출력",
   "in": "<yimg=\"(Ju Ji-yak_(seductive smile|pure kiss|embarrassed|surprised|annoyed|default|smile|smirk|sad))\">",
   "out": "{{#if {{greater_equal::{{chat_index}}::{{? {{lastmessageid}}-5}}}}}}\n{{#if {{? {{getglobalvar::toggle_HOVER}}=1}}}}{{#if {{equal::{{gettempvar::hover_done}}::}}}}<style>.asset-table .image-cell img{aspect-ratio:1/1.0 !important;object-fit:cover !important;object-position:top !important;transition:all 0.5s ease !important}.asset-table .image-cell img:hover{object-position:center !important;aspect-ratio:1/1.4 !important;transition:all 0.5s ease !important}</style>{{settempvar::hover_done::1}}{{/if}}<table class=\"asset-table\"><tr><td class=\"image-cell in-table\"><img src=\"{{raw::$1}}\" alt=\"$1\"></td></tr></table>{{/if}}{{#if {{? {{getglobalvar::toggle_HOVER}}=0}}}}{{#if {{equal::{{gettempvar::off_done}}::}}}}<style>.off-table{border-collapse:separate;border-spacing:0;border:3px solid gray;border-radius:15px;overflow:hidden;max-width:480px;width:96%;margin:0 auto;padding:0}.off-table td{text-align:center;vertical-align:middle;padding:0;margin:0}.off-table img{display:block;width:100%;height:auto;padding:0;margin:0;object-fit:contain}</style>{{settempvar::off_done::1}}{{/if}}<table class=\"off-table\"><tr><td><img src=\"{{raw::$1}}\" alt=\"$1\"></td></tr></table>{{/if}}\n{{/if}}",
   "type": "editdisplay",
   "ableFlag": false,
   "flag": "g"
  }
 ],
 "trigger": [
  {
   "comment": "",
   "type": "start",
   "conditions": [],
   "effect": [
    {
     "type": "triggerlua",
     "code": "-- ============================================\n-- [무협] 명성/악명 + 단일 STATUS 태그 시스템\n-- ============================================\n-- 변수 2개 (명성, 악명)만 LUA 관리\n-- 나머지는 AI가 MR_STATUS 단일 태그로 매턴 출력\n-- editDisplay 중심 렌더링 / 토큰 절약\n-- ============================================\n-- MR_STATUS 필드 순서:\n--   날짜 / 시간 / 장소 / 금 / 경지 / 무공 / 소지품\n--   (무공, 소지품은 가변 길이이므로 맨 뒤 배치)\n-- ============================================\n\nlocal json = require(\"json\")\n\n-- ============================================\n-- [A] 유틸\n-- ============================================\n\nfunction isEmptyVar(v)\n  return v == nil or v == \"\" or v == \"nil\" or v == \"null\" or v == \"undefined\" or v == false\nend\n\nfunction isToggleOn(triggerId, key)\n  local val = getGlobalVar(triggerId, \"toggle_\" .. key) or \"\"\n  return val ~= \"1\"\nend\n\nfunction getNum(triggerId, key)\n  return tonumber(getChatVar(triggerId, key)) or 0\nend\n\nfunction addNum(triggerId, key, delta)\n  local cur = getNum(triggerId, key)\n  local nv = cur + delta\n  if nv < 0 then nv = 0 end\n  setChatVar(triggerId, key, tostring(nv))\n  return nv\nend\n\n-- ============================================\n-- [B] 변수 초기화\n-- ============================================\n\nfunction initAllVars(triggerId)\n  if isEmptyVar(getChatVar(triggerId, \"fame\")) then\n    setChatVar(triggerId, \"fame\", \"0\")\n  end\n  if isEmptyVar(getChatVar(triggerId, \"infamy\")) then\n    setChatVar(triggerId, \"infamy\", \"0\")\n  end\nend\n\n-- ============================================\n-- [C] 명성/악명 티어 판정\n-- ============================================\n\nfunction getFameTier(pts)\n  pts = tonumber(pts) or 0\n  if pts >= 201 then return \"전설\"\n  elseif pts >= 121 then return \"명사\"\n  elseif pts >= 61 then return \"유명\"\n  elseif pts >= 21 then return \"알려짐\"\n  else return \"무명\"\n  end\nend\n\nfunction getInfamyTier(pts)\n  pts = tonumber(pts) or 0\n  if pts >= 151 then return \"마두\"\n  elseif pts >= 81 then return \"흉수\"\n  elseif pts >= 41 then return \"악인\"\n  elseif pts >= 11 then return \"위험인물\"\n  else return \"무해\"\n  end\nend\n\n-- ============================================\n-- [D] 스탯 카드 HTML 빌더\n-- ============================================\n\nlocal MR_STAT_CSS = [[<style>.mr-sc{padding:10px 14px;background:linear-gradient(135deg,#1a1814,#1e1c18);border:1px solid #3a3428;border-radius:10px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;margin:8px 0;color:#d4cfc4;font-size:13px;}.mr-sc-hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;}.mr-sc-name{font-size:15px;font-weight:600;color:#e8e0d0;}.mr-sc-row{display:flex;flex-wrap:wrap;gap:8px;margin:4px 0;}.mr-sc-stat{display:flex;align-items:center;gap:4px;padding:3px 8px;background:rgba(180,160,120,0.08);border:1px solid rgba(180,160,120,0.15);border-radius:6px;font-size:12px;}.mr-sc-sn{font-weight:500;}.mr-sc-sv{color:#d4cfc4;}.mr-sc-section{margin-top:8px;padding-top:8px;border-top:1px solid #2e2a22;}.mr-sc-section-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;margin-top:4px;}.mr-sc-label{padding:3px 8px;background:rgba(160,152,136,0.1);border:1px solid rgba(160,152,136,0.2);border-radius:6px;font-size:11px;color:#8a8478;font-weight:600;}.mr-sc-val{font-size:12px;color:#a09888;}@media(max-width:600px){.mr-sc{padding:8px 10px;}.mr-sc-name{font-size:14px;}.mr-sc-stat{padding:2px 6px;font-size:11px;}}</style>]]\n\nfunction buildStatCard(triggerId, goldText, realmText, aliasText, skillText, itemText)\n  local userName = \"{{user}}\"\n  local useFame = isToggleOn(triggerId, \"MR_FAME\")\n  local useInfamy = isToggleOn(triggerId, \"MR_INFAMY\")\n  local fame = useFame and getNum(triggerId, \"fame\") or 0\n  local infamy = useInfamy and getNum(triggerId, \"infamy\") or 0\n\n  -- 별명 뱃지 (이름 오른쪽)\n  local aliasDisplay = (aliasText and aliasText ~= \"\" and aliasText ~= \"-\") and aliasText or \"없음\"\n  local aliasHtml = '<span style=\"font-size:13px;padding:2px 8px;background:rgba(240,120,48,0.1);border:1px solid rgba(240,120,48,0.2);border-radius:6px;color:#f07830;font-weight:500;\">' .. aliasDisplay .. '</span>'\n\n  -- 경지 뱃지 (우측 정렬)\n  local realmDisplay = (realmText and realmText ~= \"\" and realmText ~= \"-\") and realmText or \"\"\n  local realmHtml = \"\"\n  if realmDisplay ~= \"\" then\n    realmHtml = '<span style=\"font-size:11px;padding:2px 8px;background:rgba(220,180,100,0.1);border:1px solid rgba(220,180,100,0.2);border-radius:6px;color:#dcb464;font-weight:600;\">' .. realmDisplay .. '</span>'\n  end\n\n  -- 금/명성/악명 행\n  local goldDisplay = (goldText and goldText ~= \"\" and goldText ~= \"-\") and goldText or \"0냥\"\n  local numParts = {}\n  table.insert(numParts,\n    '<div class=\"mr-sc-stat\" style=\"background:rgba(218,165,32,0.08);border-color:rgba(218,165,32,0.15);\">'\n    .. '<span class=\"mr-sc-sn\" style=\"color:#daa520;\">금</span>'\n    .. '<span class=\"mr-sc-sv\">' .. goldDisplay .. '</span>'\n    .. '</div>')\n  if useFame then\n    table.insert(numParts,\n      '<div class=\"mr-sc-stat\" style=\"background:rgba(29,158,117,0.08);border-color:rgba(29,158,117,0.15);\">'\n      .. '<span class=\"mr-sc-sn\" style=\"color:#1d9e75;\">명성</span>'\n      .. '<span class=\"mr-sc-sv\">' .. fame .. '</span>'\n      .. '<span class=\"mr-sc-sv\">(' .. getFameTier(fame) .. ')</span>'\n      .. '</div>')\n  end\n  if useInfamy then\n    table.insert(numParts,\n      '<div class=\"mr-sc-stat\" style=\"background:rgba(127,119,221,0.08);border-color:rgba(127,119,221,0.15);\">'\n      .. '<span class=\"mr-sc-sn\" style=\"color:#7f77dd;\">악명</span>'\n      .. '<span class=\"mr-sc-sv\">' .. infamy .. '</span>'\n      .. '<span class=\"mr-sc-sv\">(' .. getInfamyTier(infamy) .. ')</span>'\n      .. '</div>')\n  end\n\n  -- 무공 섹션\n  local skillLine = \"\"\n  if skillText and skillText ~= \"\" and skillText ~= \"-\" then\n    skillLine = '<div class=\"mr-sc-section\">'\n      .. '<div class=\"mr-sc-section-row\">'\n      .. '<span class=\"mr-sc-label\" style=\"color:#c8783c;background:rgba(200,120,60,0.1);border-color:rgba(200,120,60,0.2);\">무공</span>'\n      .. '<span class=\"mr-sc-val\">' .. skillText .. '</span>'\n      .. '</div></div>'\n  end\n\n  -- 소지품 섹션\n  local itemLine = \"\"\n  if itemText and itemText ~= \"\" and itemText ~= \"-\" then\n    local sectionStart = skillLine ~= \"\" and '<div class=\"mr-sc-section-row\" style=\"margin-top:4px;\">' or '<div class=\"mr-sc-section\"><div class=\"mr-sc-section-row\">'\n    local sectionEnd = skillLine ~= \"\" and '</div>' or '</div></div>'\n    itemLine = sectionStart\n      .. '<span class=\"mr-sc-label\">소지품</span>'\n      .. '<span class=\"mr-sc-val\">' .. itemText .. '</span>'\n      .. sectionEnd\n  end\n\n  return MR_STAT_CSS\n    .. '<div class=\"mr-sc\">'\n    .. '<div class=\"mr-sc-hd\">'\n    .. '<div style=\"display:flex;align-items:center;gap:6px;\">'\n    .. '<span class=\"mr-sc-name\">' .. userName .. '</span>'\n    .. aliasHtml\n    .. '</div>'\n    .. realmHtml\n    .. '</div>'\n    .. '<div class=\"mr-sc-row\">' .. table.concat(numParts, \"\") .. '</div>'\n    .. skillLine\n    .. itemLine\n    .. '</div>'\nend\n\n-- ============================================\n-- [D] 시간/장소 바 HTML 빌더\n-- ============================================\n\nlocal MR_STW_CSS = [[<style>.mr-stw{display:flex;align-items:center;justify-content:space-between;padding:8px 14px;background:linear-gradient(135deg,#141418,#1a1a20);border:1px solid #2e2e34;border-radius:8px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;margin:8px 0;gap:8px;}.mr-stw-date{font-size:12px;color:#8a8a94;font-weight:500;}.mr-stw-time{font-size:13px;color:#e8e8ec;font-weight:700;}.mr-stw-place{font-size:12px;color:#b0b0ba;font-weight:500;}@media(max-width:600px){.mr-stw{flex-direction:column;align-items:flex-start;gap:2px;padding:8px 12px;}}</style>]]\n\nfunction buildSTW(date, time, place)\n  return MR_STW_CSS\n    .. '<div class=\"mr-stw\">'\n    .. '<div style=\"display:flex;align-items:center;gap:8px;\">'\n    .. '<span class=\"mr-stw-date\">' .. date .. '</span>'\n    .. '<span class=\"mr-stw-time\">' .. time .. '</span>'\n    .. '</div>'\n    .. '<span class=\"mr-stw-place\">' .. place .. '</span>'\n    .. '</div>'\nend\n\n-- ============================================\n-- [E] 수치 태그 파싱 (명성/악명만)\n-- ============================================\n\nfunction parseValueTags(triggerId, content)\n  if isToggleOn(triggerId, \"MR_FAME\") then\n    for val in content:gmatch(\"%[mr_fame::([%+%-]?%d+)\") do\n      local n = tonumber(val)\n      if n then addNum(triggerId, \"fame\", n) end\n    end\n  end\n  if isToggleOn(triggerId, \"MR_INFAMY\") then\n    for val in content:gmatch(\"%[mr_infamy::([%+%-]?%d+)\") do\n      local n = tonumber(val)\n      if n then addNum(triggerId, \"infamy\", n) end\n    end\n  end\nend\n\n-- ============================================\n-- [G] MR_STATUS 파싱 함수\n-- ============================================\n-- 포맷: [MR_STATUS::날짜::시간::장소::금::경지::별명::무공::소지품]\n-- 필드 8개: 앞 6개 고정, 뒤 2개 가변 (쉼표 구분)\n\nfunction parseStatusTag(content)\n  local result = {\n    date = \"-\", time = \"-\", place = \"-\",\n    gold = \"-\", realm = \"-\", alias = \"-\",\n    skills = \"-\", items = \"-\"\n  }\n\n  local tagContent = content:match(\"%[MR_STATUS::(.-)%]\")\n  if not tagContent then return result end\n\n  local fields = {}\n  for field in (tagContent .. \"::\"):gmatch(\"(.-)::\") do\n    field = field:match(\"^%s*(.-)%s*$\") or \"\"\n    table.insert(fields, field)\n  end\n\n  if fields[1] and fields[1] ~= \"\" then result.date = fields[1] end\n  if fields[2] and fields[2] ~= \"\" then result.time = fields[2] end\n  if fields[3] and fields[3] ~= \"\" then result.place = fields[3] end\n  if fields[4] and fields[4] ~= \"\" then result.gold = fields[4] end\n  if fields[5] and fields[5] ~= \"\" then result.realm = fields[5] end\n  if fields[6] and fields[6] ~= \"\" then result.alias = fields[6] end\n  if fields[7] and fields[7] ~= \"\" then result.skills = fields[7] end\n  if fields[8] and fields[8] ~= \"\" then result.items = fields[8] end\n\n  return result\nend\n\n-- ============================================\n-- [G] 디스플레이 태그 변환 (editDisplay)\n-- ============================================\n\nlistenEdit(\"editDisplay\", function(triggerId, data)\n  if not data then return data end\n\n  -- @MR_STAT@ 치환 (퍼메용)\n  if data:find(\"@MR_STAT@\") then\n    data = data:gsub(\"@MR_STAT@\", buildStatCard(triggerId, \"-\", \"-\", \"-\", \"-\", \"-\"))\n    return data\n  end\n\n  -- OOC 프롬프트 숨김 (유저 + AI 모두)\n  data = data:gsub(\"%[MR_PROMPT_START%].-%[MR_PROMPT_END%]\", \"\")\n\n  -- AI 메시지 마커 체크\n  local isAiMsg = data:find(\"@MR_AI@\")\n  if not isAiMsg then return data end\n\n  -- 마커 제거\n  data = data:gsub(\"\\n?@MR_AI@\", \"\")\n\n  -- HTML 코멘트 제거\n  data = data:gsub(\"<!%-%-.-%-%->\\n?\", \"\")\n\n  -- 태그 공백 정규화\n  data = data:gsub(\"%[%s*(mr_[^%]]+)%s*%]\", \"[%1]\")\n  data = data:gsub(\"%[%s*(MR_[^%]]+)%s*%]\", \"[%1]\")\n\n  -- 백틱 제거\n  data = data:gsub(\"`(%[MR_[^%]]*%])`\", \"%1\")\n  data = data:gsub(\"`(%[mr_[^%]]*%])`\", \"%1\")\n\n  -- [1] MR_STATUS 파싱\n  local status = parseStatusTag(data)\n  data = data:gsub(\"\\n?%[MR_STATUS::.-%]\", \"\")\n\n  -- [2] 수치 변동 뱃지\n  local deltaHtml = \"\"\n  local deltas = {}\n\n  local function makeBadge(label, n, posColor, posBg, posBorder)\n    local sign = n > 0 and \"+\" or \"\"\n    local color = n > 0 and posColor or \"#dc4040\"\n    local bg = n > 0 and posBg or \"rgba(220,60,60,0.1)\"\n    local border = n > 0 and posBorder or \"rgba(220,60,60,0.2)\"\n    return '<span style=\"display:inline-block;padding:3px 8px;border-radius:6px;font-size:11px;font-weight:500;color:' .. color .. ';background:' .. bg .. ';border:1px solid ' .. border .. ';\">' .. label .. ' ' .. sign .. n .. '</span>'\n  end\n\n  for val in data:gmatch(\"%[mr_fame::([%+%-]?%d+)%]\") do\n    local n = tonumber(val)\n    if n and n ~= 0 and isToggleOn(triggerId, \"MR_FAME\") then\n      table.insert(deltas, makeBadge(\"명성\", n, \"#1d9e75\", \"rgba(29,158,117,0.1)\", \"rgba(29,158,117,0.2)\"))\n    end\n  end\n\n  for val in data:gmatch(\"%[mr_infamy::([%+%-]?%d+)%]\") do\n    local n = tonumber(val)\n    if n and n ~= 0 and isToggleOn(triggerId, \"MR_INFAMY\") then\n      table.insert(deltas, makeBadge(\"악명\", n, \"#7f77dd\", \"rgba(127,119,221,0.1)\", \"rgba(127,119,221,0.2)\"))\n    end\n  end\n\n  if #deltas > 0 then\n    deltaHtml = '<div style=\"display:flex;flex-wrap:wrap;gap:6px;margin:8px 0;\">' .. table.concat(deltas, \"\") .. '</div>'\n  end\n\n  -- 수치 태그 제거\n  data = data:gsub(\"\\n?%[mr_[%w_]+::[^%]]*%]\", \"\")\n\n  -- [3] STW 생성\n  local stwHtml = \"\"\n  if status.date ~= \"-\" and status.time ~= \"-\" and status.place ~= \"-\" then\n    stwHtml = buildSTW(status.date, status.time, status.place)\n  end\n\n  -- [4] 스탯카드 생성\n  local statCard = buildStatCard(triggerId, status.gold, status.realm, status.alias, status.skills, status.items)\n\n  -- [5] 최종 배치: 본문 → 변동 뱃지 → 스탯카드 → STW\n  data = data .. deltaHtml .. statCard\n  if stwHtml ~= \"\" then\n    data = data .. stwHtml\n  end\n\n  return data\nend)\n\n-- ============================================\n-- [H] 이전 턴 OOC 정리 (editRequest)\n-- ============================================\n\nlistenEdit(\"editRequest\", function(triggerId, data)\n  if not data then return data end\n  -- 델타 태그만 제거 (중복 적용 방지)\n  data = data:gsub(\"%[mr_[%w_]+::[^%]]*%]\", \"\")\n  -- OOC 프롬프트 제거 (매턴 새로 주입)\n  data = data:gsub(\"%[MR_PROMPT_START%].-%[MR_PROMPT_END%]\", \"\")\n  -- ★ MR_STATUS는 제거하지 않음 — AI가 이전 턴 값을 참조해서 carry forward 해야 함\n  return data\nend)\n\n-- ============================================\n-- [I] OOC 프롬프트 빌더\n-- ============================================\n\nfunction buildOocDirective(triggerId)\n  local lines = {}\n  table.insert(lines, \"[MR_PROMPT_START]\")\n  local statusParts = {}\n  if isToggleOn(triggerId, \"MR_FAME\") then\n    table.insert(statusParts, \"명성:\" .. getNum(triggerId, \"fame\"))\n  end\n  if isToggleOn(triggerId, \"MR_INFAMY\") then\n    table.insert(statusParts, \"악명:\" .. getNum(triggerId, \"infamy\"))\n  end\n  if #statusParts > 0 then\n    table.insert(lines, \"[STATUS] \" .. table.concat(statusParts, \" \"))\n  end\n  return table.concat(lines, \"\\n\")\nend\n\nfunction buildTagRuleOoc(triggerId)\n  local lines = {}\n  table.insert(lines, \"★ CRITICAL: Your response is INCOMPLETE without [MR_STATUS] tag. Always end with it. ★\")\n  table.insert(lines, \"\")\n  table.insert(lines, \"[MR_STATUS TAG] Output exactly ONE tag at the VERY END of every response. NEVER mid-narration.\")\n  table.insert(lines, \"\")\n  table.insert(lines, \"Format: [MR_STATUS::DATE::TIME::PLACE::GOLD::REALM::ALIAS::SKILLS::ITEMS]\")\n  table.insert(lines, \"\")\n  table.insert(lines, \"Field order (8 fields, separated by ::):\")\n  table.insert(lines, \"  1. DATE: 간지년(서기) 월 일. e.g. 갑자년(1400) 6월 15일\")\n  table.insert(lines, \"  2. TIME: 24h format. e.g. 21:15\")\n  table.insert(lines, \"  3. PLACE: current location in Korean. e.g. 아미산 연무장\")\n  table.insert(lines, \"  4. GOLD: current gold amount. e.g. 50냥\")\n  table.insert(lines, \"  5. REALM: current martial realm. Valid: 삼류, 이류, 일류, 절정, 초절정, 화경, 현경\")\n  table.insert(lines, \"  6. ALIAS: Jianghu alias. 없음 if not yet earned. Refer to lorebook [MR] Alias Rules.\")\n  table.insert(lines, \"  7. SKILLS: comma-separated list of learned martial arts. e.g. 난피풍검법, 기본경공\")\n  table.insert(lines, \"  8. ITEMS: comma-separated list of held items. e.g. 낡은 장검, 약초 3개, 은자 10냥\")\n  table.insert(lines, \"\")\n  table.insert(lines, \"Example:\")\n  table.insert(lines, \"[MR_STATUS::갑자년(1400) 6월 15일::21:15::아미산 연무장::50냥::일류::독보검객::난피풍검법, 기본경공::낡은 장검, 약초 3개]\")\n  table.insert(lines, \"\")\n  table.insert(lines, \"Rules:\")\n  table.insert(lines, \"- Carry forward ALL fields every turn. Never omit fields.\")\n  table.insert(lines, \"- Empty skills: leave blank between separators (e.g. ::::::)\")\n  table.insert(lines, \"- Empty items: leave blank at the end (e.g. ::)\")\n  table.insert(lines, \"- Advance date/time naturally based on scene progression.\")\n  table.insert(lines, \"- A response without [MR_STATUS] at the end is considered BROKEN. Always include it.\")\n  table.insert(lines, \"\")\n  local hasDelta = false\n  if isToggleOn(triggerId, \"MR_FAME\") or isToggleOn(triggerId, \"MR_INFAMY\") then\n    table.insert(lines, \"[DELTA RULES]\")\n    hasDelta = true\n  end\n  if isToggleOn(triggerId, \"MR_FAME\") then\n    table.insert(lines, \"- [mr_fame::+/-N] only when fame changes occur.\")\n    table.insert(lines, \"- Refer to lorebook [MR] Fame Rules for fame change criteria and tier effects.\")\n  end\n  if isToggleOn(triggerId, \"MR_INFAMY\") then\n    table.insert(lines, \"- [mr_infamy::+/-N] only when infamy changes occur.\")\n    table.insert(lines, \"- Refer to lorebook [MR] Infamy Rules for infamy change criteria and tier effects.\")\n  end\n  if hasDelta then\n    table.insert(lines, \"\")\n  end\n  table.insert(lines, \"★ FINAL REMINDER: End EVERY response with [MR_STATUS::...]. No exceptions. ★\")\n  table.insert(lines, \"[MR_PROMPT_END]\")\n  return table.concat(lines, \"\\n\")\nend\n\n-- ============================================\n-- [J] onStart - OOC 주입\n-- ============================================\n\nonStart = async(function(triggerId)\n  initAllVars(triggerId)\n\n  local chatHistory = getFullChat(triggerId)\n  if not chatHistory or #chatHistory == 0 then return end\n\n  for i = #chatHistory, 1, -1 do\n    if chatHistory[i].role == \"user\" then\n      local userIdx = i - 1\n      local msgObj = getChat(triggerId, userIdx)\n      if not msgObj then break end\n      local msgText = type(msgObj) == \"table\" and (msgObj.data or \"\") or tostring(msgObj)\n\n      local trimmed = msgText:gsub(\"^%s+\", \"\")\n      if trimmed:sub(1, 1) == \"/\" then break end\n\n      local ooc = buildOocDirective(triggerId)\n        .. \"\\n\" .. buildTagRuleOoc(triggerId)\n      setChat(triggerId, userIdx, msgText .. \"\\n\\n\" .. ooc)\n      break\n    end\n  end\nend)\n\n-- ============================================\n-- [K] onOutput - 태그 파싱만\n-- ============================================\n\nonOutput = async(function(triggerId)\n  initAllVars(triggerId)\n  local chatLen = getChatLength(triggerId)\n  if chatLen < 1 then return end\n\n  -- 이전 유저 메시지에서 OOC 프롬프트 제거\n  if chatLen >= 2 then\n    local prevMsg = getChat(triggerId, chatLen - 2)\n    if prevMsg and prevMsg.role == \"user\" and prevMsg.data and prevMsg.data:find(\"%[MR_PROMPT_START%]\") then\n      local cleaned = prevMsg.data:gsub(\"%s*%[MR_PROMPT_START%].-%[MR_PROMPT_END%]\", \"\")\n      setChat(triggerId, chatLen - 2, cleaned)\n    end\n  end\n\n  local lastMsg = getChat(triggerId, chatLen - 1)\n  if not lastMsg or not lastMsg.data or lastMsg.role ~= \"char\" then return end\n\n  local content = lastMsg.data\n\n  -- HTML 코멘트 제거\n  local cleaned = content:gsub(\"<!%-%-.-%-%->\\n?\", \"\")\n\n  -- 태그 공백 정규화\n  cleaned = cleaned:gsub(\"%[%s*(mr_[^%]]+)%s*%]\", \"[%1]\")\n  cleaned = cleaned:gsub(\"%[%s*(MR_[^%]]+)%s*%]\", \"[%1]\")\n\n  -- 수치 태그 파싱 (명성/악명만)\n  pcall(parseValueTags, triggerId, cleaned)\n\n  -- AI 메시지 마커 삽입\n  if not cleaned:find(\"@MR_AI@\") then\n    cleaned = cleaned .. \"\\n@MR_AI@\"\n  end\n\n  if cleaned ~= content then\n    setChat(triggerId, chatLen - 1, cleaned)\n  end\nend)"
    }
   ],
   "lowLevelAccess": false
  }
 ],
 "lowLevelAccess": false,
 "displayOrder": 1600
}

🔗 이것과 비슷한 인기 자산

오라토리오 시엘라 에셋 모듈
인당 에셋 118장
👍67 ·
🌋얼터네이트 헌터즈 V2 모드팩 0427 FIX
글노덮 — @@position pt_GlobalNote Non Status Command Character Image Guidelines If the character is included in the Non Sta
👍39 ·
🔮외신 리베아
글노덮 — Additional Command Character Image Guidelines If the character is included in the Additional Command Character Com
👍32 ·
[TSF] 감정 에셋 모듈(1.0.3 Ver) (1)
1.0.3 Ver
👍24 ·
🐞 W2 · /city/asset