{"id":26244,"date":"2025-12-25T16:24:58","date_gmt":"2025-12-25T07:24:58","guid":{"rendered":"http:\/\/www.tyosuke20xx.com\/blog\/?p=26244"},"modified":"2025-12-25T16:25:00","modified_gmt":"2025-12-25T07:25:00","slug":"aran-red-fantasy-html","status":"publish","type":"post","link":"http:\/\/www.tyosuke20xx.com\/blog\/?p=26244","title":{"rendered":"Aran Red Fantasy.html"},"content":{"rendered":"\n<pre class=\"wp-block-code\"><code>\n&lt;!DOCTYPE html>\n&lt;html lang=\"en\">\n&lt;head>\n  &lt;meta charset=\"UTF-8\" \/>\n  &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\/>\n  &lt;title>Aran Red Fantasy - Ultimate&lt;\/title>\n\n  &lt;style>\n    \/* ===============================\n       \u57fa\u672cCSS\u30b9\u30bf\u30a4\u30eb\n    =============================== *\/\n    body {\n      font-family: Arial, sans-serif;\n      background-color: #f0f0f0;\n      margin: 0;\n      padding: 0;\n    }\n    header, footer, nav {\n      background-color: #005ce6;\n      color: #fff;\n      text-align: center;\n      padding: 10px;\n    }\n    header h1, footer .container { margin: 0; }\n\n    nav a {\n      color: #fff;\n      text-decoration: none;\n      margin: 0 8px;\n      padding: 5px 8px;\n      display: inline-block;\n    }\n    nav a:hover { background-color: #004bb5; border-radius: 4px; }\n    nav a.active { background-color: #003a8c; border-radius: 4px; }\n\n    main { padding: 20px; }\n    .container { max-width: 1400px; margin: 0 auto; }\n\n    .button {\n      background-color: #4CAF50;\n      border: none;\n      color: white;\n      padding: 8px 16px;\n      text-align: center;\n      text-decoration: none;\n      font-size: 14px;\n      margin: 4px 2px;\n      cursor: pointer;\n      border-radius: 5px;\n    }\n    .button:hover { background-color: #45a049; }\n    .disabled { opacity: 0.6; cursor: default; }\n\n    .muted { color:#667; font-size: 13px; }\n\n    \/* \u30ab\u30fc\u30c9\u98a8 *\/\n    .card {\n      background-color: #fff;\n      border: 1px solid #ddd;\n      border-radius: 5px;\n      padding: 16px;\n      margin-bottom: 20px;\n      box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);\n    }\n    .card h3 { margin-top: 0; }\n\n    \/* \u30d7\u30ed\u30b0\u30ec\u30b9\u30d0\u30fc *\/\n    .progress-bar {\n      background-color: #ddd;\n      border-radius: 5px;\n      height: 20px;\n      width: 100%;\n      margin-bottom: 10px;\n    }\n    .progress {\n      background-color: #4CAF50;\n      height: 100%;\n      border-radius: 5px;\n      width: 0%;\n    }\n\n    \/* \u30a4\u30f3\u30d9\u30f3\u30c8\u30ea\u30a2\u30a4\u30c6\u30e0\u8868\u793a *\/\n    .inventory-item {\n      background-color: #fff;\n      border: 1px solid #ddd;\n      border-radius: 5px;\n      display: inline-block;\n      margin: 5px;\n      padding: 10px;\n      min-width: 120px;\n      text-align: center;\n      cursor: pointer;\n      transition: background-color 0.2s;\n      user-select: none;\n    }\n    .inventory-item:hover { background-color: #eef; }\n\n    \/* \u30e1\u30c3\u30bb\u30fc\u30b8\u8868\u793a *\/\n    .message {\n      background-color: #fff8dd;\n      border: 1px solid #f5c666;\n      padding: 10px;\n      margin-bottom: 10px;\n      border-radius: 5px;\n      white-space: pre-wrap;\n    }\n\n    \/* \u30e2\u30fc\u30c0\u30eb *\/\n    .modal-bg {\n      position: fixed;\n      top: 0; left: 0;\n      width:100%; height:100%;\n      background: rgba(0,0,0,.5);\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      z-index: 999;\n    }\n    .modal {\n      background: #fff;\n      padding: 20px;\n      border-radius: 5px;\n      text-align: center;\n      max-width: 520px;\n      width: 92%;\n    }\n    .modal h2 { margin-top: 0; }\n    .modal img { max-width: 100%; height: auto; border-radius: 6px; }\n\n    \/* \u30ad\u30e3\u30e9\u30af\u30bf\u30fc\u8868\u793a *\/\n    #character-image {\n      max-width: 420px;\n      width: 100%;\n      height: auto;\n      margin: 20px auto;\n      display: block;\n      border-radius: 8px;\n      border: 1px solid #ddd;\n      background: #fff;\n    }\n\n    \/* \u30d0\u30c8\u30eb\u7528\u30b9\u30bf\u30a4\u30eb *\/\n    .battle-container {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 20px;\n    }\n    .enemy-card {\n      background-color: #fff;\n      border: 1px solid #ddd;\n      border-radius: 5px;\n      width: 250px;\n      padding: 16px;\n      text-align: center;\n    }\n\n    \/* \u30e1\u30c3\u30bb\u30fc\u30b8\u30ed\u30b0 *\/\n    .log {\n      background-color: #eef;\n      border: 1px solid #bbe;\n      border-radius: 5px;\n      padding: 10px;\n      max-height: 300px;\n      overflow-y: auto;\n      margin: 10px 0;\n      white-space: pre-wrap;\n    }\n\n    \/* \u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u30dc\u30bf\u30f3 *\/\n    #location-buttons button { margin-right: 10px; }\n\n    \/* \u30b9\u30ad\u30eb\u4e00\u89a7 *\/\n    .skill-list { list-style: none; padding: 0; }\n    .skill-list li { margin: 5px 0; }\n\n    \/* \u30af\u30a8\u30b9\u30c8\u30ed\u30b0 *\/\n    #quest-log-list { list-style: none; padding: 0; }\n    #quest-log-list li { margin: 4px 0; }\n\n    \/* \u5b9f\u7e3e\u4e00\u89a7 *\/\n    #achievement-list { list-style: none; padding: 0; }\n    #achievement-list li { margin: 5px 0; }\n\n    \/* ===============================\n       \u30a2\u30fc\u30c8\u30ae\u30e3\u30e9\u30ea\u30fc\n    =============================== *\/\n    .gallery-grid{\n      display:grid;\n      grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n      gap: 14px;\n    }\n    .gallery-item{\n      background:#fff;\n      border:1px solid #ddd;\n      border-radius:8px;\n      overflow:hidden;\n      box-shadow: 0px 2px 4px rgba(0,0,0,0.08);\n      display:flex;\n      flex-direction:column;\n    }\n    .gallery-item img{\n      width:100%;\n      height:auto;\n      display:block;\n      background:#fff;\n    }\n    .gallery-meta{\n      padding:10px;\n      display:flex;\n      align-items:center;\n      justify-content:space-between;\n      gap:8px;\n      flex-wrap:wrap;\n    }\n    .badge{\n      display:inline-block;\n      padding:4px 8px;\n      border-radius:999px;\n      font-size:12px;\n      background:#eef;\n      border:1px solid #bbe;\n      color:#223;\n    }\n    .badge.owned{\n      background:#e9ffe9;\n      border-color:#9fd49f;\n      color:#1c5a1c;\n    }\n    .badge.rarity-ur{\n      background:#fff2cc;\n      border-color:#f3d27a;\n      color:#6b4b00;\n    }\n    .badge.rarity-ssr{\n      background:#e8f0ff;\n      border-color:#9fb7ff;\n      color:#133a7a;\n    }\n    .gallery-actions{\n      display:flex;\n      gap:8px;\n      flex-wrap:wrap;\n      padding: 0 10px 12px;\n    }\n\n    \/* \u30ac\u30c1\u30e3UI *\/\n    .gacha-row{\n      display:flex;\n      align-items:center;\n      justify-content:space-between;\n      gap:10px;\n      flex-wrap:wrap;\n    }\n    .gacha-result{\n      margin-top:10px;\n      display:flex;\n      gap:10px;\n      flex-wrap:wrap;\n      align-items:flex-start;\n    }\n    .gacha-card{\n      width: 220px;\n      background:#fff;\n      border:1px solid #ddd;\n      border-radius:10px;\n      overflow:hidden;\n      box-shadow: 0px 2px 4px rgba(0,0,0,0.08);\n    }\n    .gacha-card img{ width:100%; display:block; }\n    .gacha-card .p{ padding:10px; }\n  &lt;\/style>\n&lt;\/head>\n\n&lt;body>\n  &lt;!-- ===============================\n       \u30d8\u30c3\u30c0\u30fc\n  =============================== -->\n  &lt;header>\n    &lt;h1>Aran Red Fantasy - Ultimate&lt;\/h1>\n  &lt;\/header>\n\n  &lt;!-- ===============================\n       \u30ca\u30d3\u30b2\u30fc\u30b7\u30e7\u30f3\n  =============================== -->\n  &lt;nav>\n    &lt;div class=\"container\">\n      &lt;a href=\"#\" id=\"home-link\" onclick=\"showPage('home')\">Home&lt;\/a>\n      &lt;a href=\"#\" id=\"quests-link\" onclick=\"showPage('quests')\">Quests&lt;\/a>\n      &lt;a href=\"#\" id=\"items-link\" onclick=\"showPage('items')\">Items&lt;\/a>\n      &lt;a href=\"#\" id=\"friends-link\" onclick=\"showPage('friends')\">Companions&lt;\/a>\n      &lt;a href=\"#\" id=\"character-link\" onclick=\"showPage('character')\">Character&lt;\/a>\n      &lt;a href=\"#\" id=\"art-link\" onclick=\"showPage('art')\">Art Gallery&lt;\/a>\n      &lt;a href=\"#\" id=\"battle-link\" onclick=\"showPage('battle')\">Battle&lt;\/a>\n      &lt;a href=\"#\" id=\"store-link\" onclick=\"showPage('store')\">Store&lt;\/a>\n      &lt;a href=\"#\" id=\"craft-link\" onclick=\"showPage('craft')\">Craft&lt;\/a>\n      &lt;a href=\"#\" id=\"skills-link\" onclick=\"showPage('skills')\">Skills&lt;\/a>\n      &lt;a href=\"#\" id=\"questlog-link\" onclick=\"showPage('questlog')\">QuestLog&lt;\/a>\n      &lt;a href=\"#\" id=\"achievements-link\" onclick=\"showPage('achievements')\">Achievements&lt;\/a>\n    &lt;\/div>\n  &lt;\/nav>\n\n  &lt;!-- ===============================\n       \u30e1\u30a4\u30f3\u30b3\u30f3\u30c6\u30f3\u30c4\n  =============================== -->\n  &lt;main>\n    &lt;div class=\"container\" id=\"content\">\n\n      &lt;!-- ===============================\n           Home\n      =============================== -->\n      &lt;div id=\"home\">\n        &lt;h2>Welcome to Aran Red Fantasy!&lt;\/h2>\n        &lt;p>Explore the world, complete quests, craft items, recruit companions, and unlock achievements!&lt;\/p>\n\n        &lt;div id=\"home-message\">&lt;\/div>\n\n        &lt;button class=\"button\" onclick=\"showLoginModal()\">Log In \/ Change User&lt;\/button>\n        &lt;button class=\"button\" onclick=\"logout()\">Logout (Reset All Data)&lt;\/button>\n        &lt;br\/>&lt;br\/>\n\n        &lt;!-- BGM\uff08\u7d99\u7d9a\u518d\u751f\u5bfe\u5fdc\uff1aaudio\u306f\u30da\u30fc\u30b8\u5916\u306b\u7f6e\u304f\uff09 -->\n        &lt;div class=\"card\">\n          &lt;h3>BGM&lt;\/h3>\n          &lt;p class=\"muted\">\u203b \u6700\u521d\u306e1\u56de\u3060\u3051\u300cEnable BGM\u300d\u3092\u62bc\u3057\u3066\u304f\u3060\u3055\u3044\uff08\u30d6\u30e9\u30a6\u30b6\u306e\u81ea\u52d5\u518d\u751f\u30d6\u30ed\u30c3\u30af\u5bfe\u7b56\uff09\u3002\u4ee5\u5f8c\u306f\u30da\u30fc\u30b8\u5207\u66ff\u3057\u3066\u3082\u7d99\u7d9a\u3057\u307e\u3059\u3002&lt;\/p>\n          &lt;button class=\"button\" id=\"bgm-enable-btn\" onclick=\"enableBGM()\">Enable BGM (First Click)&lt;\/button>\n          &lt;button class=\"button\" onclick=\"toggleMusic()\">Toggle Music&lt;\/button>\n          &lt;div class=\"muted\" id=\"bgm-status\">Status: Off&lt;\/div>\n        &lt;\/div>\n\n        &lt;!-- \u2605\u30ac\u30c1\u30e3\uff08SSR\/UR\u8ffd\u52a0\uff09 -->\n        &lt;div class=\"card\">\n          &lt;h3>Art Gacha&lt;\/h3>\n          &lt;div class=\"gacha-row\">\n            &lt;div class=\"muted\">\n              Cost: &lt;strong>10 Gold&lt;\/strong> \/ pull&lt;br\/>\n              SSR\/UR\u304c\u51fa\u307e\u3059\u3002\u5f15\u3044\u305f\u30a2\u30fc\u30c8\u306f\u81ea\u52d5\u3067\u6240\u6301\u306b\u306a\u308a\u3001\u30a2\u30fc\u30c8\u30ae\u30e3\u30e9\u30ea\u30fc\u306b\u8ffd\u52a0\u3055\u308c\u307e\u3059\u3002\n            &lt;\/div>\n            &lt;div>\n              &lt;button class=\"button\" onclick=\"pullArtGacha(1)\">Pull x1&lt;\/button>\n              &lt;button class=\"button\" onclick=\"pullArtGacha(10)\">Pull x10&lt;\/button>\n            &lt;\/div>\n          &lt;\/div>\n          &lt;div class=\"muted\" id=\"gacha-status\">\u2014&lt;\/div>\n          &lt;div class=\"gacha-result\" id=\"gacha-result\">&lt;\/div>\n        &lt;\/div>\n\n        &lt;!-- \u30e9\u30f3\u30c0\u30e0\u30a4\u30d9\u30f3\u30c8\/\u5929\u5019\u8868\u793a -->\n        &lt;div id=\"weather-display\">&lt;\/div>\n        &lt;button class=\"button\" onclick=\"triggerRandomEvent()\">Check Random Event&lt;\/button>\n\n        &lt;!-- \u663c\u591c\u30b5\u30a4\u30af\u30eb -->\n        &lt;div id=\"day-night-display\">&lt;\/div>\n        &lt;button class=\"button\" onclick=\"advanceTime()\">Pass Time (+6h)&lt;\/button>\n\n        &lt;!-- \u5bbf\u5c4b\u3067\u4f11\u606f -->\n        &lt;h3>Inn&lt;\/h3>\n        &lt;button class=\"button\" onclick=\"restAtInn()\">Rest at Inn (10 Gold)&lt;\/button>\n\n        &lt;!-- \u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u79fb\u52d5 -->\n        &lt;div id=\"location-section\" class=\"card\">\n          &lt;h3>Locations&lt;\/h3>\n          &lt;div id=\"location-buttons\">\n            &lt;button class=\"button\" onclick=\"moveLocation('Town')\">Move to Town&lt;\/button>\n            &lt;button class=\"button\" onclick=\"moveLocation('Forest')\">Move to Forest&lt;\/button>\n            &lt;button class=\"button\" onclick=\"moveLocation('Dungeon')\">Move to Dungeon&lt;\/button>\n            &lt;button class=\"button\" onclick=\"moveLocation('Mountain')\">Move to Mountain&lt;\/button>\n          &lt;\/div>\n          &lt;p>Current Location: &lt;span id=\"current-location\">Town&lt;\/span>&lt;\/p>\n          &lt;div class=\"log\" id=\"location-log\">&lt;\/div>\n        &lt;\/div>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Quests\n      =============================== -->\n      &lt;div id=\"quests\" style=\"display: none;\">\n        &lt;h2>Quests&lt;\/h2>\n\n        &lt;h3>Main Quests&lt;\/h3>\n        &lt;div class=\"card\" id=\"dragon-quest\">\n          &lt;h4>Defeat the Dragon&lt;\/h4>\n          &lt;p>A fierce dragon has appeared near the village! Defeat it to save the locals.&lt;\/p>\n          &lt;div class=\"progress-bar\">\n            &lt;div class=\"progress\" id=\"dragon-progress\">&lt;\/div>\n          &lt;\/div>\n          &lt;p>Reward: 100 Gold, 100 XP, Dragon Scale&lt;\/p>\n          &lt;button class=\"button\" onclick=\"startQuest('dragon')\">Start Quest&lt;\/button>\n        &lt;\/div>\n\n        &lt;div class=\"card\" id=\"final-quest\" style=\"display: none;\">\n          &lt;h4>The Ancient Evil (Final)&lt;\/h4>\n          &lt;p>The final threat emerges after you've proven your strength! Vanquish it!&lt;\/p>\n          &lt;div class=\"progress-bar\">\n            &lt;div class=\"progress\" id=\"final-progress\">&lt;\/div>\n          &lt;\/div>\n          &lt;p>Reward: 200 Gold, 200 XP, Legendary Relic&lt;\/p>\n          &lt;button class=\"button\" onclick=\"startQuest('final')\">Start Quest&lt;\/button>\n        &lt;\/div>\n\n        &lt;h3>Side Quests&lt;\/h3>\n        &lt;div class=\"card\" id=\"crystal-quest\">\n          &lt;h4>Collect Magic Crystals&lt;\/h4>\n          &lt;p>Gather magical crystals scattered around the forest. Watch out for monsters!&lt;\/p>\n          &lt;div class=\"progress-bar\">\n            &lt;div class=\"progress\" id=\"crystal-progress\">&lt;\/div>\n          &lt;\/div>\n          &lt;p>Reward: 50 Gold, 50 XP, Magic Crystal&lt;\/p>\n          &lt;button class=\"button\" onclick=\"startQuest('crystal')\">Start Quest&lt;\/button>\n        &lt;\/div>\n\n        &lt;div class=\"card\" id=\"orc-quest\">\n          &lt;h4>Eliminate the Orc Bandits&lt;\/h4>\n          &lt;p>A group of orc bandits is attacking travelers. Defeat them to restore peace!&lt;\/p>\n          &lt;div class=\"progress-bar\">\n            &lt;div class=\"progress\" id=\"orc-progress\">&lt;\/div>\n          &lt;\/div>\n          &lt;p>Reward: 80 Gold, 70 XP, Orc Tusk&lt;\/p>\n          &lt;button class=\"button\" onclick=\"startQuest('orc')\">Start Quest&lt;\/button>\n        &lt;\/div>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Items\n      =============================== -->\n      &lt;div id=\"items\" style=\"display: none;\">\n        &lt;h2>Inventory&lt;\/h2>\n        &lt;p>Click an item to use\/equip\/sell it (if applicable).&lt;\/p>\n        &lt;div id=\"inventory\">&lt;\/div>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Companions\n      =============================== -->\n      &lt;div id=\"friends\" style=\"display: none;\">\n        &lt;h2>Companions&lt;\/h2>\n        &lt;p>Hire companions who fight alongside you!&lt;\/p>\n        &lt;input type=\"text\" id=\"friendName\" placeholder=\"Companion name\" \/>\n        &lt;button class=\"button\" onclick=\"hireCompanion()\">Hire Companion&lt;\/button>\n\n        &lt;h3>Your Companions&lt;\/h3>\n        &lt;ul id=\"companion-list\">&lt;\/ul>\n        &lt;p class=\"muted\">* Each companion has its own level, HP, and Attack. They also gain XP when you do.&lt;\/p>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Character\n      =============================== -->\n      &lt;div id=\"character\" style=\"display: none;\">\n        &lt;h2>Character&lt;\/h2>\n        &lt;img src=\"a.png\" alt=\"Character\" id=\"character-image\"\/>\n        &lt;p>Name: &lt;span id=\"character-name\">&lt;\/span>&lt;\/p>\n        &lt;p>Level: &lt;span id=\"character-level\">&lt;\/span>&lt;\/p>\n        &lt;p>HP: &lt;span id=\"character-hp\">&lt;\/span> \/ &lt;span id=\"character-maxhp\">&lt;\/span>&lt;\/p>\n        &lt;p>XP: &lt;span id=\"character-xp\">&lt;\/span> \/ &lt;span id=\"character-nextLevelXp\">&lt;\/span>&lt;\/p>\n        &lt;p>Gold: &lt;span id=\"character-gold\">&lt;\/span>&lt;\/p>\n        &lt;p>Attack: &lt;span id=\"character-attack\">&lt;\/span>&lt;\/p>\n        &lt;p>Defense: &lt;span id=\"character-defense\">&lt;\/span>&lt;\/p>\n        &lt;p>Skill Points: &lt;span id=\"character-skillpoints\">&lt;\/span>&lt;\/p>\n        &lt;p>Active Buffs\/Debuffs: &lt;span id=\"character-buffs\">None&lt;\/span>&lt;\/p>\n        &lt;p>Special Items: &lt;span id=\"character-items\">None&lt;\/span>&lt;\/p>\n        &lt;p class=\"muted\">Portrait changes when you buy or pull an art (Store \/ Gacha).&lt;\/p>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Art Gallery\n      =============================== -->\n      &lt;div id=\"art\" style=\"display: none;\">\n        &lt;h2>\u30a2\u30fc\u30c8\u30ae\u30e3\u30e9\u30ea\u30fc&lt;\/h2>\n        &lt;p class=\"muted\">\u6240\u6301\u6e08\u307f\u306e\u30a2\u30fc\u30c8\u306f\u300cSet as Character Art\u300d\u3067\u30ad\u30e3\u30e9\u30af\u30bf\u30fc\u753b\u50cf\u306b\u8a2d\u5b9a\u3067\u304d\u307e\u3059\u3002\u30ac\u30c1\u30e3\u3067\u3082\u5165\u624b\u3067\u304d\u307e\u3059\u3002&lt;\/p>\n\n        &lt;div class=\"card\">\n          &lt;h3>Your Art Collection&lt;\/h3>\n          &lt;div id=\"art-collection-summary\" class=\"muted\">&lt;\/div>\n        &lt;\/div>\n\n        &lt;div class=\"gallery-grid\" id=\"art-gallery-grid\">&lt;\/div>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Battle\n      =============================== -->\n      &lt;div id=\"battle\" style=\"display: none;\">\n        &lt;h2>Battle Arena&lt;\/h2>\n        &lt;p>Choose an enemy to fight or wait for random encounters in the wild!&lt;\/p>\n\n        &lt;div class=\"battle-container\">\n          &lt;div class=\"enemy-card\">\n            &lt;h3>Slime&lt;\/h3>\n            &lt;p>HP: 30&lt;\/p>\n            &lt;p>Attack: 1-3&lt;\/p>\n            &lt;p>Reward: 10 Gold, 10 XP&lt;\/p>\n            &lt;button class=\"button\" onclick=\"startBattle('slime')\">Fight Slime&lt;\/button>\n          &lt;\/div>\n\n          &lt;div class=\"enemy-card\">\n            &lt;h3>Goblin&lt;\/h3>\n            &lt;p>HP: 50&lt;\/p>\n            &lt;p>Attack: 2-5&lt;\/p>\n            &lt;p>Reward: 20 Gold, 20 XP&lt;\/p>\n            &lt;button class=\"button\" onclick=\"startBattle('goblin')\">Fight Goblin&lt;\/button>\n          &lt;\/div>\n\n          &lt;div class=\"enemy-card\">\n            &lt;h3>Orc Warrior&lt;\/h3>\n            &lt;p>HP: 80&lt;\/p>\n            &lt;p>Attack: 5-8&lt;\/p>\n            &lt;p>Reward: 40 Gold, 40 XP&lt;\/p>\n            &lt;button class=\"button\" onclick=\"startBattle('orcEnemy')\">Fight Orc&lt;\/button>\n          &lt;\/div>\n        &lt;\/div>\n\n        &lt;div class=\"log\" id=\"battle-log\">&lt;\/div>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Store\n      =============================== -->\n      &lt;div id=\"store\" style=\"display: none;\">\n        &lt;h2>Store&lt;\/h2>\n        &lt;p>Use your gold to purchase or sell items!&lt;\/p>\n\n        &lt;div class=\"card\">\n          &lt;h3>Buy Items&lt;\/h3>\n          &lt;div>\n            &lt;h4>Minor Health Potion (20 Gold)&lt;\/h4>\n            &lt;button class=\"button\" onclick=\"buyItem('Minor Health Potion')\">Buy&lt;\/button>\n          &lt;\/div>\n          &lt;div>\n            &lt;h4>Major Health Potion (50 Gold)&lt;\/h4>\n            &lt;button class=\"button\" onclick=\"buyItem('Major Health Potion')\">Buy&lt;\/button>\n          &lt;\/div>\n          &lt;div>\n            &lt;h4>Iron Sword (80 Gold)&lt;\/h4>\n            &lt;button class=\"button\" onclick=\"buyItem('Iron Sword')\">Buy&lt;\/button>\n          &lt;\/div>\n          &lt;div>\n            &lt;h4>Steel Armor (100 Gold)&lt;\/h4>\n            &lt;button class=\"button\" onclick=\"buyItem('Steel Armor')\">Buy&lt;\/button>\n          &lt;\/div>\n          &lt;div>\n            &lt;h4>Lucky Ring (120 Gold)&lt;\/h4>\n            &lt;button class=\"button\" onclick=\"buyItem('Lucky Ring')\">Buy&lt;\/button>\n          &lt;\/div>\n        &lt;\/div>\n\n        &lt;!-- \u30a2\u30fc\u30c8\u8cfc\u5165\uff1a\u8cfc\u5165\u3059\u308b\u3068\u30ad\u30e3\u30e9\u30af\u30bf\u30fc\u7d75\u304c\u5909\u308f\u308b -->\n        &lt;div class=\"card\">\n          &lt;h3>Art Shop\uff08\u8cfc\u5165\u3067\u30ad\u30e3\u30e9\u30af\u30bf\u30fc\u753b\u50cf\u304c\u5909\u308f\u308b\uff09&lt;\/h3>\n          &lt;p class=\"muted\">Buy an art \u2192 it becomes \u201cOwned\u201d and you can set it anytime. (Gacha also adds Owned.)&lt;\/p>\n          &lt;div id=\"art-shop-list\">&lt;\/div>\n        &lt;\/div>\n\n        &lt;div class=\"card\">\n          &lt;h3>Sell Items&lt;\/h3>\n          &lt;p>Click an item in your inventory to sell it, if possible.&lt;\/p>\n          &lt;p class=\"muted\">(You can't sell special quest items or currently equipped gear.)&lt;\/p>\n        &lt;\/div>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Craft\n      =============================== -->\n      &lt;div id=\"craft\" style=\"display: none;\">\n        &lt;h2>Item Crafting&lt;\/h2>\n        &lt;p>Combine items to create something new!&lt;\/p>\n        &lt;div class=\"card\">\n          &lt;h3>Example Recipes&lt;\/h3>\n          &lt;ul>\n            &lt;li>Dragon Scale + Orc Tusk => Dragon Tusk Lance (Weapon)&lt;\/li>\n            &lt;li>Magic Crystal + Magic Crystal => Greater Crystal (Special)&lt;\/li>\n          &lt;\/ul>\n          &lt;p>Select any two items from your inventory to craft (if a valid recipe exists).&lt;\/p>\n        &lt;\/div>\n        &lt;p>Currently Selected: &lt;span id=\"craft-selection\">None&lt;\/span>&lt;\/p>\n        &lt;button class=\"button\" id=\"craft-button\" onclick=\"attemptCraft()\" disabled>Craft&lt;\/button>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Skills\n      =============================== -->\n      &lt;div id=\"skills\" style=\"display: none;\">\n        &lt;h2>Skills&lt;\/h2>\n        &lt;p>Use skill points to learn or upgrade skills!&lt;\/p>\n        &lt;p>You have &lt;span id=\"skill-point-display\">&lt;\/span> skill points.&lt;\/p>\n        &lt;ul class=\"skill-list\" id=\"skill-list\">&lt;\/ul>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           QuestLog\n      =============================== -->\n      &lt;div id=\"questlog\" style=\"display: none;\">\n        &lt;h2>Quest Log&lt;\/h2>\n        &lt;ul id=\"quest-log-list\">&lt;\/ul>\n      &lt;\/div>\n\n      &lt;!-- ===============================\n           Achievements\n      =============================== -->\n      &lt;div id=\"achievements\" style=\"display: none;\">\n        &lt;h2>Achievements&lt;\/h2>\n        &lt;ul id=\"achievement-list\">&lt;\/ul>\n      &lt;\/div>\n\n    &lt;\/div>\n  &lt;\/main>\n\n  &lt;!-- ===============================\n       \u30d5\u30c3\u30bf\u30fc\n  =============================== -->\n  &lt;footer>\n    &lt;div class=\"container\">\n      &amp;copy; 2025 Aran Red Fantasy\n    &lt;\/div>\n  &lt;\/footer>\n\n  &lt;!-- ===============================\n       \u30ed\u30b0\u30a4\u30f3\u30e2\u30fc\u30c0\u30eb\n  =============================== -->\n  &lt;div class=\"modal-bg\" id=\"login-modal-bg\" style=\"display: none;\">\n    &lt;div class=\"modal\">\n      &lt;h2>Enter Your Name&lt;\/h2>\n      &lt;input type=\"text\" id=\"loginName\" placeholder=\"Your name\" \/>\n      &lt;br\/>&lt;br\/>\n      &lt;button class=\"button\" onclick=\"confirmLogin()\">Login&lt;\/button>\n      &lt;button class=\"button\" onclick=\"closeLoginModal()\">Cancel&lt;\/button>\n    &lt;\/div>\n  &lt;\/div>\n\n  &lt;!-- ===============================\n       \u30a2\u30a4\u30c6\u30e0\u4f7f\u7528\u30e2\u30fc\u30c0\u30eb\n  =============================== -->\n  &lt;div class=\"modal-bg\" id=\"item-modal-bg\" style=\"display: none;\">\n    &lt;div class=\"modal\">\n      &lt;h2 id=\"item-modal-title\">Use\/Equip Item&lt;\/h2>\n      &lt;p id=\"item-modal-description\">&lt;\/p>\n      &lt;button class=\"button\" onclick=\"confirmItemUse()\">Use\/Equip&lt;\/button>\n      &lt;button class=\"button\" onclick=\"closeItemModal()\">Cancel&lt;\/button>\n    &lt;\/div>\n  &lt;\/div>\n\n  &lt;!-- ===============================\n       \u30a2\u30fc\u30c8\u30d7\u30ec\u30d3\u30e5\u30fc\u30e2\u30fc\u30c0\u30eb\n  =============================== -->\n  &lt;div class=\"modal-bg\" id=\"art-modal-bg\" style=\"display: none;\">\n    &lt;div class=\"modal\">\n      &lt;h2 id=\"art-modal-title\">Art Preview&lt;\/h2>\n      &lt;img id=\"art-modal-img\" alt=\"Art Preview\" \/>\n      &lt;p class=\"muted\" id=\"art-modal-desc\">&lt;\/p>\n      &lt;div style=\"margin-top:10px;\">\n        &lt;button class=\"button\" id=\"art-modal-set-btn\" onclick=\"confirmSetPortrait()\">Set as Character Art&lt;\/button>\n        &lt;button class=\"button\" onclick=\"closeArtModal()\">Close&lt;\/button>\n      &lt;\/div>\n    &lt;\/div>\n  &lt;\/div>\n\n  &lt;!-- ===============================\n       BGM\u672c\u4f53\uff08\u7d99\u7d9a\u518d\u751f\u306e\u305f\u3081\u30da\u30fc\u30b8\u5207\u66ff\u306e\u5916\u306b\u7f6e\u304f\uff09\n  =============================== -->\n  &lt;audio id=\"bgm\" loop preload=\"auto\" playsinline>\n    &lt;source src=\"http:\/\/tyosuke20xx.com\/fjordnosundakaze.mp3\" type=\"audio\/mpeg\">\n  &lt;\/audio>\n\n  &lt;!-- ===============================\n       JavaScript\n  =============================== -->\n  &lt;script>\n    \/\/ -------------------------------------------\n    \/\/ \u30da\u30fc\u30b8\u5207\u308a\u66ff\u3048\n    \/\/ -------------------------------------------\n    function showPage(page) {\n      const pages = &#91;\n        \"home\", \"quests\", \"items\", \"friends\", \"character\",\n        \"art\",\n        \"battle\", \"store\", \"craft\", \"skills\", \"questlog\", \"achievements\"\n      ];\n      pages.forEach(p => {\n        const pageElement = document.getElementById(p);\n        const linkElement = document.getElementById(p + '-link');\n        if (!pageElement) return;\n\n        if (p === page) {\n          pageElement.style.display = \"block\";\n          if (linkElement) linkElement.classList.add(\"active\");\n        } else {\n          pageElement.style.display = \"none\";\n          if (linkElement) linkElement.classList.remove(\"active\");\n        }\n      });\n\n      if (page === \"skills\") refreshSkillList();\n      if (page === \"questlog\") updateQuestLog();\n      if (page === \"achievements\") updateAchievementList();\n      if (page === \"art\") renderArtGallery();\n      if (page === \"store\") renderArtShop();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30ed\u30fc\u30ab\u30eb\u30b9\u30c8\u30ec\u30fc\u30b8\u30ad\u30fc\n    \/\/ -------------------------------------------\n    const LS_KEY_USER        = \"ARF_Username_Ultimate\";\n    const LS_KEY_CHARACTER   = \"ARF_Character_Ultimate\";\n    const LS_KEY_INVENTORY   = \"ARF_Inventory_Ultimate\";\n    const LS_KEY_COMPANIONS  = \"ARF_Companions_Ultimate\";\n    const LS_KEY_QUESTS      = \"ARF_Quests_Ultimate\";\n    const LS_KEY_SKILLS      = \"ARF_Skills_Ultimate\";\n    const LS_KEY_DAYTIME     = \"ARF_Daytime_Ultimate\";\n    const LS_KEY_WEATHER     = \"ARF_Weather_Ultimate\";\n    const LS_KEY_ACHIEVEMENT = \"ARF_Achievement_Ultimate\";\n\n    \/\/ BGM\u72b6\u614b\n    const LS_KEY_BGM = \"ARF_BGM_STATE_Ultimate\";\n\n    \/\/ -------------------------------------------\n    \/\/ \u2605\u30a2\u30fc\u30c8\u5b9a\u7fa9\uff08SSR1\u301cSSR3 + UR1\u301cUR10\uff09\n    \/\/ -------------------------------------------\n    const ART_LIST = &#91;\n      \/\/ SSR\uff08\u8ffd\u52a0\uff09\n      { key:\"SSR1\", name:\"SSR Art 1\", url:\"http:\/\/tyosuke20xx.com\/SSR1.png\", cost: 20, rarity:\"SSR\" },\n      { key:\"SSR2\", name:\"SSR Art 2\", url:\"http:\/\/tyosuke20xx.com\/SSR2.png\", cost: 20, rarity:\"SSR\" },\n      { key:\"SSR3\", name:\"SSR Art 3\", url:\"http:\/\/tyosuke20xx.com\/SSR3.png\", cost: 20, rarity:\"SSR\" },\n\n      \/\/ UR\n      { key:\"UR1\",  name:\"UR Art 1\",  url:\"http:\/\/tyosuke20xx.com\/UR1.png\",  cost: 30, rarity:\"UR\" },\n      { key:\"UR2\",  name:\"UR Art 2\",  url:\"http:\/\/tyosuke20xx.com\/UR2.png\",  cost: 30, rarity:\"UR\" },\n      { key:\"UR3\",  name:\"UR Art 3\",  url:\"http:\/\/tyosuke20xx.com\/UR3.png\",  cost: 30, rarity:\"UR\" },\n      { key:\"UR4\",  name:\"UR Art 4\",  url:\"http:\/\/tyosuke20xx.com\/UR4.png\",  cost: 30, rarity:\"UR\" },\n      { key:\"UR5\",  name:\"UR Art 5\",  url:\"http:\/\/tyosuke20xx.com\/UR5.png\",  cost: 30, rarity:\"UR\" },\n      { key:\"UR6\",  name:\"UR Art 6\",  url:\"http:\/\/tyosuke20xx.com\/UR6.png\",  cost: 30, rarity:\"UR\" },\n      { key:\"UR7\",  name:\"UR Art 7\",  url:\"http:\/\/tyosuke20xx.com\/UR7.png\",  cost: 30, rarity:\"UR\" },\n      { key:\"UR8\",  name:\"UR Art 8\",  url:\"http:\/\/tyosuke20xx.com\/UR8.png\",  cost: 30, rarity:\"UR\" },\n      { key:\"UR9\",  name:\"UR Art 9\",  url:\"http:\/\/tyosuke20xx.com\/UR9.png\",  cost: 30, rarity:\"UR\" },\n      { key:\"UR10\", name:\"UR Art 10\", url:\"http:\/\/tyosuke20xx.com\/UR10.png\", cost: 30, rarity:\"UR\" }\n    ];\n\n    \/\/ -------------------------------------------\n    \/\/ \u2605\u30ac\u30c1\u30e3\u8a2d\u5b9a\uff08SSR\/UR\u62bd\u9078\uff09\n    \/\/ -------------------------------------------\n    const GACHA_COST = 10;         \/\/ 1\u56de10G\n    const GACHA_RATE_UR = 10;      \/\/ UR 10%\n    const GACHA_RATE_SSR = 90;     \/\/ SSR 90%\uff08\u6b8b\u308a\uff09\n\n    \/\/ -------------------------------------------\n    \/\/ \u30ad\u30e3\u30e9\u30af\u30bf\u30fc\u60c5\u5831\n    \/\/ -------------------------------------------\n    let character = {\n      name: \"Adventurer\",\n      level: 1,\n      hp: 50,\n      maxHp: 50,\n      xp: 0,\n      nextLevelXp: 100,\n      gold: 0,\n      attack: 5,\n      defense: 2,\n      skillPoints: 0,\n      location: \"Town\",\n      specialItems: &#91;],\n      buffs: &#91;],\n\n      ownedArtKeys: &#91;],\n      portraitUrl: \"a.png\"\n    };\n\n    \/\/ -------------------------------------------\n    \/\/ \u30af\u30a8\u30b9\u30c8\u60c5\u5831\n    \/\/ -------------------------------------------\n    let mainQuests = {\n      dragon: {\n        name: \"Defeat the Dragon\",\n        progress: 0,\n        reward: { gold: 100, xp: 100, items: &#91;\"Dragon Scale\"] },\n        isRunning: false,\n        isCompleted: false,\n        unlockNext: \"final\",\n        locked: false\n      },\n      final: {\n        name: \"The Ancient Evil\",\n        progress: 0,\n        reward: { gold: 200, xp: 200, items: &#91;\"Legendary Relic\"] },\n        isRunning: false,\n        isCompleted: false,\n        unlockNext: null,\n        locked: true\n      }\n    };\n    let sideQuests = {\n      crystal: {\n        name: \"Collect Magic Crystals\",\n        progress: 0,\n        reward: { gold: 50, xp: 50, items: &#91;\"Magic Crystal\"] },\n        isRunning: false,\n        isCompleted: false,\n        unlockNext: null,\n        locked: false\n      },\n      orc: {\n        name: \"Eliminate the Orc Bandits\",\n        progress: 0,\n        reward: { gold: 80, xp: 70, items: &#91;\"Orc Tusk\"] },\n        isRunning: false,\n        isCompleted: false,\n        unlockNext: null,\n        locked: false\n      }\n    };\n\n    function getAllQuests() {\n      return { ...mainQuests, ...sideQuests };\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30a4\u30f3\u30d9\u30f3\u30c8\u30ea\n    \/\/ -------------------------------------------\n    let inventory = &#91;];\n\n    \/\/ -------------------------------------------\n    \/\/ \u4ef2\u9593\n    \/\/ -------------------------------------------\n    let companions = &#91;];\n\n    \/\/ -------------------------------------------\n    \/\/ \u30b9\u30ad\u30eb\n    \/\/ -------------------------------------------\n    let skills = {\n      Fireball: {\n        name: \"Fireball\",\n        level: 0,\n        maxLevel: 3,\n        cost: 1,\n        description: \"Deal extra magic damage in battle\"\n      },\n      Heal: {\n        name: \"Heal\",\n        level: 0,\n        maxLevel: 3,\n        cost: 1,\n        description: \"Restores some HP at the start of battle\"\n      }\n    };\n\n    \/\/ -------------------------------------------\n    \/\/ \u30d0\u30c8\u30eb\u7528\u30a8\u30cd\u30df\u30fc\n    \/\/ -------------------------------------------\n    const enemies = {\n      slime: {\n        name: \"Slime\",\n        hp: 30,\n        attackMin: 1,\n        attackMax: 3,\n        rewardGold: 10,\n        rewardXp: 10\n      },\n      goblin: {\n        name: \"Goblin\",\n        hp: 50,\n        attackMin: 2,\n        attackMax: 5,\n        rewardGold: 20,\n        rewardXp: 20\n      },\n      orcEnemy: {\n        name: \"Orc Warrior\",\n        hp: 80,\n        attackMin: 5,\n        attackMax: 8,\n        rewardGold: 40,\n        rewardXp: 40\n      }\n    };\n\n    \/\/ -------------------------------------------\n    \/\/ \u30b9\u30c8\u30a2\u30a2\u30a4\u30c6\u30e0\n    \/\/ -------------------------------------------\n    const storeItems = {\n      \"Minor Health Potion\": { name: \"Minor Health Potion\", type: \"potion\", heal: 20, cost: 20 },\n      \"Major Health Potion\": { name: \"Major Health Potion\", type: \"potion\", heal: 50, cost: 50 },\n      \"Iron Sword\": { name: \"Iron Sword\", type: \"weapon\", attack: 5, cost: 80, equipped: false },\n      \"Steel Armor\": { name: \"Steel Armor\", type: \"armor\", defense: 5, cost: 100, equipped: false },\n      \"Lucky Ring\": { name: \"Lucky Ring\", type: \"accessory\", attack: 1, defense: 1, cost: 120, equipped: false }\n    };\n\n    \/\/ -------------------------------------------\n    \/\/ \u30af\u30e9\u30d5\u30c8\u7528\u30ec\u30b7\u30d4\n    \/\/ -------------------------------------------\n    const craftRecipes = &#91;\n      {\n        components: &#91;\"Dragon Scale\", \"Orc Tusk\"].sort(),\n        result: { name: \"Dragon Tusk Lance\", type: \"weapon\", attack: 10, equipped: false }\n      },\n      {\n        components: &#91;\"Magic Crystal\", \"Magic Crystal\"].sort(),\n        result: { name: \"Greater Crystal\", type: \"special\" }\n      }\n    ];\n\n    \/\/ -------------------------------------------\n    \/\/ \u663c\u591c &amp; \u5929\u5019\n    \/\/ -------------------------------------------\n    let currentHour = 12;\n    let currentWeather = \"Sunny\";\n    const possibleWeathers = &#91;\"Sunny\",\"Rainy\",\"Storm\",\"Cloudy\"];\n\n    \/\/ -------------------------------------------\n    \/\/ \u5b9f\u7e3e\n    \/\/ -------------------------------------------\n    let achievements = {\n      firstKill: { name: \"First Blood\", description: \"Defeat your first enemy.\", isUnlocked: false },\n      level5:    { name: \"Rising Hero\", description: \"Reach Level 5.\", isUnlocked: false },\n      quest3:    { name: \"Quest Hunter\", description: \"Complete 3 Quests.\", isUnlocked: false }\n    };\n\n    \/\/ -------------------------------------------\n    \/\/ onload\n    \/\/ -------------------------------------------\n    window.onload = function() {\n      loadLocalData();\n\n      \/\/ BGM\uff08\u7d99\u7d9a\u518d\u751f &amp; \u72b6\u614b\u4fdd\u5b58\uff09\n      loadBgmState();\n      wireBgmAutoSave();\n      updateEnableBtn();\n      setBgmStatus(isMusicPlaying ? \"On (will resume)\" : \"Off\");\n      resumeBgmOnNextUserActionIfNeeded();\n\n      updateCharacterInfo();\n      updateQuestVisibility();\n      updateInventoryDisplay();\n      updateCompanionList();\n      updateDayNightDisplay();\n      updateWeatherDisplay();\n      updateAchievementList();\n      renderArtShop();\n      renderArtGallery();\n\n      showPage('home');\n      document.getElementById(\"current-location\").textContent = character.location;\n    };\n\n    \/\/ -------------------------------------------\n    \/\/ \u30ed\u30fc\u30ab\u30eb\u30b9\u30c8\u30ec\u30fc\u30b8: \u8aad\u8fbc\/\u4fdd\u5b58\/\u30ea\u30bb\u30c3\u30c8\n    \/\/ -------------------------------------------\n    function loadLocalData() {\n      let storedName = localStorage.getItem(LS_KEY_USER);\n      if (storedName) character.name = storedName;\n\n      let storedChar = localStorage.getItem(LS_KEY_CHARACTER);\n      if (storedChar) {\n        try {\n          const parsed = JSON.parse(storedChar);\n          character = { ...character, ...parsed };\n        } catch(e) {}\n      }\n\n      if (!Array.isArray(character.ownedArtKeys)) character.ownedArtKeys = &#91;];\n      if (!character.portraitUrl) character.portraitUrl = \"a.png\";\n\n      let storedInv = localStorage.getItem(LS_KEY_INVENTORY);\n      if (storedInv) { try { inventory = JSON.parse(storedInv); } catch(e) {} }\n\n      let storedComp = localStorage.getItem(LS_KEY_COMPANIONS);\n      if (storedComp) { try { companions = JSON.parse(storedComp); } catch(e) {} }\n\n      let storedMQ = localStorage.getItem(LS_KEY_QUESTS+\"_main\");\n      if (storedMQ) { try { mainQuests = JSON.parse(storedMQ); } catch(e) {} }\n\n      let storedSQ = localStorage.getItem(LS_KEY_QUESTS+\"_side\");\n      if (storedSQ) { try { sideQuests = JSON.parse(storedSQ); } catch(e) {} }\n\n      let storedSkills = localStorage.getItem(LS_KEY_SKILLS);\n      if (storedSkills) { try { skills = JSON.parse(storedSkills); } catch(e) {} }\n\n      let storedHour = localStorage.getItem(LS_KEY_DAYTIME+\"_hour\");\n      if (storedHour) currentHour = parseInt(storedHour, 10);\n\n      let storedWeather = localStorage.getItem(LS_KEY_WEATHER);\n      if (storedWeather) currentWeather = storedWeather;\n\n      let storedAchv = localStorage.getItem(LS_KEY_ACHIEVEMENT);\n      if (storedAchv) { try { achievements = JSON.parse(storedAchv); } catch(e) {} }\n    }\n\n    function saveLocalData() {\n      localStorage.setItem(LS_KEY_USER, character.name);\n      localStorage.setItem(LS_KEY_CHARACTER, JSON.stringify(character));\n      localStorage.setItem(LS_KEY_INVENTORY, JSON.stringify(inventory));\n      localStorage.setItem(LS_KEY_COMPANIONS, JSON.stringify(companions));\n      localStorage.setItem(LS_KEY_QUESTS+\"_main\", JSON.stringify(mainQuests));\n      localStorage.setItem(LS_KEY_QUESTS+\"_side\", JSON.stringify(sideQuests));\n      localStorage.setItem(LS_KEY_SKILLS, JSON.stringify(skills));\n      localStorage.setItem(LS_KEY_DAYTIME+\"_hour\", currentHour.toString());\n      localStorage.setItem(LS_KEY_WEATHER, currentWeather);\n      localStorage.setItem(LS_KEY_ACHIEVEMENT, JSON.stringify(achievements));\n    }\n\n    function logout() {\n      if (!confirm(\"All data will be cleared. Are you sure?\")) return;\n      localStorage.clear();\n      location.reload();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30ed\u30b0\u30a4\u30f3\u30e2\u30fc\u30c0\u30eb\n    \/\/ -------------------------------------------\n    function showLoginModal() {\n      document.getElementById(\"login-modal-bg\").style.display = \"flex\";\n    }\n    function closeLoginModal() {\n      document.getElementById(\"login-modal-bg\").style.display = \"none\";\n    }\n    function confirmLogin() {\n      const inputName = document.getElementById(\"loginName\").value.trim();\n      if (inputName) {\n        character.name = inputName;\n        saveLocalData();\n        updateCharacterInfo();\n      }\n      closeLoginModal();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30ad\u30e3\u30e9\u30af\u30bf\u30fc\u60c5\u5831\u8868\u793a\u66f4\u65b0\n    \/\/ -------------------------------------------\n    function updateCharacterInfo() {\n      document.getElementById(\"character-name\").textContent = character.name;\n      document.getElementById(\"character-level\").textContent = character.level;\n      document.getElementById(\"character-hp\").textContent = character.hp;\n      document.getElementById(\"character-maxhp\").textContent = character.maxHp;\n      document.getElementById(\"character-xp\").textContent = character.xp;\n      document.getElementById(\"character-nextLevelXp\").textContent = character.nextLevelXp;\n      document.getElementById(\"character-gold\").textContent = character.gold;\n      document.getElementById(\"character-attack\").textContent = character.attack;\n      document.getElementById(\"character-defense\").textContent = character.defense;\n      document.getElementById(\"character-skillpoints\").textContent = character.skillPoints;\n\n      const img = document.getElementById(\"character-image\");\n      if (img) img.src = character.portraitUrl || \"a.png\";\n\n      if (character.specialItems.length > 0) {\n        document.getElementById(\"character-items\").textContent = character.specialItems.join(\", \");\n      } else {\n        document.getElementById(\"character-items\").textContent = \"None\";\n      }\n\n      if (character.buffs.length > 0) {\n        document.getElementById(\"character-buffs\").textContent = character.buffs.map(b => b.name).join(\", \");\n      } else {\n        document.getElementById(\"character-buffs\").textContent = \"None\";\n      }\n\n      saveLocalData();\n      checkAchievements();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30ec\u30d9\u30eb\u30a2\u30c3\u30d7\n    \/\/ -------------------------------------------\n    function addXp(amount) {\n      character.xp += amount;\n      while (character.xp >= character.nextLevelXp) {\n        character.level++;\n        character.xp -= character.nextLevelXp;\n        character.nextLevelXp = character.level * 100;\n        character.maxHp += 20;\n        character.hp = character.maxHp;\n        character.attack += 1;\n        character.defense += 1;\n        character.skillPoints += 1;\n        showHomeMessage(`Level up! Now Level ${character.level} (+1 Skill Point).`);\n\n        for (let c of companions) {\n          c.level++;\n          c.hp = c.maxHp;\n          c.attack++;\n        }\n      }\n      updateCharacterInfo();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30d0\u30d5\/\u30c7\u30d0\u30d5\n    \/\/ -------------------------------------------\n    function addBuff(buffObj) {\n      character.buffs.push(buffObj);\n      updateCharacterInfo();\n    }\n\n    function processBuffsEachTurn(logElm) {\n      for (let i = character.buffs.length - 1; i >= 0; i--) {\n        const b = character.buffs&#91;i];\n        if (b.effectType === \"dot\") {\n          character.hp -= b.effectValue;\n          if (character.hp &lt; 0) character.hp = 0;\n          logMessage(logElm, `&#91;${b.name}] You take ${b.effectValue} damage! (HP: ${character.hp})`);\n        }\n        b.turns--;\n        if (b.turns &lt;= 0) {\n          logMessage(logElm, `&#91;${b.name}] effect ended.`);\n          character.buffs.splice(i, 1);\n        }\n      }\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30db\u30fc\u30e0\u30e1\u30c3\u30bb\u30fc\u30b8\n    \/\/ -------------------------------------------\n    function showHomeMessage(msg) {\n      const homeMessage = document.getElementById('home-message');\n      homeMessage.innerHTML = `&lt;div class=\"message\">${msg}&lt;\/div>`;\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u663c\u591c\n    \/\/ -------------------------------------------\n    function updateDayNightDisplay() {\n      let dnElm = document.getElementById(\"day-night-display\");\n      let hourStr = (currentHour &lt; 10) ? \"0\"+currentHour : currentHour;\n      let isNight = (currentHour >= 18 || currentHour &lt; 6);\n      let dayNight = isNight ? \"Night\" : \"Day\";\n      dnElm.innerHTML = `&lt;p>Time: ${hourStr}:00 (${dayNight})&lt;\/p>`;\n    }\n    function advanceTime() {\n      currentHour += 6;\n      if (currentHour >= 24) currentHour -= 24;\n      saveLocalData();\n      updateDayNightDisplay();\n      showHomeMessage(\"Time passes by...\");\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u5929\u5019\n    \/\/ -------------------------------------------\n    function updateWeatherDisplay() {\n      const wElm = document.getElementById(\"weather-display\");\n      wElm.innerHTML = `&lt;p>Weather: ${currentWeather}&lt;\/p>`;\n    }\n    function changeWeatherRandom() {\n      currentWeather = possibleWeathers&#91;Math.floor(Math.random() * possibleWeathers.length)];\n      updateWeatherDisplay();\n      saveLocalData();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30e9\u30f3\u30c0\u30e0\u30a4\u30d9\u30f3\u30c8\n    \/\/ -------------------------------------------\n    function triggerRandomEvent() {\n      const randomRoll = Math.random();\n      let msg = \"\";\n      if (randomRoll &lt; 0.2) {\n        msg = \"A traveling merchant appears, offering rare goods (not yet implemented).\";\n      } else if (randomRoll &lt; 0.4) {\n        changeWeatherRandom();\n        msg = `The weather suddenly changes to ${currentWeather}!`;\n      } else if (randomRoll &lt; 0.6) {\n        addBuff({ name: 'Poison', turns: 3, effectType: 'dot', effectValue: 3 });\n        msg = \"You stepped on a poisonous trap! You are now poisoned.\";\n      } else {\n        msg = \"Nothing special happens.\";\n      }\n      showHomeMessage(msg);\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u5bbf\u5c4b\n    \/\/ -------------------------------------------\n    function restAtInn() {\n      if (character.gold &lt; 10) {\n        showHomeMessage(\"Not enough gold to rest at the inn!\");\n        return;\n      }\n      character.gold -= 10;\n      character.hp = character.maxHp;\n      for (let c of companions) c.hp = c.maxHp;\n      showHomeMessage(\"You and your companions rest at the inn and recover full HP.\");\n      updateCharacterInfo();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u79fb\u52d5 + \u30e9\u30f3\u30c0\u30e0\u30a8\u30f3\u30ab\u30a6\u30f3\u30c8\n    \/\/ -------------------------------------------\n    function moveLocation(newLocation) {\n      character.location = newLocation;\n      document.getElementById(\"current-location\").textContent = newLocation;\n      saveLocalData();\n\n      const logElm = document.getElementById('location-log');\n      logElm.textContent = `You moved to ${newLocation}.`;\n\n      let encounterChance = 0;\n      if (newLocation === \"Town\") encounterChance = 0;\n      else if (newLocation === \"Forest\") encounterChance = 40;\n      else if (newLocation === \"Dungeon\") encounterChance = 70;\n      else if (newLocation === \"Mountain\") encounterChance = 50;\n\n      const roll = Math.random() * 100;\n      if (roll &lt; encounterChance) {\n        const enemyKeys = Object.keys(enemies);\n        const randEnemyKey = enemyKeys&#91;Math.floor(Math.random() * enemyKeys.length)];\n        logElm.textContent += `\\nA wild ${enemies&#91;randEnemyKey].name} appears!`;\n        startBattle(randEnemyKey);\n      }\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u4ef2\u9593\u306e\u96c7\u7528\n    \/\/ -------------------------------------------\n    function hireCompanion() {\n      const input = document.getElementById('friendName');\n      let name = input.value.trim();\n      if (!name) return;\n\n      let newCompanion = { name, level: 1, hp: 30, maxHp: 30, attack: 2 };\n      companions.push(newCompanion);\n\n      input.value = \"\";\n      updateCompanionList();\n      saveLocalData();\n      showHomeMessage(`${name} joined your party!`);\n    }\n\n    function updateCompanionList() {\n      const listElm = document.getElementById('companion-list');\n      listElm.innerHTML = \"\";\n      companions.forEach(c => {\n        const li = document.createElement(\"li\");\n        li.textContent = `${c.name} (Lv ${c.level}, HP ${c.hp}\/${c.maxHp}, ATK ${c.attack})`;\n        listElm.appendChild(li);\n      });\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30a4\u30f3\u30d9\u30f3\u30c8\u30ea\u8868\u793a\n    \/\/ -------------------------------------------\n    let selectedItemIndex = null;\n    let selectedForCraft = &#91;];\n\n    function updateInventoryDisplay() {\n      const invElm = document.getElementById('inventory');\n      invElm.innerHTML = \"\";\n\n      if (inventory.length === 0) {\n        invElm.innerHTML = \"&lt;p>Your inventory is empty.&lt;\/p>\";\n        return;\n      }\n\n      inventory.forEach((item, index) => {\n        const div = document.createElement(\"div\");\n        div.className = \"inventory-item\";\n        div.textContent = item.name;\n\n        if (item.equipped) div.style.border = \"2px solid #4CAF50\";\n\n        div.onclick = () => onInventoryItemClick(index);\n        invElm.appendChild(div);\n      });\n    }\n\n    function onInventoryItemClick(index) {\n      if (document.getElementById(\"craft\").style.display === \"block\") {\n        toggleCraftSelection(index);\n        return;\n      }\n\n      selectedItemIndex = index;\n      const item = inventory&#91;index];\n      const modalTitle = document.getElementById(\"item-modal-title\");\n      const modalDesc = document.getElementById(\"item-modal-description\");\n\n      if (item.type === \"potion\") {\n        modalTitle.textContent = `Use ${item.name}?`;\n        modalDesc.textContent = `This potion restores ${item.heal} HP.`;\n      } else if (item.type === \"weapon\") {\n        modalTitle.textContent = `Equip ${item.name}?`;\n        modalDesc.textContent = `Weapon (+${item.attack} Attack).`;\n      } else if (item.type === \"armor\") {\n        modalTitle.textContent = `Equip ${item.name}?`;\n        modalDesc.textContent = `Armor (+${item.defense} Defense).`;\n      } else if (item.type === \"accessory\") {\n        modalTitle.textContent = `Equip ${item.name}?`;\n        modalDesc.textContent = `Accessory (+${item.attack} ATK, +${item.defense} DEF).`;\n      } else {\n        modalTitle.textContent = item.name;\n        modalDesc.textContent = \"A special item. No direct use\/equip.\";\n      }\n\n      if (canSellItem(item)) {\n        modalDesc.textContent += `\\n(Sell price: ${sellPrice(item)} Gold)`;\n      }\n\n      document.getElementById(\"item-modal-bg\").style.display = \"flex\";\n    }\n\n    function closeItemModal() {\n      document.getElementById(\"item-modal-bg\").style.display = \"none\";\n      selectedItemIndex = null;\n    }\n\n    function confirmItemUse() {\n      if (selectedItemIndex === null) return;\n      const item = inventory&#91;selectedItemIndex];\n\n      if (item.type === \"potion\") {\n        character.hp += item.heal;\n        if (character.hp > character.maxHp) character.hp = character.maxHp;\n        inventory.splice(selectedItemIndex, 1);\n        showHomeMessage(`${item.name} used! You recovered ${item.heal} HP.`);\n      }\n      else if (item.type === \"weapon\") {\n        unequipItem(\"weapon\");\n        item.equipped = true;\n        character.attack += item.attack;\n        showHomeMessage(`${item.name} equipped. (+${item.attack} Attack)`);\n      }\n      else if (item.type === \"armor\") {\n        unequipItem(\"armor\");\n        item.equipped = true;\n        character.defense += item.defense;\n        showHomeMessage(`${item.name} equipped. (+${item.defense} Defense)`);\n      }\n      else if (item.type === \"accessory\") {\n        unequipItem(\"accessory\");\n        item.equipped = true;\n        character.attack += item.attack;\n        character.defense += item.defense;\n        showHomeMessage(`${item.name} equipped. (+${item.attack} ATK, +${item.defense} DEF)`);\n      }\n      else {\n        if (canSellItem(item)) {\n          let price = sellPrice(item);\n          character.gold += price;\n          inventory.splice(selectedItemIndex, 1);\n          showHomeMessage(`You sold ${item.name} for ${price} Gold.`);\n        } else {\n          showHomeMessage(`You can't use ${item.name} right now.`);\n        }\n      }\n\n      updateCharacterInfo();\n      updateInventoryDisplay();\n      closeItemModal();\n    }\n\n    function unequipItem(type) {\n      for (let i = 0; i &lt; inventory.length; i++) {\n        let it = inventory&#91;i];\n        if (it.type === type &amp;&amp; it.equipped) {\n          it.equipped = false;\n          if (type === \"weapon\") character.attack -= it.attack;\n          else if (type === \"armor\") character.defense -= it.defense;\n          else if (type === \"accessory\") { character.attack -= it.attack; character.defense -= it.defense; }\n        }\n      }\n    }\n\n    function canSellItem(item) {\n      if (item.equipped) return false;\n      if (item.type === \"special\") return false;\n      return !&#91;\"weapon\",\"armor\",\"accessory\",\"potion\"].includes(item.type) ? true : false;\n    }\n    function sellPrice(item) { return 30; }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30af\u30a8\u30b9\u30c8UI\n    \/\/ -------------------------------------------\n    function updateQuestVisibility() {\n      const finalQuestCard = document.getElementById(\"final-quest\");\n      finalQuestCard.style.display = (!mainQuests.final.locked) ? \"block\" : \"none\";\n    }\n\n    function startQuest(questKey) {\n      let q = mainQuests&#91;questKey] || sideQuests&#91;questKey];\n      if (!q) return;\n      if (q.isRunning || q.isCompleted) return;\n      if (q.locked) {\n        showHomeMessage(\"This quest is locked. Complete the previous quest first!\");\n        return;\n      }\n\n      q.isRunning = true;\n      q.progress = 0;\n      updateProgressBar(questKey);\n\n      let progressInterval = setInterval(() => {\n        q.progress += 5;\n        if (q.progress > 100) q.progress = 100;\n        updateProgressBar(questKey);\n\n        if (q.progress === 100) {\n          clearInterval(progressInterval);\n          completeQuest(questKey);\n        }\n      }, 400);\n    }\n\n    function completeQuest(questKey) {\n      let q = mainQuests&#91;questKey] || sideQuests&#91;questKey];\n      q.isRunning = false;\n      q.isCompleted = true;\n\n      character.gold += q.reward.gold;\n      addXp(q.reward.xp);\n      q.reward.items.forEach(it => character.specialItems.push(it));\n\n      showHomeMessage(`${q.name} completed! You got ${q.reward.gold} Gold, ${q.reward.xp} XP, and ${q.reward.items.join(\", \")}.`);\n\n      if (q.unlockNext) {\n        if (mainQuests&#91;q.unlockNext]) mainQuests&#91;q.unlockNext].locked = false;\n        else if (sideQuests&#91;q.unlockNext]) sideQuests&#91;q.unlockNext].locked = false;\n      }\n\n      updateQuestVisibility();\n      updateCharacterInfo();\n      saveLocalData();\n    }\n\n    function updateProgressBar(questKey) {\n      const bar = document.getElementById(questKey + '-progress');\n      let q = mainQuests&#91;questKey] || sideQuests&#91;questKey];\n      if (bar) bar.style.width = q.progress + '%';\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30d0\u30c8\u30eb\n    \/\/ -------------------------------------------\n    function startBattle(enemyKey) {\n      const enemyDef = enemies&#91;enemyKey];\n      if (!enemyDef) return;\n      const logElm = document.getElementById('battle-log');\n      logElm.innerHTML = `A wild ${enemyDef.name} appears! (HP: ${enemyDef.hp})`;\n\n      if (skills.Heal.level > 0) {\n        const healAmount = skills.Heal.level * 10;\n        character.hp += healAmount;\n        if (character.hp > character.maxHp) character.hp = character.maxHp;\n        logMessage(logElm, `&#91;Skill: Heal Lv${skills.Heal.level}] You healed ${healAmount} HP!`);\n        updateCharacterInfo();\n      }\n\n      let enemyHp = enemyDef.hp;\n\n      let battleInterval = setInterval(() => {\n        processBuffsEachTurn(logElm);\n\n        if (character.hp &lt;= 0) {\n          clearInterval(battleInterval);\n          logMessage(logElm, \"You have been defeated...\");\n          saveLocalData();\n          return;\n        }\n\n        let baseDamage = getRandomInt(character.attack - 2, character.attack + 2);\n        if (baseDamage &lt; 1) baseDamage = 1;\n\n        if (skills.Fireball.level > 0) {\n          let extra = skills.Fireball.level * 2;\n          baseDamage += extra;\n          logMessage(logElm, `&#91;Fireball Lv${skills.Fireball.level}] Extra ${extra} magic damage!`);\n        }\n\n        let totalCompanionDamage = 0;\n        companions.forEach(c => { if (c.hp > 0) totalCompanionDamage += c.attack; });\n\n        let totalDamage = baseDamage + totalCompanionDamage;\n        enemyHp -= totalDamage;\n\n        logMessage(logElm, `You (and companions) deal ${totalDamage} damage! (Enemy HP: ${Math.max(enemyHp, 0)})`);\n\n        if (enemyHp &lt;= 0) {\n          clearInterval(battleInterval);\n          logMessage(logElm, `You defeated the ${enemyDef.name}!`);\n          character.gold += enemyDef.rewardGold;\n          addXp(enemyDef.rewardXp);\n          updateCompanionXP(enemyDef.rewardXp);\n          updateCharacterInfo();\n          saveLocalData();\n\n          achievements.firstKill.isUnlocked = true;\n          updateAchievementList();\n          return;\n        }\n\n        let eAtk = getRandomInt(enemyDef.attackMin, enemyDef.attackMax);\n        let dmgToPlayer = eAtk - character.defense;\n        if (dmgToPlayer &lt; 1) dmgToPlayer = 1;\n\n        character.hp -= dmgToPlayer;\n        if (character.hp &lt; 0) character.hp = 0;\n\n        logMessage(logElm, `The ${enemyDef.name} hits you for ${dmgToPlayer}. (Your HP: ${character.hp})`);\n        updateCharacterInfo();\n\n        if (character.hp &lt;= 0) {\n          clearInterval(battleInterval);\n          logMessage(logElm, \"You have been defeated...\");\n          saveLocalData();\n          return;\n        }\n      }, 800);\n    }\n\n    function logMessage(logElm, msg) {\n      const p = document.createElement(\"p\");\n      p.textContent = msg;\n      logElm.appendChild(p);\n      logElm.scrollTop = logElm.scrollHeight;\n    }\n\n    function updateCompanionXP(amount) {\n      for (let c of companions) {\n        c.level += Math.floor(amount\/50);\n        c.maxHp += 5;\n        c.hp = c.maxHp;\n        c.attack += 1;\n      }\n      updateCompanionList();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30b9\u30c8\u30a2\u8cfc\u5165\n    \/\/ -------------------------------------------\n    function buyItem(itemKey) {\n      const itemDef = storeItems&#91;itemKey];\n      if (!itemDef) return;\n      if (character.gold &lt; itemDef.cost) {\n        showHomeMessage(`You don't have enough gold to buy ${itemDef.name}.`);\n        return;\n      }\n      character.gold -= itemDef.cost;\n\n      let newItem = JSON.parse(JSON.stringify(itemDef));\n      if (&#91;\"weapon\",\"armor\",\"accessory\"].includes(newItem.type)) newItem.equipped = false;\n\n      inventory.push(newItem);\n\n      showHomeMessage(`You bought ${newItem.name}!`);\n      updateCharacterInfo();\n      updateInventoryDisplay();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30a2\u30fc\u30c8\u6240\u6301\/\u8cfc\u5165\/\u8a2d\u5b9a\n    \/\/ -------------------------------------------\n    function isArtOwned(artKey) {\n      return character.ownedArtKeys.includes(artKey);\n    }\n\n    function grantArt(artKey, setAsPortrait=false) {\n      const art = ART_LIST.find(a => a.key === artKey);\n      if (!art) return false;\n\n      if (!isArtOwned(art.key)) {\n        character.ownedArtKeys.push(art.key);\n      }\n      if (setAsPortrait) {\n        character.portraitUrl = art.url;\n      }\n      saveLocalData();\n      updateCharacterInfo();\n      renderArtShop();\n      renderArtGallery();\n      return true;\n    }\n\n    function buyArt(artKey) {\n      const art = ART_LIST.find(a => a.key === artKey);\n      if (!art) return;\n\n      if (isArtOwned(art.key)) {\n        showHomeMessage(`You already own ${art.name}.`);\n        return;\n      }\n      if (character.gold &lt; art.cost) {\n        showHomeMessage(`Not enough gold to buy ${art.name}. Need ${art.cost} Gold.`);\n        return;\n      }\n\n      character.gold -= art.cost;\n\n      \/\/ \u8cfc\u5165\u3057\u305f\u3089\u5373\u30ad\u30e3\u30e9\u7d75\u5909\u66f4\n      grantArt(art.key, true);\n\n      showHomeMessage(`Purchased ${art.name}! Character portrait changed.`);\n    }\n\n    function setPortraitFromArt(artKey) {\n      const art = ART_LIST.find(a => a.key === artKey);\n      if (!art) return;\n\n      if (!isArtOwned(art.key)) {\n        showHomeMessage(\"You don't own this art yet. Buy it in Store or pull it from Gacha.\");\n        return;\n      }\n\n      character.portraitUrl = art.url;\n      saveLocalData();\n      updateCharacterInfo();\n      showHomeMessage(`Character portrait set to ${art.name}.`);\n    }\n\n    \/\/ Store\u306e\u30a2\u30fc\u30c8\u4e00\u89a7\u63cf\u753b\n    function renderArtShop() {\n      const wrap = document.getElementById(\"art-shop-list\");\n      if (!wrap) return;\n\n      wrap.innerHTML = \"\";\n      ART_LIST.forEach(a => {\n        const owned = isArtOwned(a.key);\n\n        const rarityBadge = a.rarity === \"UR\"\n          ? `&lt;span class=\"badge rarity-ur\">UR&lt;\/span>`\n          : `&lt;span class=\"badge rarity-ssr\">SSR&lt;\/span>`;\n\n        const row = document.createElement(\"div\");\n        row.style.marginBottom = \"10px\";\n        row.innerHTML = `\n          &lt;div style=\"display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap;\">\n            &lt;div>\n              &lt;strong>${a.name}&lt;\/strong>\n              ${rarityBadge}\n              &lt;span class=\"muted\">(${a.cost} Gold)&lt;\/span>\n              ${owned ? `&lt;span class=\"badge owned\">Owned&lt;\/span>` : `&lt;span class=\"badge\">Not Owned&lt;\/span>`}\n            &lt;\/div>\n            &lt;div>\n              &lt;button class=\"button\" onclick=\"buyArt('${a.key}')\" ${owned ? 'disabled class=\"button disabled\"' : ''} ${owned ? 'disabled' : ''}>Buy&lt;\/button>\n              &lt;button class=\"button\" onclick=\"openArtModal('${a.key}')\">Preview&lt;\/button>\n            &lt;\/div>\n          &lt;\/div>\n        `;\n        wrap.appendChild(row);\n      });\n    }\n\n    \/\/ \u30ae\u30e3\u30e9\u30ea\u30fc\u63cf\u753b\n    function renderArtGallery() {\n      const grid = document.getElementById(\"art-gallery-grid\");\n      const sum = document.getElementById(\"art-collection-summary\");\n      if (!grid || !sum) return;\n\n      const ownedCount = character.ownedArtKeys.length;\n      sum.textContent = `Owned: ${ownedCount} \/ ${ART_LIST.length}`;\n\n      grid.innerHTML = \"\";\n      ART_LIST.forEach(a => {\n        const owned = isArtOwned(a.key);\n\n        const item = document.createElement(\"div\");\n        item.className = \"gallery-item\";\n\n        const img = document.createElement(\"img\");\n        img.src = a.url;\n        img.alt = a.name;\n        img.loading = \"lazy\";\n        img.style.cursor = \"pointer\";\n        img.onclick = () => openArtModal(a.key);\n\n        const meta = document.createElement(\"div\");\n        meta.className = \"gallery-meta\";\n        meta.innerHTML = `\n          &lt;div>\n            &lt;strong>${a.name}&lt;\/strong>&lt;br\/>\n            &lt;span class=\"muted\">${a.cost} Gold&lt;\/span>\n          &lt;\/div>\n          &lt;div style=\"display:flex; gap:6px; align-items:center;\">\n            ${a.rarity === \"UR\"\n              ? `&lt;span class=\"badge rarity-ur\">UR&lt;\/span>`\n              : `&lt;span class=\"badge rarity-ssr\">SSR&lt;\/span>`\n            }\n            ${owned ? `&lt;span class=\"badge owned\">Owned&lt;\/span>` : `&lt;span class=\"badge\">Not Owned&lt;\/span>`}\n          &lt;\/div>\n        `;\n\n        const actions = document.createElement(\"div\");\n        actions.className = \"gallery-actions\";\n\n        const btnPreview = document.createElement(\"button\");\n        btnPreview.className = \"button\";\n        btnPreview.textContent = \"Preview\";\n        btnPreview.onclick = () => openArtModal(a.key);\n\n        const btnSet = document.createElement(\"button\");\n        btnSet.className = \"button\";\n        btnSet.textContent = \"Set as Character Art\";\n        btnSet.disabled = !owned;\n        if (!owned) btnSet.classList.add(\"disabled\");\n        btnSet.onclick = () => setPortraitFromArt(a.key);\n\n        actions.appendChild(btnPreview);\n        actions.appendChild(btnSet);\n\n        item.appendChild(img);\n        item.appendChild(meta);\n        item.appendChild(actions);\n        grid.appendChild(item);\n      });\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u2605\u30ac\u30c1\u30e3\uff08SSR1\u301cSSR3\/UR1\u301cUR10\u304b\u3089\u62bd\u9078\uff09\n    \/\/ - \u5f15\u3044\u305f\u30a2\u30fc\u30c8\u306fOwned\u306b\u8ffd\u52a0\n    \/\/ - 1\u679a\u76ee\u3060\u3051\u306f\u6f14\u51fa\u7684\u306b\u30ad\u30e3\u30e9\u7d75\u3082\u5373\u5909\u66f4\uff08setAsPortrait=true\uff09\n    \/\/ -------------------------------------------\n    function pullArtGacha(times) {\n      const totalCost = GACHA_COST * times;\n      const status = document.getElementById(\"gacha-status\");\n      const resultWrap = document.getElementById(\"gacha-result\");\n      resultWrap.innerHTML = \"\";\n\n      if (character.gold &lt; totalCost) {\n        status.textContent = `Not enough gold. Need ${totalCost} Gold.`;\n        showHomeMessage(`Not enough gold for gacha. Need ${totalCost} Gold.`);\n        return;\n      }\n\n      character.gold -= totalCost;\n\n      let pulled = &#91;];\n      for (let i=0; i&lt;times; i++) {\n        const art = rollOneArt();\n        pulled.push(art);\n        \/\/ 1\u679a\u76ee\u3060\u3051\u5373\u30dd\u30fc\u30c8\u30ec\u30fc\u30c8\u5909\u66f4\uff08\u7d99\u7d9a\u4ed5\u69d8\uff09\n        grantArt(art.key, i === 0);\n      }\n\n      updateCharacterInfo();\n\n      status.textContent = `Pulled ${times} time(s). Cost ${totalCost} Gold.`;\n\n      \/\/ \u8868\u793a\n      pulled.forEach((a, idx) => {\n        const card = document.createElement(\"div\");\n        card.className = \"gacha-card\";\n        card.innerHTML = `\n          &lt;img src=\"${a.url}\" alt=\"${a.name}\">\n          &lt;div class=\"p\">\n            &lt;strong>${a.name}&lt;\/strong>&lt;br\/>\n            &lt;span class=\"muted\">${a.rarity}&lt;\/span>\n            ${isArtOwned(a.key) ? `&lt;span class=\"badge owned\" style=\"margin-left:6px;\">Owned&lt;\/span>` : ``}\n          &lt;\/div>\n        `;\n        card.onclick = () => openArtModal(a.key);\n        resultWrap.appendChild(card);\n      });\n\n      showHomeMessage(`Gacha result: ${pulled.map(a => a.rarity + \" \" + a.key).join(\", \")}`);\n\n      saveLocalData();\n      renderArtShop();\n      renderArtGallery();\n    }\n\n    function rollOneArt() {\n      const r = Math.random() * 100;\n      let rarity = (r &lt; GACHA_RATE_UR) ? \"UR\" : \"SSR\";\n\n      const pool = ART_LIST.filter(a => a.rarity === rarity);\n      \/\/ \u5ff5\u306e\u305f\u3081\n      if (pool.length === 0) return ART_LIST&#91;Math.floor(Math.random() * ART_LIST.length)];\n\n      return pool&#91;Math.floor(Math.random() * pool.length)];\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30a2\u30fc\u30c8\u30d7\u30ec\u30d3\u30e5\u30fc\u30fb\u30e2\u30fc\u30c0\u30eb\n    \/\/ -------------------------------------------\n    let pendingArtKey = null;\n\n    function openArtModal(artKey) {\n      const art = ART_LIST.find(a => a.key === artKey);\n      if (!art) return;\n\n      pendingArtKey = artKey;\n\n      document.getElementById(\"art-modal-title\").textContent = `${art.name} (${art.rarity})`;\n      document.getElementById(\"art-modal-img\").src = art.url;\n\n      const owned = isArtOwned(art.key);\n      document.getElementById(\"art-modal-desc\").textContent =\n        owned ? \"Owned: You can set this as your character art.\" : \"Not owned: Buy it in Store or pull it from Gacha.\";\n\n      const setBtn = document.getElementById(\"art-modal-set-btn\");\n      setBtn.disabled = !owned;\n      if (!owned) setBtn.classList.add(\"disabled\");\n      else setBtn.classList.remove(\"disabled\");\n\n      document.getElementById(\"art-modal-bg\").style.display = \"flex\";\n    }\n\n    function closeArtModal() {\n      document.getElementById(\"art-modal-bg\").style.display = \"none\";\n      pendingArtKey = null;\n    }\n\n    function confirmSetPortrait() {\n      if (!pendingArtKey) return;\n      setPortraitFromArt(pendingArtKey);\n      closeArtModal();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30af\u30e9\u30d5\u30c8\u95a2\u9023\n    \/\/ -------------------------------------------\n    function toggleCraftSelection(invIndex) {\n      const item = inventory&#91;invIndex];\n      if (selectedForCraft.includes(invIndex)) {\n        selectedForCraft = selectedForCraft.filter(i => i !== invIndex);\n      } else {\n        if (selectedForCraft.length >= 2) {\n          showHomeMessage(\"You can only select up to 2 items for crafting.\");\n          return;\n        }\n        selectedForCraft.push(invIndex);\n      }\n      updateCraftSelectionDisplay();\n    }\n\n    function updateCraftSelectionDisplay() {\n      let names = selectedForCraft.map(i => inventory&#91;i].name);\n      if (names.length === 0) names.push(\"None\");\n      document.getElementById(\"craft-selection\").textContent = names.join(\" &amp; \");\n      document.getElementById(\"craft-button\").disabled = (selectedForCraft.length &lt; 2);\n    }\n\n    function attemptCraft() {\n      if (selectedForCraft.length &lt; 2) return;\n\n      let itemA = inventory&#91;selectedForCraft&#91;0]];\n      let itemB = inventory&#91;selectedForCraft&#91;1]];\n      let combo = &#91;itemA.name, itemB.name].sort();\n\n      let craftedItem = null;\n      for (let r of craftRecipes) {\n        if (r.components&#91;0] === combo&#91;0] &amp;&amp; r.components&#91;1] === combo&#91;1]) {\n          craftedItem = r.result;\n          break;\n        }\n      }\n\n      if (!craftedItem) {\n        showHomeMessage(\"No valid recipe found for these items.\");\n        selectedForCraft = &#91;];\n        updateCraftSelectionDisplay();\n        return;\n      }\n\n      let idxA = Math.max(selectedForCraft&#91;0], selectedForCraft&#91;1]);\n      let idxB = Math.min(selectedForCraft&#91;0], selectedForCraft&#91;1]);\n      inventory.splice(idxA, 1);\n      inventory.splice(idxB, 1);\n      inventory.push(craftedItem);\n\n      showHomeMessage(`You crafted: ${craftedItem.name}!`);\n      selectedForCraft = &#91;];\n      updateCraftSelectionDisplay();\n      updateInventoryDisplay();\n      saveLocalData();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30b9\u30ad\u30eb\n    \/\/ -------------------------------------------\n    function refreshSkillList() {\n      document.getElementById(\"skill-point-display\").textContent = character.skillPoints;\n      const listElm = document.getElementById(\"skill-list\");\n      listElm.innerHTML = \"\";\n\n      for (let sKey in skills) {\n        let sk = skills&#91;sKey];\n        let li = document.createElement(\"li\");\n        li.innerHTML = `\n          &lt;strong>${sk.name} (Lv${sk.level}\/${sk.maxLevel})&lt;\/strong>\n          - ${sk.description}\n          ${\n            sk.level &lt; sk.maxLevel\n              ? `(&lt;button onclick=\"learnSkill('${sKey}')\">Upgrade (cost ${sk.cost})&lt;\/button>)`\n              : ''\n          }\n        `;\n        listElm.appendChild(li);\n      }\n    }\n\n    function learnSkill(skillKey) {\n      let skill = skills&#91;skillKey];\n      if (!skill) return;\n\n      if (skill.level >= skill.maxLevel) {\n        showHomeMessage(`${skill.name} is already at max level.`);\n        return;\n      }\n      if (character.skillPoints &lt; skill.cost) {\n        showHomeMessage(`Not enough skill points to upgrade ${skill.name}.`);\n        return;\n      }\n\n      character.skillPoints -= skill.cost;\n      skill.level++;\n      showHomeMessage(`You upgraded ${skill.name} to level ${skill.level}.`);\n      updateCharacterInfo();\n      refreshSkillList();\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u30af\u30a8\u30b9\u30c8\u30ed\u30b0\n    \/\/ -------------------------------------------\n    function updateQuestLog() {\n      const logElm = document.getElementById(\"quest-log-list\");\n      logElm.innerHTML = \"\";\n\n      let allQ = getAllQuests();\n      for (let key in allQ) {\n        let q = allQ&#91;key];\n        let status = q.isCompleted ? \"Completed\" : (q.isRunning ? \"In Progress\" : \"Not Started\");\n        let li = document.createElement(\"li\");\n        li.textContent = `${q.name}: ${status}`;\n        logElm.appendChild(li);\n      }\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u5b9f\u7e3e\n    \/\/ -------------------------------------------\n    function checkAchievements() {\n      if (character.level >= 5) achievements.level5.isUnlocked = true;\n\n      let completedCount = 0;\n      let allQ = getAllQuests();\n      for (let key in allQ) if (allQ&#91;key].isCompleted) completedCount++;\n\n      if (completedCount >= 3) achievements.quest3.isUnlocked = true;\n\n      saveLocalData();\n      updateAchievementList();\n    }\n\n    function updateAchievementList() {\n      const listElm = document.getElementById(\"achievement-list\");\n      listElm.innerHTML = \"\";\n\n      for (let aKey in achievements) {\n        let a = achievements&#91;aKey];\n        let status = a.isUnlocked ? \"Unlocked\" : \"Locked\";\n        let li = document.createElement(\"li\");\n        li.textContent = `${a.name} - ${a.description} &#91;${status}]`;\n        listElm.appendChild(li);\n      }\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ BGM\uff08\u7d99\u7d9a\u518d\u751f &amp; \u72b6\u614b\u4fdd\u5b58 &amp; \u5fa9\u5e30\uff09\n    \/\/ -------------------------------------------\n    let isMusicPlaying = false;\n    let bgmUnlocked = false;\n    let wantAutoResume = false;\n\n    function setBgmStatus(text) {\n      const s = document.getElementById(\"bgm-status\");\n      if (s) s.textContent = \"Status: \" + text;\n    }\n\n    function saveBgmState() {\n      const audio = document.getElementById(\"bgm\");\n      if (!audio) return;\n\n      const state = {\n        unlocked: bgmUnlocked,\n        playing: isMusicPlaying,\n        volume: audio.volume,\n        time: audio.currentTime\n      };\n      localStorage.setItem(LS_KEY_BGM, JSON.stringify(state));\n    }\n\n    function loadBgmState() {\n      const audio = document.getElementById(\"bgm\");\n      if (!audio) return;\n\n      const raw = localStorage.getItem(LS_KEY_BGM);\n      if (!raw) return;\n\n      try {\n        const st = JSON.parse(raw);\n        bgmUnlocked = !!st.unlocked;\n        isMusicPlaying = !!st.playing;\n        wantAutoResume = isMusicPlaying;\n\n        if (typeof st.volume === \"number\") audio.volume = st.volume;\n\n        if (typeof st.time === \"number\") {\n          audio.addEventListener(\"loadedmetadata\", () => {\n            try { audio.currentTime = Math.max(0, st.time); } catch(e) {}\n          }, { once: true });\n        }\n      } catch(e) {}\n    }\n\n    function wireBgmAutoSave() {\n      const audio = document.getElementById(\"bgm\");\n      if (!audio) return;\n\n      audio.addEventListener(\"play\", () => { isMusicPlaying = true; saveBgmState(); });\n      audio.addEventListener(\"pause\", () => { isMusicPlaying = false; saveBgmState(); });\n      audio.addEventListener(\"volumechange\", saveBgmState);\n\n      let lastSave = 0;\n      audio.addEventListener(\"timeupdate\", () => {\n        const now = Date.now();\n        if (now - lastSave > 4000) {\n          lastSave = now;\n          saveBgmState();\n        }\n      });\n    }\n\n    function updateEnableBtn() {\n      const btn = document.getElementById(\"bgm-enable-btn\");\n      if (!btn) return;\n\n      if (bgmUnlocked) {\n        btn.textContent = \"BGM Enabled\";\n        btn.classList.add(\"disabled\");\n        btn.disabled = true;\n      } else {\n        btn.textContent = \"Enable BGM (First Click)\";\n        btn.classList.remove(\"disabled\");\n        btn.disabled = false;\n      }\n    }\n\n    function resumeBgmOnNextUserActionIfNeeded() {\n      if (!bgmUnlocked || !wantAutoResume) return;\n\n      const audio = document.getElementById(\"bgm\");\n      if (!audio) return;\n\n      const resumeOnce = () => {\n        audio.play().then(() => {\n          isMusicPlaying = true;\n          wantAutoResume = false;\n          setBgmStatus(\"On (Resumed)\");\n          saveBgmState();\n        }).catch(() => {\n          setBgmStatus(\"Blocked (Enable again)\");\n        });\n      };\n\n      document.addEventListener(\"pointerdown\", resumeOnce, { once: true });\n      document.addEventListener(\"keydown\", resumeOnce, { once: true });\n    }\n\n    function enableBGM() {\n      const audio = document.getElementById(\"bgm\");\n      if (!audio) return;\n\n      audio.volume = 0.6;\n\n      audio.play().then(() => {\n        bgmUnlocked = true;\n        isMusicPlaying = true;\n        wantAutoResume = false;\n\n        updateEnableBtn();\n        setBgmStatus(\"On\");\n        showHomeMessage(\"BGM Enabled &amp; Playing\");\n        saveBgmState();\n      }).catch(() => {\n        setBgmStatus(\"Blocked (Click again)\");\n        showHomeMessage(\"BGM blocked by browser. Click Enable BGM again.\");\n      });\n    }\n\n    function toggleMusic() {\n      const audio = document.getElementById(\"bgm\");\n      if (!audio) return;\n\n      if (!bgmUnlocked) {\n        showHomeMessage(\"First, click 'Enable BGM (First Click)'.\");\n        setBgmStatus(\"Locked\");\n        return;\n      }\n\n      if (!isMusicPlaying) {\n        audio.play().then(() => {\n          isMusicPlaying = true;\n          setBgmStatus(\"On\");\n          showHomeMessage(\"Music On\");\n          saveBgmState();\n        }).catch(() => {\n          setBgmStatus(\"Blocked\");\n          showHomeMessage(\"Music could not be played (browser block).\");\n        });\n      } else {\n        audio.pause();\n        isMusicPlaying = false;\n        setBgmStatus(\"Off\");\n        showHomeMessage(\"Music Off\");\n        saveBgmState();\n      }\n    }\n\n    \/\/ -------------------------------------------\n    \/\/ \u6c4e\u7528\u30e9\u30f3\u30c0\u30e0\u6574\u6570\n    \/\/ -------------------------------------------\n    function getRandomInt(min, max) {\n      return Math.floor(Math.random() * (max - min + 1) + min);\n    }\n  &lt;\/script>\n&lt;\/body>\n&lt;\/html><\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[44],"tags":[3],"class_list":["post-26244","post","type-post","status-publish","format-standard","hentry","category-44","tag-programming"],"aioseo_notices":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/26244","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=26244"}],"version-history":[{"count":1,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/26244\/revisions"}],"predecessor-version":[{"id":26245,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/26244\/revisions\/26245"}],"wp:attachment":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=26244"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=26244"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=26244"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}