{"id":25882,"date":"2025-03-02T10:56:54","date_gmt":"2025-03-02T01:56:54","guid":{"rendered":"http:\/\/www.tyosuke20xx.com\/blog\/?p=25882"},"modified":"2025-03-02T10:57:42","modified_gmt":"2025-03-02T01:57:42","slug":"questifyinfinity-html","status":"publish","type":"post","link":"http:\/\/www.tyosuke20xx.com\/blog\/?p=25882","title":{"rendered":"QuestifyInfinity.html"},"content":{"rendered":"\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html>\n&lt;html lang=\"ja\">\n&lt;head>\n  &lt;meta charset=\"UTF-8\">\n  &lt;title>Questify Advanced Battle&lt;\/title>\n  &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\n  &lt;!-- \u30ec\u30c8\u30ed\u30b2\u30fc\u30e0\u98a8\u30d5\u30a9\u30f3\u30c8 -->\n  &lt;link href=\"https:\/\/fonts.googleapis.com\/css2?family=Press+Start+2P&amp;display=swap\" rel=\"stylesheet\">\n\n  &lt;style>\n    \/* ========== \u5168\u4f53 ========== *\/\n    body {\n      background-color: #1a1a1a;\n      color: #fff;\n      font-family: 'Press Start 2P', cursive;\n      margin: 0; padding: 0;\n    }\n    header, footer {\n      background: #333; text-align: center; padding: 15px; border-bottom: 2px solid #555;\n    }\n    header h1 { font-size: 1.3rem; margin: 0; }\n    main { max-width: 1200px; margin: 10px auto; padding-bottom: 80px; }\n\n    .box {\n      background: #2b2b2b; margin-bottom: 20px;\n      border: 2px solid #555; padding: 20px; position: relative;\n    }\n    .section-title { font-size: 1.2rem; margin-bottom: 10px; }\n    button {\n      font-family: 'Press Start 2P', cursive;\n      border: none; cursor: pointer; padding: 6px 10px; margin-right: 5px;\n    }\n    button:hover { opacity: 0.8; }\n    .btn-primary { background: #006699; color: #fff; }\n    .btn-delete { background: #bb3333; color: #fff; }\n\n    \/* \u30b9\u30c6\u30fc\u30bf\u30b9\u8868\u793a *\/\n    .hero-info p { margin: 5px 0; }\n    .xp-bar {\n      background: #555; width: 100%; height: 20px; margin: 5px 0;\n      position: relative; overflow: hidden;\n    }\n    .xp-fill {\n      background: #00aa00; width: 0%; height: 100%;\n      transition: width 0.3s ease;\n    }\n\n    \/* \u30d1\u30fc\u30c6\u30a3 *\/\n    .party-member {\n      background: #333; border: 2px solid #555;\n      padding: 10px; margin-bottom: 10px;\n    }\n\n    \/* \u30b9\u30ad\u30eb\u30c4\u30ea\u30fc *\/\n    .skill-tree .skill {\n      background: #333; border: 2px solid #555;\n      padding: 10px; margin-bottom: 10px;\n    }\n\n    \/* \u88c5\u5099\u30fb\u7d20\u6750 *\/\n    .equipment-info p { margin: 3px 0; }\n    .craft-recipe {\n      border: 1px dashed #999;\n      margin-bottom: 10px; padding: 5px;\n    }\n\n    \/* \u30af\u30a8\u30b9\u30c8 *\/\n    .quest-list .quest {\n      background: #333; border: 2px solid #555;\n      padding: 10px; margin-bottom: 10px;\n    }\n\n    \/* \u5b9f\u7e3e *\/\n    .achievements .achievement {\n      background: #333; border: 2px solid #555;\n      padding: 10px; margin-bottom: 10px; position: relative;\n    }\n    .achievement.locked { opacity: 0.5; }\n    .locked-label {\n      position: absolute; top: 5px; right: 5px;\n      background: #cc0000; padding: 3px 5px; font-size: 0.7rem;\n    }\n\n    \/* \u30d0\u30c8\u30eb\u30e2\u30fc\u30c0\u30eb *\/\n    .modal-bg {\n      position: fixed; top: 0; left: 0; width: 100%; height: 100%;\n      background: rgba(0,0,0,0.8);\n      display: none; justify-content: center; align-items: center;\n    }\n    .modal {\n      background: #2b2b2b; border: 2px solid #555;\n      padding: 20px; max-width: 600px; width: 90%;\n      position: relative;\n    }\n    .close-btn {\n      position: absolute; top: 10px; right: 10px;\n      background: #dd3333; color: #fff; padding: 5px 8px; border: none;\n    }\n    .battle-enemy-info, .battle-hero-info {\n      margin-bottom: 10px;\n    }\n    #battleLog {\n      border: 1px solid #555;\n      min-height: 60px; padding: 5px;\n      margin: 10px 0; max-height: 200px; overflow-y: auto;\n    }\n    .battle-action-buttons { margin-top: 10px; }\n    .battle-action-buttons button { margin: 5px 5px 0 0; }\n  &lt;\/style>\n&lt;\/head>\n\n&lt;body>\n&lt;header>\n  &lt;h1>Questify Advanced Battle&lt;\/h1>\n&lt;\/header>\n\n&lt;main>\n\n  &lt;!-- ===================== \u30d2\u30fc\u30ed\u30fc &amp; \u30d1\u30fc\u30c6\u30a3\u30b9\u30c6\u30fc\u30bf\u30b9 ===================== -->\n  &lt;section class=\"box\">\n    &lt;h2 class=\"section-title\">\u5192\u967a\u8005\u30b9\u30c6\u30fc\u30bf\u30b9&lt;\/h2>\n    &lt;div class=\"hero-info\">\n      &lt;p>\u52c7\u8005: &lt;span id=\"heroNameDisplay\">No Name&lt;\/span>&lt;\/p>\n      &lt;p>\u8077\u696d: &lt;span id=\"heroJobDisplay\">\u672a\u8a2d\u5b9a&lt;\/span>&lt;\/p>\n      &lt;p>\n        Lv.&lt;span id=\"heroLevel\">1&lt;\/span>\n        HP:&lt;span id=\"heroHp\">?&lt;\/span>\/&lt;span id=\"heroMaxHp\">?&lt;\/span>\n      &lt;\/p>\n      &lt;div class=\"xp-bar\">\n        &lt;div class=\"xp-fill\" id=\"xpFill\">&lt;\/div>\n      &lt;\/div>\n      &lt;p>EXP: &lt;span id=\"heroXp\">0&lt;\/span> \/ &lt;span id=\"xpToNextLevel\">100&lt;\/span>&lt;\/p>\n      &lt;p>Gold: &lt;span id=\"heroGold\">0&lt;\/span>&lt;\/p>\n      &lt;p>Skill Pts: &lt;span id=\"heroSkillPts\">0&lt;\/span>&lt;\/p>\n      &lt;label>\u52c7\u8005\u540d: &lt;input type=\"text\" id=\"heroNameInput\" placeholder=\"\u30a2\u30eb\u30b9\" \/>&lt;\/label>\n      &lt;button onclick=\"changeHeroName()\">\u5909\u66f4&lt;\/button>\n    &lt;\/div>\n  &lt;\/section>\n\n  &lt;section class=\"box\">\n    &lt;h2 class=\"section-title\">\u30d1\u30fc\u30c6\u30a3\u30e1\u30f3\u30d0\u30fc&lt;\/h2>\n    &lt;div id=\"partyList\">&lt;\/div>\n    &lt;button class=\"btn-primary\" onclick=\"addPartyMember()\">\u4ef2\u9593\u3092\u96c7\u3046 (\u6700\u59272\u4eba)&lt;\/button>\n  &lt;\/section>\n\n  &lt;!-- ===================== \u30b9\u30ad\u30eb\u30c4\u30ea\u30fc ===================== -->\n  &lt;section class=\"box\">\n    &lt;h2 class=\"section-title\">\u8077\u696d &amp; \u30b9\u30ad\u30eb\u30c4\u30ea\u30fc&lt;\/h2>\n    &lt;p>\n      &lt;button onclick=\"setJob('\u6226\u58eb')\">\u6226\u58eb&lt;\/button>\n      &lt;button onclick=\"setJob('\u9b54\u6cd5\u4f7f\u3044')\">\u9b54\u6cd5\u4f7f\u3044&lt;\/button>\n      &lt;button onclick=\"setJob('\u76d7\u8cca')\">\u76d7\u8cca&lt;\/button>\n    &lt;\/p>\n    &lt;div class=\"skill-tree\" id=\"skillTree\">&lt;\/div>\n  &lt;\/section>\n\n  &lt;!-- ===================== \u88c5\u5099 &amp; \u30af\u30e9\u30d5\u30c8 ===================== -->\n  &lt;section class=\"box\">\n    &lt;h2 class=\"section-title\">\u88c5\u5099 &amp; \u30af\u30e9\u30d5\u30c8&lt;\/h2>\n    &lt;div class=\"equipment-info\" id=\"heroEquipment\">&lt;\/div>\n\n    &lt;h3>\u30af\u30e9\u30d5\u30c8\u30ec\u30b7\u30d4&lt;\/h3>\n    &lt;div id=\"craftContainer\">&lt;\/div>\n  &lt;\/section>\n\n  &lt;!-- ===================== \u30af\u30a8\u30b9\u30c8 (\u30c7\u30a4\u30ea\u30fc\/\u901a\u5e38\/\u30a6\u30a3\u30fc\u30af\u30ea\u30fc) ===================== -->\n  &lt;section class=\"box\">\n    &lt;h2 class=\"section-title\">\u30af\u30a8\u30b9\u30c8&lt;\/h2>\n    &lt;div style=\"margin-bottom:10px;\">\n      &lt;button class=\"btn-primary\" onclick=\"switchQuestTab('daily')\">\u30c7\u30a4\u30ea\u30fc&lt;\/button>\n      &lt;button class=\"btn-primary\" onclick=\"switchQuestTab('normal')\">\u901a\u5e38&lt;\/button>\n      &lt;button class=\"btn-primary\" onclick=\"switchQuestTab('weekly')\">\u30a6\u30a3\u30fc\u30af\u30ea\u30fc&lt;\/button>\n    &lt;\/div>\n    &lt;div id=\"dailyQuests\" class=\"quest-list\">&lt;\/div>\n    &lt;div id=\"normalQuests\" class=\"quest-list\" style=\"display:none;\">&lt;\/div>\n    &lt;div id=\"weeklyQuests\" class=\"quest-list\" style=\"display:none;\">&lt;\/div>\n  &lt;\/section>\n\n  &lt;!-- ===================== \u30de\u30c3\u30d7 &amp; \u30dc\u30b9\u6226 ===================== -->\n  &lt;section class=\"box\">\n    &lt;h2 class=\"section-title\">\u30ef\u30fc\u30eb\u30c9\u30de\u30c3\u30d7&lt;\/h2>\n    &lt;div id=\"mapAreaContainer\">&lt;\/div>\n  &lt;\/section>\n\n  &lt;!-- ===================== \u30e9\u30f3\u30c0\u30e0\u30a8\u30f3\u30ab\u30a6\u30f3\u30c8 ===================== -->\n  &lt;section class=\"box\">\n    &lt;h2 class=\"section-title\">\u30c0\u30f3\u30b8\u30e7\u30f3\u6f5c\u5165 (\u8907\u6570\u6575\u30e9\u30f3\u30c0\u30e0\u30a8\u30f3\u30ab\u30a6\u30f3\u30c8)&lt;\/h2>\n    &lt;p>\u30dc\u30bf\u30f3\u3092\u62bc\u3059\u3068 **\u8907\u6570\u306e\u6575** \u304c\u51fa\u308b\u5834\u5408\u3082\uff01&lt;\/p>\n    &lt;button class=\"btn-primary\" onclick=\"startRandomEncounter()\">\u30c0\u30f3\u30b8\u30e7\u30f3\u306b\u6f5c\u308b&lt;\/button>\n  &lt;\/section>\n\n  &lt;!-- ===================== \u5b9f\u7e3e &amp; \u30b9\u30c8\u30fc\u30ea\u30fc ===================== -->\n  &lt;section class=\"box achievements\">\n    &lt;h2 class=\"section-title\">\u5b9f\u7e3e(Achievements)&lt;\/h2>\n    &lt;div id=\"achievementList\">&lt;\/div>\n  &lt;\/section>\n\n  &lt;section class=\"box\">\n    &lt;h2 class=\"section-title\">\u30b9\u30c8\u30fc\u30ea\u30fc\u9032\u884c&lt;\/h2>\n    &lt;div id=\"storyProgress\">&lt;\/div>\n  &lt;\/section>\n\n&lt;\/main>\n\n&lt;footer>\n  &lt;p>\u00a9 2025 Questify Advanced Battle&lt;\/p>\n&lt;\/footer>\n\n&lt;!-- ========== \u500b\u5225\u30bf\u30fc\u30f3\u5236\u30d0\u30c8\u30eb\u30e2\u30fc\u30c0\u30eb ========== -->\n&lt;div class=\"modal-bg\" id=\"battleModalBg\">\n  &lt;div class=\"modal\" id=\"battleModal\">\n    &lt;button class=\"close-btn\" onclick=\"closeBattleModal()\">\u00d7&lt;\/button>\n    &lt;h2 id=\"battleTitle\">\u30d0\u30c8\u30eb&lt;\/h2>\n    &lt;p id=\"battleDesc\">&lt;\/p>\n\n    &lt;!-- \u6575\u4e00\u89a7\u8868\u793a -->\n    &lt;div class=\"battle-enemy-info\" id=\"battleEnemyInfo\">&lt;\/div>\n    &lt;!-- \u5473\u65b9\u4e00\u89a7\u8868\u793a -->\n    &lt;div class=\"battle-hero-info\" id=\"battleHeroInfo\">&lt;\/div>\n\n    &lt;!-- \u884c\u52d5\u30ed\u30b0 -->\n    &lt;div id=\"battleLog\">&lt;\/div>\n\n    &lt;!-- \u30d7\u30ec\u30a4\u30e4\u30fc\u64cd\u4f5c\u30dc\u30bf\u30f3 (\u30a2\u30af\u30b7\u30e7\u30f3\u9078\u629e) -->\n    &lt;div class=\"battle-action-buttons\" id=\"battleActionButtons\">\n      &lt;button onclick=\"chooseAttack()\">\u653b\u6483&lt;\/button>\n      &lt;button onclick=\"chooseSkill()\">\u30b9\u30ad\u30eb&lt;\/button>\n      &lt;button onclick=\"chooseItem()\">\u30a2\u30a4\u30c6\u30e0&lt;\/button>\n      &lt;button onclick=\"chooseDefend()\">\u9632\u5fa1&lt;\/button>\n      &lt;button onclick=\"chooseFlee()\">\u9003\u3052\u308b&lt;\/button>\n    &lt;\/div>\n  &lt;\/div>\n&lt;\/div>\n\n&lt;script>\n\/* =========================================\n   1) \u30c7\u30fc\u30bf\u69cb\u9020 &amp; \u30ed\u30fc\u30ab\u30eb\u30b9\u30c8\u30ec\u30fc\u30b8\n========================================= *\/\nconst STORAGE_KEY = \"questify_battle_data\";\n\nlet gameData = {\n  hero: {\n    name: \"No Name\",\n    job: \"\u672a\u8a2d\u5b9a\",\n    level: 1,\n    xp: 0,\n    gold: 0,\n    skillPts: 0,\n    hp: 50,\n    maxHp: 50,\n    speed: 8,  \/\/ \u8ffd\u52a0: \u7d20\u65e9\u3055\n    equipment: { weapon: null, armor: null, accessory: null },\n    materials: { wood: 0, ore: 0, magicCrystal: 0 },\n    consumables: { potion: 2 },\n    \/\/ \u30a2\u30af\u30c6\u30a3\u30d6\u30b9\u30ad\u30eb(\u4f8b): \u653b\u6483\u30b9\u30ad\u30eb\n    activeSkills: &#91;\n      { id: \"slash\", name: \"\u30d1\u30ef\u30fc\u30b9\u30e9\u30c3\u30b7\u30e5\", baseDamage: 15 },\n      \/\/ \u3053\u3053\u306b\u8ffd\u52a0\u30b9\u30ad\u30eb\u3092\u5897\u3084\u3059\n    ]\n  },\n  party: &#91;\n    \/\/ \u4ef2\u9593\u3082 speed \u3092\u6301\u3064\u3001activeSkills \u3092\u6301\u3064\u306a\u3069\u62e1\u5f35\u53ef\u80fd\n    \/\/ { id, name, level, xp, hp, maxHp, speed, attack, equipment, activeSkills, ... }\n  ],\n  \/\/ \u8077\u696d\u3054\u3068\u306e\u30d1\u30c3\u30b7\u30d6\u30b9\u30ad\u30eb\n  skills: {\n    warrior: &#91;\n      { id: 1, name: \"\u5263\u8853\u719f\u7df4\", level: 0, maxLevel: 5, desc: \"\u653b\u6483\u529b+2\/Lv\" },\n      { id: 2, name: \"\u4f53\u529b\u5897\u5f37\", level: 0, maxLevel: 5, desc: \"HP+10\/Lv\" }\n    ],\n    mage: &#91;\n      { id: 1, name: \"\u9b54\u529b\u5897\u5f37\", level: 0, maxLevel: 5, desc: \"\u9b54\u6cd5\u653b\u6483\u529b+3\/Lv\" },\n      { id: 2, name: \"\u7cbe\u795e\u96c6\u4e2d\", level: 0, maxLevel: 5, desc: \"\u30dc\u30b9\u6226\u8ffd\u52a0\u30c0\u30e1\u30fc\u30b8+5\/Lv\" }\n    ],\n    thief: &#91;\n      { id: 1, name: \"\u7d20\u65e9\u3055\u5f37\u5316\", level: 0, maxLevel: 5, desc: \"\u653b\u6483\u529b+2\/Lv\" },\n      { id: 2, name: \"\u30b4\u30fc\u30eb\u30c9\u76d7\u307f\", level: 0, maxLevel: 5, desc: \"\u8a0e\u4f10\u6642Gold+5\/Lv\" }\n    ]\n  },\n  \/\/ \u30af\u30e9\u30d5\u30c8\u30ec\u30b7\u30d4 &amp; \u30af\u30a8\u30b9\u30c8 &amp; \u30de\u30c3\u30d7 &amp; \u5b9f\u7e3e \u7b49\u306f\u524d\u56de\u30b3\u30fc\u30c9\u3068\u540c\u3058\n  \/\/ \u2026\uff08\u7701\u7565\u306a\u3057\u3001\u5168\u3066\u8a18\u8f09\uff09\u2026\n  craftingRecipes: &#91;\n    {\n      id: 1,\n      result: { name: \"\u56de\u5fa9\u85ac\", type: \"consumable\", itemKey: \"potion\", amount: 1, hpRestore: 30 },\n      materialsRequired: { wood: 1, magicCrystal: 1 },\n      desc: \"\u6728\u67501 &amp; \u9b54\u529b\u7d50\u66761 \u3067\u56de\u5fa9\u85ac\u30921\u3064\u751f\u6210\"\n    }\n  ],\n  dailyQuests: &#91;\n    { id: 1, title: \"\u3010\u30c7\u30a4\u30ea\u30fc\u3011\u90e8\u5c4b\u6383\u9664\", exp: 10, gold: 5, completed: false },\n    { id: 2, title: \"\u3010\u30c7\u30a4\u30ea\u30fc\u3011\u7b4b\u30c8\u30ec15\u5206\", exp: 15, gold: 5, completed: false }\n  ],\n  normalQuests: &#91;\n    { id: 1, title: \"HTML\/CSS\u306e\u5b66\u7fd2\", exp: 20, gold: 10, completed: false },\n    { id: 2, title: \"\u30e9\u30f3\u30cb\u30f3\u30b030\u5206\", exp: 25, gold: 10, completed: false }\n  ],\n  weeklyQuests: &#91;\n    { id: 1, title: \"\u3010\u30a6\u30a3\u30fc\u30af\u30ea\u30fc\u30115\u65e5\u9023\u7d9a\u65e9\u8d77\u304d\", exp: 50, gold: 30, completed: false },\n    { id: 2, title: \"\u3010\u30a6\u30a3\u30fc\u30af\u30ea\u30fc\u3011\u5408\u8a085\u6642\u9593\u306e\u5b66\u7fd2\", exp: 60, gold: 40, completed: false }\n  ],\n  mapAreas: &#91;\n    { id: 1, name: \"\u8857\", levelReq: 1, desc: \"\u5b89\u5168\u306a\u8857\", boss: null },\n    { id: 2, name: \"\u68ee\", levelReq: 3, desc: \"\u6728\u6750\u304c\u624b\u306b\u5165\u308b\u304b\u3082\", boss: { name: \"\u68ee\u306e\u4e3b\", hp: 60, maxHp: 60, speed: 6, atk: 12, rewardExp: 50, rewardGold: 30 } },\n    { id: 3, name: \"\u6d1e\u7a9f\", levelReq: 5, desc: \"\u9271\u77f3\u304c\u7720\u308b\", boss: { name: \"\u30b4\u30fc\u30ec\u30e0\", hp: 100, maxHp: 100, speed: 4, atk: 20, rewardExp: 100, rewardGold: 80 } },\n    { id: 4, name: \"\u9b54\u738b\u57ce\", levelReq: 10, desc: \"\u9b54\u738b\u304c\u652f\u914d\u3059\u308b\u57ce\", boss: { name: \"\u9b54\u738b\", hp: 200, maxHp: 200, speed: 10, atk: 35, rewardExp: 300, rewardGold: 200 } }\n  ],\n  achievements: &#91;\n    { id: 1, title: \"\u521d\u30af\u30a8\u30b9\u30c8\u9054\u6210\", desc: \"\u30af\u30a8\u30b9\u30c8\u30921\u56de\u5b8c\u4e86\", type: \"questCount\", target: 1, unlocked: false },\n    { id: 2, title: \"\u30ec\u30d9\u30eb10\u5230\u9054\", desc: \"Lv.10\u306b\u306a\u308b\", type: \"level\", target: 10, unlocked: false },\n    { id: 3, title: \"\u68ee\u306e\u4e3b\u6483\u7834\", desc: \"\u68ee\u306e\u4e3b\u3092\u5012\u3059\", type: \"bossKill\", bossName: \"\u68ee\u306e\u4e3b\", unlocked: false },\n    { id: 4, title: \"\u9b54\u738b\u6483\u7834\", desc: \"\u9b54\u738b\u3092\u5012\u3059\", type: \"bossKill\", bossName: \"\u9b54\u738b\", unlocked: false },\n    { id: 5, title: \"\u91d1\u6301\u3061\", desc: \"\u30b4\u30fc\u30eb\u30c9\u304c100\u3092\u8d85\u3048\u308b\", type: \"gold\", target: 100, unlocked: false }\n  ],\n  story: &#91;\n    { bossName: \"\u68ee\u306e\u4e3b\", text: \"\u68ee\u306e\u4e3b\u3092\u5012\u3057\u3001\u68ee\u306b\u5e73\u7a4f\u304c\u623b\u3063\u305f\u2026\uff01\" },\n    { bossName: \"\u30b4\u30fc\u30ec\u30e0\", text: \"\u6d1e\u7a9f\u306e\u30b4\u30fc\u30ec\u30e0\u3092\u7c89\u7815\u3057\u3001\u9271\u5c71\u3078\u306e\u9053\u304c\u958b\u3051\u305f\u3002\" },\n    { bossName: \"\u9b54\u738b\", text: \"\u9b54\u738b\u3092\u5012\u3057\u3001\u4e16\u754c\u306b\u5e73\u548c\u304c\u8a2a\u308c\u305f\u3002\u3042\u306a\u305f\u306f\u771f\u306e\u52c7\u8005\uff01\" }\n  ],\n  enemies: &#91;\n    { name: \"\u68ee\u306e\u72fc\", hp: 30, maxHp: 30, speed: 7, atk: 8, exp: 15, gold: 5, dropMat: { wood: 1 } },\n    { name: \"\u6d1e\u7a9f\u30b3\u30a6\u30e2\u30ea\", hp: 35, maxHp: 35, speed: 9, atk: 10, exp: 20, gold: 8, dropMat: { ore: 1 } },\n    { name: \"\u30b4\u30d6\u30ea\u30f3\", hp: 40, maxHp: 40, speed: 5, atk: 12, exp: 25, gold: 10, dropMat: { wood: 1, ore: 1 } }\n  ],\n  lastDailyReset: null,\n  lastWeeklyReset: null\n};\n\nfunction loadData() {\n  const saved = localStorage.getItem(STORAGE_KEY);\n  if (saved) {\n    gameData = JSON.parse(saved);\n  }\n}\nfunction saveData() {\n  localStorage.setItem(STORAGE_KEY, JSON.stringify(gameData));\n}\n\n\/* =========================================\n   2) \u521d\u671f\u5316\u51e6\u7406\n========================================= *\/\nwindow.addEventListener(\"load\", () => {\n  loadData();\n  dailyQuestResetCheck();\n  weeklyQuestResetCheck();\n  applySkillPassive();\n  updateAllUI();\n});\n\n\/* =========================================\n   3) \u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3 &amp; \u30b9\u30c6\u30fc\u30bf\u30b9\u7cfb\n========================================= *\/\nfunction xpNeededForLevel(level) {\n  return level * 100;\n}\nfunction checkLevelUp() {\n  while (gameData.hero.xp >= xpNeededForLevel(gameData.hero.level)) {\n    gameData.hero.xp -= xpNeededForLevel(gameData.hero.level);\n    gameData.hero.level++;\n    gameData.hero.skillPts++;\n    applySkillPassive();\n    alert(`\u30ec\u30d9\u30eb\u30a2\u30c3\u30d7\uff01Lv.${gameData.hero.level} \u306b\u306a\u3063\u305f\u3002\u30b9\u30ad\u30eb\u30dd\u30a4\u30f3\u30c8+1`);\n  }\n}\nfunction gainExp(amount) {\n  gameData.hero.xp += amount;\n  checkLevelUp();\n  saveData();\n  updateHeroUI();\n  checkAchievements();\n}\nfunction gainGold(amount) {\n  gameData.hero.gold += amount;\n  saveData();\n  updateHeroUI();\n  checkAchievements();\n}\nfunction updateHeroUI() {\n  const h = gameData.hero;\n  document.getElementById(\"heroNameDisplay\").textContent = h.name;\n  document.getElementById(\"heroJobDisplay\").textContent = h.job;\n  document.getElementById(\"heroLevel\").textContent = h.level;\n  document.getElementById(\"heroHp\").textContent = h.hp;\n  document.getElementById(\"heroMaxHp\").textContent = h.maxHp;\n  document.getElementById(\"heroXp\").textContent = h.xp;\n  document.getElementById(\"xpToNextLevel\").textContent = xpNeededForLevel(h.level);\n  document.getElementById(\"heroGold\").textContent = h.gold;\n  document.getElementById(\"heroSkillPts\").textContent = h.skillPts;\n\n  const ratio = (h.xp \/ xpNeededForLevel(h.level)) * 100;\n  document.getElementById(\"xpFill\").style.width = ratio + \"%\";\n\n  updateHeroEquipmentUI();\n}\nfunction changeHeroName() {\n  const input = document.getElementById(\"heroNameInput\");\n  const newName = input.value.trim();\n  if (!newName) return;\n  gameData.hero.name = newName;\n  input.value = \"\";\n  saveData();\n  updateHeroUI();\n}\nfunction applySkillPassive() {\n  const hero = gameData.hero;\n  hero.maxHp = 50 + (hero.level - 1) * 5;\n  let skillList = &#91;];\n  if (hero.job === \"\u6226\u58eb\") skillList = gameData.skills.warrior;\n  if (hero.job === \"\u9b54\u6cd5\u4f7f\u3044\") skillList = gameData.skills.mage;\n  if (hero.job === \"\u76d7\u8cca\") skillList = gameData.skills.thief;\n  skillList.forEach(s => {\n    if (s.name === \"\u4f53\u529b\u5897\u5f37\") {\n      hero.maxHp += (s.level * 10);\n    }\n  });\n  if (hero.hp > hero.maxHp) hero.hp = hero.maxHp;\n}\n\n\/* =========================================\n   4) \u30d1\u30fc\u30c6\u30a3\u7ba1\u7406\n========================================= *\/\nfunction addPartyMember() {\n  if (gameData.party.length >= 2) {\n    alert(\"\u4ef2\u9593\u306f\u6700\u59272\u4eba\u307e\u3067\u3067\u3059\u3002\");\n    return;\n  }\n  const newMem = {\n    id: Date.now(),\n    name: `\u4ef2\u9593${gameData.party.length + 1}`,\n    level: 1,\n    xp: 0,\n    hp: 40,\n    maxHp: 40,\n    speed: 5,\n    attack: 5,\n    equipment: { weapon: null, armor: null, accessory: null },\n    activeSkills: &#91;\n      \/\/ \u4f8b: \u4ef2\u9593\u5c02\u7528\u30b9\u30ad\u30eb\u3092\u5165\u308c\u305f\u3051\u308c\u3070\u3053\u3053\u306b\n    ]\n  };\n  gameData.party.push(newMem);\n  alert(`${newMem.name} \u3092\u96c7\u3044\u307e\u3057\u305f\uff01`);\n  saveData();\n  updateAllUI();\n}\n\nfunction updatePartyUI() {\n  const container = document.getElementById(\"partyList\");\n  container.innerHTML = \"\";\n  if (gameData.party.length === 0) {\n    container.innerHTML = \"&lt;p>\u4ef2\u9593\u306f\u3044\u307e\u305b\u3093&lt;\/p>\";\n    return;\n  }\n  gameData.party.forEach(m => {\n    const div = document.createElement(\"div\");\n    div.className = \"party-member\";\n    div.innerHTML = `&lt;p>${m.name} (Lv.${m.level}) HP:${m.hp}\/${m.maxHp} \u653b:${m.attack}&lt;\/p>`;\n    const leaveBtn = document.createElement(\"button\");\n    leaveBtn.className = \"btn-delete\";\n    leaveBtn.textContent = \"\u96e2\u8131\";\n    leaveBtn.onclick = () => {\n      if (confirm(`${m.name}\u3092\u5916\u3057\u307e\u3059\u304b\uff1f`)) {\n        gameData.party = gameData.party.filter(x => x.id !== m.id);\n        saveData();\n        updateAllUI();\n      }\n    };\n    div.appendChild(leaveBtn);\n    container.appendChild(div);\n  });\n}\n\nfunction distributePartyExp(amount) {\n  gameData.party.forEach(m => {\n    m.xp += amount;\n    while (m.xp >= m.level * 50) {\n      m.xp -= m.level * 50;\n      m.level++;\n      m.attack += 2;\n      m.maxHp += 5;\n      m.hp += 5;\n    }\n  });\n}\n\n\/* =========================================\n   5) \u8077\u696d &amp; \u30b9\u30ad\u30eb\u30c4\u30ea\u30fc\n========================================= *\/\nfunction setJob(job) {\n  gameData.hero.job = job;\n  alert(`\u8077\u696d\u3092\u300c${job}\u300d\u306b\u5909\u66f4\u3057\u307e\u3057\u305f\u3002`);\n  saveData();\n  applySkillPassive();\n  updateAllUI();\n}\nfunction updateSkillTreeUI() {\n  const container = document.getElementById(\"skillTree\");\n  container.innerHTML = \"\";\n  const job = gameData.hero.job;\n  if (job === \"\u672a\u8a2d\u5b9a\") {\n    container.innerHTML = \"&lt;p>\u8077\u696d\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044&lt;\/p>\";\n    return;\n  }\n  let skillList = &#91;];\n  if (job === \"\u6226\u58eb\") skillList = gameData.skills.warrior;\n  if (job === \"\u9b54\u6cd5\u4f7f\u3044\") skillList = gameData.skills.mage;\n  if (job === \"\u76d7\u8cca\") skillList = gameData.skills.thief;\n\n  skillList.forEach(s => {\n    const skillDiv = document.createElement(\"div\");\n    skillDiv.className = \"skill\";\n    skillDiv.innerHTML = `\n      &lt;h4>${s.name} (Lv.${s.level}\/${s.maxLevel})&lt;\/h4>\n      &lt;p>${s.desc}&lt;\/p>\n    `;\n    const btn = document.createElement(\"button\");\n    btn.className = \"btn-primary\";\n    if (s.level >= s.maxLevel) {\n      btn.textContent = \"MAX\";\n      btn.disabled = true;\n    } else {\n      btn.textContent = \"\u5f37\u5316\";\n      btn.onclick = () => {\n        if (gameData.hero.skillPts &lt;= 0) {\n          alert(\"\u30b9\u30ad\u30eb\u30dd\u30a4\u30f3\u30c8\u304c\u8db3\u308a\u307e\u305b\u3093\");\n          return;\n        }\n        s.level++;\n        gameData.hero.skillPts--;\n        saveData();\n        applySkillPassive();\n        updateAllUI();\n      };\n    }\n    skillDiv.appendChild(btn);\n    container.appendChild(skillDiv);\n  });\n}\n\n\/* =========================================\n   6) \u88c5\u5099 &amp; \u30af\u30e9\u30d5\u30c8(\u7c21\u6613\u5316)\n========================================= *\/\nfunction updateHeroEquipmentUI() {\n  const eqDiv = document.getElementById(\"heroEquipment\");\n  const h = gameData.hero;\n  eqDiv.innerHTML = `\n    &lt;p>\u6b66\u5668: ${h.equipment.weapon ? h.equipment.weapon.name : \"\u306a\u3057\"}&lt;\/p>\n    &lt;p>\u9632\u5177: ${h.equipment.armor ? h.equipment.armor.name : \"\u306a\u3057\"}&lt;\/p>\n    &lt;p>\u30a2\u30af\u30bb: ${h.equipment.accessory ? h.equipment.accessory.name : \"\u306a\u3057\"}&lt;\/p>\n    &lt;p>\u7d20\u6750: \u6728\u6750(${h.materials.wood}), \u9271\u77f3(${h.materials.ore}), \u7d50\u6676(${h.materials.magicCrystal})&lt;\/p>\n    &lt;p>\u56de\u5fa9\u85ac: ${h.consumables.potion || 0}\u500b&lt;\/p>\n  `;\n}\nfunction updateCraftUI() {\n  const cDiv = document.getElementById(\"craftContainer\");\n  cDiv.innerHTML = \"\";\n  gameData.craftingRecipes.forEach(r => {\n    const div = document.createElement(\"div\");\n    div.className = \"craft-recipe\";\n    let matText = Object.keys(r.materialsRequired).map(m => {\n      return `${m}:${r.materialsRequired&#91;m]}`;\n    }).join(\", \");\n    div.innerHTML = `\n      &lt;strong>${r.result.name}&lt;\/strong> \u2192 \u5fc5\u8981\u7d20\u6750 ${matText}&lt;br>\n      ${r.desc}\n    `;\n    const btn = document.createElement(\"button\");\n    btn.textContent = \"\u30af\u30e9\u30d5\u30c8\";\n    btn.onclick = () => craftItem(r);\n    div.appendChild(btn);\n    cDiv.appendChild(div);\n  });\n}\nfunction craftItem(recipe) {\n  for (let mat in recipe.materialsRequired) {\n    if ((gameData.hero.materials&#91;mat] || 0) &lt; recipe.materialsRequired&#91;mat]) {\n      alert(\"\u7d20\u6750\u304c\u8db3\u308a\u307e\u305b\u3093\uff01\");\n      return;\n    }\n  }\n  for (let mat in recipe.materialsRequired) {\n    gameData.hero.materials&#91;mat] -= recipe.materialsRequired&#91;mat];\n  }\n  if (recipe.result.type === \"consumable\") {\n    const key = recipe.result.itemKey;\n    gameData.hero.consumables&#91;key] = (gameData.hero.consumables&#91;key] || 0) + recipe.result.amount;\n    alert(`${recipe.result.name} \u3092${recipe.result.amount}\u500b\u3001\u4f5c\u6210\u3057\u307e\u3057\u305f\uff01`);\n  }\n  saveData();\n  updateAllUI();\n}\n\n\/* =========================================\n   7) \u30af\u30a8\u30b9\u30c8\u95a2\u9023 (\u30c7\u30a4\u30ea\u30fc\/\u901a\u5e38\/\u30a6\u30a3\u30fc\u30af\u30ea\u30fc)\n========================================= *\/\nfunction switchQuestTab(tab) {\n  document.getElementById(\"dailyQuests\").style.display = (tab === \"daily\") ? \"\" : \"none\";\n  document.getElementById(\"normalQuests\").style.display = (tab === \"normal\") ? \"\" : \"none\";\n  document.getElementById(\"weeklyQuests\").style.display = (tab === \"weekly\") ? \"\" : \"none\";\n}\nfunction updateQuestsUI() {\n  renderQuestList(\"dailyQuests\", gameData.dailyQuests, completeDailyQuest);\n  renderQuestList(\"normalQuests\", gameData.normalQuests, completeNormalQuest);\n  renderQuestList(\"weeklyQuests\", gameData.weeklyQuests, completeWeeklyQuest);\n}\nfunction renderQuestList(containerId, questArr, completeFn) {\n  const container = document.getElementById(containerId);\n  container.innerHTML = \"\";\n  questArr.forEach(q => {\n    const div = document.createElement(\"div\");\n    div.className = \"quest\";\n    const h4 = document.createElement(\"h4\");\n    h4.textContent = q.completed ? `\u3010\u9054\u6210\u6e08\u3011${q.title}` : q.title;\n    const p = document.createElement(\"p\");\n    p.textContent = `EXP:${q.exp} Gold:${q.gold}`;\n    const btn = document.createElement(\"button\");\n    btn.className = \"btn-primary\";\n    if (q.completed) {\n      btn.textContent = \"\u5b8c\u4e86\u6e08\";\n      btn.disabled = true;\n    } else {\n      btn.textContent = \"\u9054\u6210\";\n      btn.onclick = () => completeFn(q.id);\n    }\n    div.appendChild(h4);\n    div.appendChild(p);\n    div.appendChild(btn);\n    container.appendChild(div);\n  });\n}\nfunction completeDailyQuest(id) {\n  const q = gameData.dailyQuests.find(x => x.id === id);\n  if (!q || q.completed) return;\n  q.completed = true;\n  alert(`${q.title} \u3092\u9054\u6210\uff01\\nEXP+${q.exp}, Gold+${q.gold}`);\n  gainExp(q.exp);\n  gainGold(q.gold);\n  saveData();\n  updateAllUI();\n}\nfunction completeNormalQuest(id) {\n  const q = gameData.normalQuests.find(x => x.id === id);\n  if (!q || q.completed) return;\n  q.completed = true;\n  alert(`${q.title} \u3092\u9054\u6210\uff01\\nEXP+${q.exp}, Gold+${q.gold}`);\n  gainExp(q.exp);\n  gainGold(q.gold);\n  saveData();\n  updateAllUI();\n}\nfunction completeWeeklyQuest(id) {\n  const q = gameData.weeklyQuests.find(x => x.id === id);\n  if (!q || q.completed) return;\n  q.completed = true;\n  alert(`${q.title} \u3092\u9054\u6210\uff01\\nEXP+${q.exp}, Gold+${q.gold}`);\n  gainExp(q.exp);\n  gainGold(q.gold);\n  saveData();\n  updateAllUI();\n}\nfunction dailyQuestResetCheck() {\n  const now = new Date();\n  const todayStr = now.toDateString();\n  if (gameData.lastDailyReset !== todayStr) {\n    gameData.dailyQuests.forEach(q => q.completed = false);\n    gameData.lastDailyReset = todayStr;\n    alert(\"\u30c7\u30a4\u30ea\u30fc\u30af\u30a8\u30b9\u30c8\u3092\u30ea\u30bb\u30c3\u30c8\u3057\u307e\u3057\u305f\uff01\");\n    saveData();\n  }\n}\nfunction weeklyQuestResetCheck() {\n  const now = new Date();\n  const year = now.getFullYear();\n  const weekNum = Math.floor((now.getDate() - now.getDay() + 10) \/ 7);\n  const currentWeekKey = `${year}-W${weekNum}`;\n  if (gameData.lastWeeklyReset !== currentWeekKey) {\n    gameData.weeklyQuests.forEach(q => q.completed = false);\n    gameData.lastWeeklyReset = currentWeekKey;\n    alert(\"\u30a6\u30a3\u30fc\u30af\u30ea\u30fc\u30af\u30a8\u30b9\u30c8\u3092\u30ea\u30bb\u30c3\u30c8\u3057\u307e\u3057\u305f\uff01\");\n    saveData();\n  }\n}\n\n\/* =========================================\n   8) \u30ef\u30fc\u30eb\u30c9\u30de\u30c3\u30d7 &amp; \u30dc\u30b9(\u8907\u6570\u30bf\u30fc\u30f3\u5236)\n========================================= *\/\nfunction updateMapUI() {\n  const container = document.getElementById(\"mapAreaContainer\");\n  container.innerHTML = \"\";\n  const heroLevel = gameData.hero.level;\n  gameData.mapAreas.forEach(area => {\n    const areaDiv = document.createElement(\"div\");\n    areaDiv.className = \"map-area\";\n    if (heroLevel &lt; area.levelReq) {\n      areaDiv.classList.add(\"locked-area\");\n    }\n    areaDiv.innerHTML = `&lt;h3>${area.name} (Lv.${area.levelReq}\uff5e)&lt;\/h3>&lt;p>${area.desc}&lt;\/p>`;\n    if (area.boss &amp;&amp; heroLevel >= area.levelReq) {\n      const btn = document.createElement(\"button\");\n      btn.className = \"btn-primary\";\n      btn.textContent = `${area.boss.name} \u3068\u6226\u3046`;\n      btn.onclick = () => {\n        \/\/ \u30dc\u30b9\u3082 enemies\u914d\u5217\u6271\u3044\n        const boss = JSON.parse(JSON.stringify(area.boss));\n        startBattle(&#91;boss], `\u30dc\u30b9\u6226: ${boss.name}`, true);\n      };\n      areaDiv.appendChild(btn);\n    }\n    container.appendChild(areaDiv);\n  });\n}\n\n\/* =========================================\n   9) \u30e9\u30f3\u30c0\u30e0\u30a8\u30f3\u30ab\u30a6\u30f3\u30c8 (\u8907\u6570\u6575\u5bfe\u5fdc)\n========================================= *\/\nfunction startRandomEncounter() {\n  \/\/ \u30e9\u30f3\u30c0\u30e0\u306b1\uff5e2\u4f53\u306e\u6575\u3092\u51fa\u3059\n  const n = Math.random() &lt; 0.5 ? 1 : 2;\n  let chosenEnemies = &#91;];\n  for (let i = 0; i &lt; n; i++) {\n    const e = gameData.enemies&#91;Math.floor(Math.random() * gameData.enemies.length)];\n    chosenEnemies.push(JSON.parse(JSON.stringify(e)));\n  }\n  startBattle(chosenEnemies, \"\u30c0\u30f3\u30b8\u30e7\u30f3\u6f5c\u5165 - \u8907\u6570\u6575\u304c\u51fa\u73fe\uff01\");\n}\n\n\/* =========================================\n   10) \u500b\u5225\u30bf\u30fc\u30f3\u5236\u30d0\u30c8\u30eb\u30ed\u30b8\u30c3\u30af\n========================================= *\/\nlet battleState = {\n  combatants: &#91;], \/\/ \u5168\u30e6\u30cb\u30c3\u30c8(\u52c7\u8005\/\u4ef2\u9593\/\u6575)\n  turnIndex: 0,\n  log: &#91;],\n  battleOver: false,\n  isBossFight: false\n};\n\nfunction startBattle(enemyList, title, isBoss=false) {\n  battleState.combatants = &#91;];\n  battleState.turnIndex = 0;\n  battleState.log = &#91;];\n  battleState.battleOver = false;\n  battleState.isBossFight = isBoss;\n\n  \/\/ \u5473\u65b9(\u52c7\u8005)\n  const hero = gameData.hero;\n  battleState.combatants.push({\n    unitType: \"hero\",\n    name: hero.name,\n    hp: hero.hp,\n    maxHp: hero.maxHp,\n    speed: hero.speed,\n    isDown: false,\n    ref: hero,\n    activeSkills: hero.activeSkills || &#91;]\n  });\n  \/\/ \u4ef2\u9593\n  gameData.party.forEach(m => {\n    battleState.combatants.push({\n      unitType: \"party\",\n      name: m.name,\n      hp: m.hp,\n      maxHp: m.maxHp,\n      speed: m.speed || 5,\n      isDown: false,\n      ref: m,\n      activeSkills: m.activeSkills || &#91;]\n    });\n  });\n  \/\/ \u6575\n  enemyList.forEach(e => {\n    battleState.combatants.push({\n      unitType: \"enemy\",\n      name: e.name,\n      hp: e.hp,\n      maxHp: e.maxHp,\n      speed: e.speed || 5,\n      atk: e.atk || 5,\n      exp: e.exp || 0,\n      gold: e.gold || 0,\n      dropMat: e.dropMat || null,\n      rewardExp: e.rewardExp,\n      rewardGold: e.rewardGold,\n      isDown: false\n    });\n  });\n\n  \/\/ \u30bd\u30fc\u30c8(\u901f\u5ea6\u964d\u9806)\n  battleState.combatants.sort((a,b) => b.speed - a.speed);\n\n  document.getElementById(\"battleTitle\").textContent = title;\n  document.getElementById(\"battleDesc\").textContent = isBoss ? \"\u30dc\u30b9\u6226\u3060\uff01\" : \"\u6575\u304c\u73fe\u308c\u305f\uff01\";\n  openBattleModal();\n  updateBattleUI();\n  battleState.log.push(\"\u30d0\u30c8\u30eb\u958b\u59cb\uff01\");\n  \/\/ \u884c\u52d5\u8005\u30c1\u30a7\u30c3\u30af\n  checkCurrentTurn();\n}\n\nfunction openBattleModal() {\n  document.getElementById(\"battleModalBg\").style.display = \"flex\";\n}\nfunction closeBattleModal() {\n  document.getElementById(\"battleModalBg\").style.display = \"none\";\n  updateAllUI();\n}\nfunction updateBattleUI() {\n  \/\/ \u6575\u4e00\u89a7\n  let enemyText = \"&lt;h3>\u6575\u30e6\u30cb\u30c3\u30c8&lt;\/h3>\";\n  battleState.combatants\n    .filter(c => c.unitType === \"enemy\")\n    .forEach(c => {\n      if (!c.isDown) {\n        enemyText += `&lt;p>${c.name} HP:${c.hp}\/${c.maxHp}&lt;\/p>`;\n      } else {\n        enemyText += `&lt;p>${c.name} (\u6483\u7834)&lt;\/p>`;\n      }\n    });\n  document.getElementById(\"battleEnemyInfo\").innerHTML = enemyText;\n\n  \/\/ \u5473\u65b9\u4e00\u89a7\n  let allyText = \"&lt;h3>\u5473\u65b9\u30e6\u30cb\u30c3\u30c8&lt;\/h3>\";\n  battleState.combatants\n    .filter(c => c.unitType===\"hero\" || c.unitType===\"party\")\n    .forEach(c => {\n      if (!c.isDown) {\n        allyText += `&lt;p>${c.name} HP:${c.hp}\/${c.maxHp}&lt;\/p>`;\n      } else {\n        allyText += `&lt;p>${c.name} (\u6226\u95d8\u4e0d\u80fd)&lt;\/p>`;\n      }\n    });\n  document.getElementById(\"battleHeroInfo\").innerHTML = allyText;\n\n  \/\/ \u30ed\u30b0\n  document.getElementById(\"battleLog\").innerHTML = battleState.log.join(\"&lt;br>\");\n}\n\n\/* \u884c\u52d5\u8005\u78ba\u8a8d *\/\nfunction checkCurrentTurn() {\n  if (battleState.battleOver) return;\n  if (battleState.turnIndex >= battleState.combatants.length) {\n    battleState.turnIndex = 0;\n  }\n  let currentUnit = battleState.combatants&#91;battleState.turnIndex];\n  if (currentUnit.isDown) {\n    \/\/ \u6b21\u3078\n    battleState.turnIndex++;\n    checkBattleEnd();\n    checkCurrentTurn();\n    return;\n  }\n\n  if (currentUnit.unitType === \"hero\" || currentUnit.unitType === \"party\") {\n    \/\/ \u30d7\u30ec\u30a4\u30e4\u30fc(\u5473\u65b9)\u884c\u52d5\n    battleState.log.push(`&#91;${currentUnit.name} \u306e\u30bf\u30fc\u30f3]`);\n    updateBattleUI();\n    \/\/ \u30dc\u30bf\u30f3\u64cd\u4f5c\u3067\u884c\u52d5\u3092\u5f85\u3064\n  } else {\n    \/\/ \u6575\u884c\u52d5\n    battleState.log.push(`&#91;${currentUnit.name} \u306e\u30bf\u30fc\u30f3(\u6575)]`);\n    updateBattleUI();\n    setTimeout(() => {\n      enemyAction(currentUnit);\n    }, 600);\n  }\n}\n\n\/* \u30d7\u30ec\u30a4\u30e4\u30fc\u884c\u52d5\u7cfb *\/\n\/\/ \u4eca\u3069\u306e\u30ad\u30e3\u30e9\u304c\u884c\u52d5\u4e2d\u304b\nfunction getCurrentUnit() {\n  if (battleState.turnIndex &lt; battleState.combatants.length) {\n    return battleState.combatants&#91;battleState.turnIndex];\n  }\n  return null;\n}\n\n\/\/ \u653b\u6483\nfunction chooseAttack() {\n  const currentUnit = getCurrentUnit();\n  if (!currentUnit) return;\n  if (currentUnit.unitType===\"enemy\") return; \/\/ \u6575\u306f\u81ea\u52d5\u884c\u52d5\n\n  \/\/ \u30bf\u30fc\u30b2\u30c3\u30c8: \u751f\u5b58\u3057\u3066\u3044\u308b\u6575\n  const aliveEnemies = battleState.combatants.filter(c => c.unitType===\"enemy\" &amp;&amp; !c.isDown);\n  if (aliveEnemies.length === 0) return; \/\/ \u6575\u304c\u3044\u306a\u3044\n  const target = aliveEnemies&#91;0]; \/\/ \u4eee\u306b\u5148\u982d\u3092\u653b\u6483(\u672c\u5f53\u306f\u9078\u629eUI\u3092\u7528\u610f\u3057\u3066\u3082OK)\n\n  doAttack(currentUnit, target);\n}\nfunction doAttack(attacker, defender) {\n  battleState.log.push(`${attacker.name}\u306e\u653b\u6483\uff01`);\n  const dmg = calcDamage(attacker, defender, false);\n  defender.hp -= dmg;\n  battleState.log.push(`\u2192 ${defender.name} \u306b ${dmg} \u30c0\u30e1\u30fc\u30b8\uff01`);\n  if (defender.hp&lt;=0) {\n    defender.hp=0;\n    defender.isDown=true;\n    battleState.log.push(`${defender.name}\u306f\u5012\u308c\u305f\uff01`);\n  }\n  endPlayerAction();\n}\n\n\/\/ \u30b9\u30ad\u30eb\nfunction chooseSkill() {\n  const currentUnit = getCurrentUnit();\n  if (!currentUnit) return;\n  if (currentUnit.activeSkills.length === 0) {\n    alert(\"\u30b9\u30ad\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\uff01\");\n    return;\n  }\n  \/\/ \u4f8b: 1\u3064\u3060\u3051\u30b9\u30ad\u30eb\u304c\u3042\u308b\u306a\u3089\u5373\u4f7f\u7528 or prompt\u3067\u9078\u629e\n  const skill = currentUnit.activeSkills&#91;0];\n  \/\/ \u30bf\u30fc\u30b2\u30c3\u30c8\u9078\u629e(\u6575)\n  const aliveEnemies = battleState.combatants.filter(c => c.unitType===\"enemy\" &amp;&amp; !c.isDown);\n  if (aliveEnemies.length === 0) {\n    alert(\"\u6575\u304c\u3044\u307e\u305b\u3093\");\n    return;\n  }\n  const target = aliveEnemies&#91;0]; \/\/ \u7c21\u6613: \u5148\u982d\n  battleState.log.push(`${currentUnit.name}\u306f${skill.name}\u3092\u767a\u52d5\uff01`);\n  const dmg = skill.baseDamage + Math.floor(Math.random()*3);\n  target.hp -= dmg;\n  battleState.log.push(`\u2192 ${target.name} \u306b ${dmg} \u30c0\u30e1\u30fc\u30b8\uff01`);\n  if (target.hp&lt;=0) {\n    target.hp=0;\n    target.isDown=true;\n    battleState.log.push(`${target.name}\u306f\u5012\u308c\u305f\uff01`);\n  }\n  endPlayerAction();\n}\n\n\/\/ \u30a2\u30a4\u30c6\u30e0\nfunction chooseItem() {\n  const currentUnit = getCurrentUnit();\n  if (!currentUnit) return;\n  if (currentUnit.unitType!==\"hero\") {\n    alert(\"\u3053\u306e\u30ad\u30e3\u30e9\u306f\u30a2\u30a4\u30c6\u30e0\u3092\u4f7f\u3048\u307e\u305b\u3093\u3002\");\n    return;\n  }\n  const hero = currentUnit.ref;\n  if ((hero.consumables.potion || 0) &lt;1) {\n    alert(\"\u56de\u5fa9\u85ac\u304c\u3042\u308a\u307e\u305b\u3093\u3002\");\n    return;\n  }\n  \/\/ \u5bfe\u8c61\u3092\u81ea\u5206\u306b\u9650\u5b9a(\u7c21\u6613)\n  hero.consumables.potion--;\n  const heal=30;\n  currentUnit.hp += heal;\n  if(currentUnit.hp>currentUnit.maxHp) currentUnit.hp=currentUnit.maxHp;\n  battleState.log.push(`${currentUnit.name}\u306f\u56de\u5fa9\u85ac\u3092\u4f7f\u7528 \u2192 HP+${heal}`);\n  endPlayerAction();\n}\n\n\/\/ \u9632\u5fa1\nfunction chooseDefend() {\n  const currentUnit = getCurrentUnit();\n  if (!currentUnit) return;\n  currentUnit.isDefending=true; \/\/ \u88ab\u30c0\u30e1\u534a\u6e1b\u306a\u3069\n  battleState.log.push(`${currentUnit.name}\u306f\u8eab\u3092\u5b88\u3063\u3066\u3044\u308b\uff01(\u88ab\u30c0\u30e1\u8efd\u6e1b)`);\n  endPlayerAction();\n}\n\n\/\/ \u9003\u3052\u308b\nfunction chooseFlee() {\n  battleState.log.push(\"\u9003\u3052\u51fa\u3057\u305f\uff01\");\n  endBattle(false);\n}\n\n\/\/ \u30d7\u30ec\u30a4\u30e4\u30fc\u884c\u52d5\u7d42\u4e86\nfunction endPlayerAction() {\n  battleState.turnIndex++;\n  checkBattleEnd();\n  setTimeout(() => {\n    checkCurrentTurn();\n    updateBattleUI();\n  }, 400);\n}\n\n\/\/ \u6575\u884c\u52d5\nfunction enemyAction(enemyUnit) {\n  \/\/ \u30bf\u30fc\u30b2\u30c3\u30c8: \u751f\u5b58\u3057\u3066\u3044\u308b\u5473\u65b9( hero\/party )\n  const aliveAllies = battleState.combatants.filter(c => (c.unitType===\"hero\"||c.unitType===\"party\") &amp;&amp; !c.isDown);\n  if (aliveAllies.length===0) {\n    checkBattleEnd();\n    return;\n  }\n  const target = aliveAllies&#91;Math.floor(Math.random()*aliveAllies.length)];\n  battleState.log.push(`${enemyUnit.name}\u306e\u653b\u6483 \u2192 ${target.name}`);\n  const dmg = calcDamage(enemyUnit, target, target.isDefending);\n  target.hp -= dmg;\n  if (target.isDefending) {\n    battleState.log.push(\"(\u9632\u5fa1\u4e2d\u3067\u88ab\u30c0\u30e1\u8efd\u6e1b)\");\n  }\n  battleState.log.push(`\u2192 ${target.name}\u306b${dmg}\u30c0\u30e1\u30fc\u30b8\uff01`);\n  target.isDefending=false; \/\/ \u9632\u5fa1\u306f1\u30bf\u30fc\u30f3\u306e\u307f\n\n  if (target.hp&lt;=0) {\n    target.hp=0;\n    target.isDown=true;\n    battleState.log.push(`${target.name}\u306f\u5012\u308c\u305f\u2026`);\n  }\n\n  battleState.turnIndex++;\n  checkBattleEnd();\n  setTimeout(() => {\n    checkCurrentTurn();\n    updateBattleUI();\n  }, 400);\n}\n\n\/* \u30c0\u30e1\u30fc\u30b8\u8a08\u7b97 *\/\nfunction calcDamage(attacker, defender, defenderIsDefending) {\n  let baseAtk=0;\n  if (attacker.unitType===\"hero\"||attacker.unitType===\"party\") {\n    \/\/ \u5473\u65b9\u306e\u653b\u6483\u529b\n    \/\/ (\u88c5\u5099\u3084\u8077\u696d\u30b9\u30ad\u30eb\u306a\u3069\u306f\u672a\u7d30\u5206\u5316\u3002\u4e0b\u306ecalcPlayerTotalAttack\u3092\u7c21\u6613\u6d41\u7528\u3067\u3082OK)\n    baseAtk=10;\n    if(attacker.ref &amp;&amp; attacker.ref.job===\"\u6226\u58eb\") {\n      const wSkills=gameData.skills.warrior;\n      wSkills.forEach(s => {\n        if(s.name===\"\u5263\u8853\u719f\u7df4\") baseAtk+=(s.level*2);\n      });\n    }\n    if(attacker.ref &amp;&amp; attacker.ref.job===\"\u9b54\u6cd5\u4f7f\u3044\") {\n      const mSkills=gameData.skills.mage;\n      mSkills.forEach(s => {\n        if(s.name===\"\u9b54\u529b\u5897\u5f37\") baseAtk+=(s.level*3);\n        if(s.name===\"\u7cbe\u795e\u96c6\u4e2d\" &amp;&amp; battleState.isBossFight) baseAtk+=(s.level*5);\n      });\n    }\n    if(attacker.ref &amp;&amp; attacker.ref.job===\"\u76d7\u8cca\") {\n      const tSkills=gameData.skills.thief;\n      tSkills.forEach(s=>{\n        if(s.name===\"\u7d20\u65e9\u3055\u5f37\u5316\") baseAtk+=(s.level*2);\n      });\n    }\n    \/\/ \u88c5\u5099\n    if(attacker.ref &amp;&amp; attacker.ref.equipment.weapon) {\n      baseAtk += (attacker.ref.equipment.weapon.attack||0);\n    }\n    if(attacker.ref &amp;&amp; attacker.ref.equipment.armor) {\n      baseAtk += (attacker.ref.equipment.armor.attack||0);\n    }\n    if(attacker.ref &amp;&amp; attacker.ref.equipment.accessory) {\n      baseAtk += (attacker.ref.equipment.accessory.attack||0);\n    }\n    \/\/ \u3055\u3089\u306b\u4ef2\u9593\u306e\u5834\u5408\u306fattack\u30d7\u30ed\u30d1\u30c6\u30a3\uff1f\n    if(attacker.unitType===\"party\") {\n      baseAtk += attacker.ref.attack; \/\/ m.attack\n    }\n  } else {\n    \/\/ \u6575\n    baseAtk=attacker.atk||5;\n  }\n  \/\/ \u9632\u5fa1\u4e2d\u306a\u3089\u534a\u6e1b\n  let finalDmg = baseAtk + Math.floor(Math.random()*3);\n  if(defenderIsDefending) {\n    finalDmg = Math.floor(finalDmg\/2);\n  }\n  return finalDmg;\n}\n\n\/* \u52dd\u5229\/\u6557\u5317\u5224\u5b9a *\/\nfunction checkBattleEnd() {\n  \/\/ \u5473\u65b9\u751f\u5b58\n  const aliveAllies = battleState.combatants.filter(c => (c.unitType===\"hero\"||c.unitType===\"party\") &amp;&amp; !c.isDown);\n  if(aliveAllies.length===0) {\n    \/\/ \u6557\u5317\n    battleState.log.push(\"\u5473\u65b9\u306f\u5168\u6ec5\u3057\u305f\u2026\");\n    doLoseBattle();\n    return true;\n  }\n  \/\/ \u6575\u751f\u5b58\n  const aliveEnemies= battleState.combatants.filter(c => c.unitType===\"enemy\" &amp;&amp; !c.isDown);\n  if(aliveEnemies.length===0) {\n    \/\/ \u52dd\u5229\n    battleState.log.push(\"\u6575\u3092\u5168\u3066\u5012\u3057\u305f\uff01 \u52dd\u5229\uff01\");\n    doWinBattle();\n    return true;\n  }\n  return false;\n}\n\nfunction doWinBattle() {\n  battleState.battleOver=true;\n  \/\/ \u30dc\u30b9 or \u96d1\u9b5a\u6575\u5168\u4f53\u306eEXP\/Gold\n  let totalExp=0, totalGold=0;\n  battleState.combatants.forEach(c=>{\n    if(c.unitType===\"enemy\") {\n      if(c.rewardExp) totalExp += c.rewardExp; else totalExp += (c.exp||0);\n      if(c.rewardGold) totalGold += c.rewardGold; else totalGold += (c.gold||0);\n    }\n  });\n  \/\/ \u76d7\u8cca\u30b9\u30ad\u30eb\u3067Gold\u30dc\u30fc\u30ca\u30b9\n  if(gameData.hero.job===\"\u76d7\u8cca\"){\n    const tSkills=gameData.skills.thief;\n    tSkills.forEach(s=>{\n      if(s.name===\"\u30b4\u30fc\u30eb\u30c9\u76d7\u307f\") {\n        totalGold+=(s.level*5);\n      }\n    });\n  }\n  gainExp(totalExp);\n  gainGold(totalGold);\n  battleState.log.push(`\u5831\u916c \u2192 EXP:${totalExp}, Gold:${totalGold}`);\n\n  \/\/ \u5012\u308c\u305f\u5473\u65b9\u3092HP1\u3067\u5fa9\u5e30\n  battleState.combatants.forEach(c=>{\n    if((c.unitType===\"hero\"||c.unitType===\"party\") &amp;&amp; c.isDown){\n      c.isDown=false; c.hp=1;\n      if(c.ref) c.ref.hp=1;\n    } else {\n      if(c.ref) c.ref.hp=c.hp; \/\/ HP\u3092\u53cd\u6620\n    }\n  });\n  setTimeout(()=>{\n    endBattle(true);\n  },800);\n}\n\nfunction doLoseBattle() {\n  battleState.battleOver=true;\n  \/\/ \u30b4\u30fc\u30eb\u30c9\u534a\u6e1b\n  gameData.hero.gold = Math.floor(gameData.hero.gold\/2);\n  battleState.log.push(\"\u6240\u6301Gold\u304c\u534a\u5206\u306b\u306a\u3063\u305f\u2026\");\n  \/\/ \u5168\u54e1HP1\u3067\u5fa9\u5e30\n  battleState.combatants.forEach(c=>{\n    if(c.unitType===\"hero\"||c.unitType===\"party\"){\n      c.isDown=false;\n      c.hp=1;\n      if(c.ref) c.ref.hp=1;\n    }\n  });\n  setTimeout(()=>{\n    endBattle(false);\n  },800);\n}\n\nfunction endBattle(win) {\n  battleState.log.push(win ? \"(\u52dd\u5229) \u30d0\u30c8\u30eb\u7d42\u4e86\" : \"(\u7d42\u4e86) \u30d0\u30c8\u30eb\u7d42\u4e86\");\n  updateBattleUI();\n  setTimeout(()=>{\n    closeBattleModal();\n  },1000);\n}\n\nfunction calcPlayerTotalAttack(){\n  \/\/ \u203b \u65e7\u306e\u4e00\u6589\u653b\u6483\u8a08\u7b97\u7528\u306f\u4f7f\u308f\u306a\u304f\u306a\u3063\u305f\u304c\u3001\u53c2\u8003\u306b\u6b8b\u3057\u3066\u304a\u304f\n  return 10;\n}\n\n\/* \u30dc\u30b9\u6483\u7834\u6642\u306e\u5b9f\u7e3e &amp; \u30b9\u30c8\u30fc\u30ea\u30fc *\/\nfunction onBossDefeated(bossName) {\n  gameData.achievements.forEach(a => {\n    if(a.type===\"bossKill\" &amp;&amp; a.bossName===bossName &amp;&amp; !a.unlocked){\n      unlockAchievement(a);\n    }\n  });\n  const st= gameData.story.find(x=>x.bossName===bossName);\n  if(st){\n    battleState.log.push(st.text);\n  }\n  saveData();\n}\n\n\/* =========================================\n   11) \u5b9f\u7e3e &amp; \u30b9\u30c8\u30fc\u30ea\u30fc\n========================================= *\/\nfunction updateAchievementsUI() {\n  const listEl = document.getElementById(\"achievementList\");\n  listEl.innerHTML = \"\";\n  gameData.achievements.forEach(a => {\n    const div = document.createElement(\"div\");\n    div.className = \"achievement\";\n    if(!a.unlocked) div.classList.add(\"locked\");\n    const h4 = document.createElement(\"h4\");\n    h4.textContent = a.title;\n    const p = document.createElement(\"p\");\n    p.textContent = a.desc;\n    if(!a.unlocked){\n      const lockedLabel=document.createElement(\"div\");\n      lockedLabel.className=\"locked-label\";\n      lockedLabel.textContent=\"Locked\";\n      div.appendChild(lockedLabel);\n    }\n    div.appendChild(h4);\n    div.appendChild(p);\n    listEl.appendChild(div);\n  });\n}\nfunction checkAchievements() {\n  const hero=gameData.hero;\n  const totalQuestCompleted= gameData.dailyQuests.filter(q=>q.completed).length\n    + gameData.normalQuests.filter(q=>q.completed).length\n    + gameData.weeklyQuests.filter(q=>q.completed).length;\n  gameData.achievements.forEach(a=>{\n    if(a.unlocked) return;\n    switch(a.type){\n      case \"questCount\":\n        if(totalQuestCompleted>=a.target){\n          unlockAchievement(a);\n        }\n        break;\n      case \"level\":\n        if(hero.level>=a.target){\n          unlockAchievement(a);\n        }\n        break;\n      case \"bossKill\":\n        \/\/ boss\u8a0e\u4f10\u6642\u306b individually check\n        break;\n      case \"gold\":\n        if(hero.gold>=a.target){\n          unlockAchievement(a);\n        }\n        break;\n    }\n  });\n}\nfunction unlockAchievement(a) {\n  a.unlocked=true;\n  alert(`\u5b9f\u7e3e\u89e3\u9664\uff01\u300c${a.title}\u300d`);\n  saveData();\n  updateAchievementsUI();\n}\nfunction updateStoryProgress(){\n  let text=\"\";\n  const bossKills= gameData.achievements.filter(a=>a.type===\"bossKill\"&amp;&amp;a.unlocked).map(a=>a.bossName);\n  bossKills.forEach(bn=>{\n    const st=gameData.story.find(x=>x.bossName===bn);\n    if(st){\n      text+=`&lt;p>\u3010${bn}\u3011&lt;br>${st.text}&lt;\/p>`;\n    }\n  });\n  if(!text) text=\"&lt;p>\u307e\u3060\u5927\u304d\u306a\u30b9\u30c8\u30fc\u30ea\u30fc\u306f\u9032\u3093\u3067\u3044\u307e\u305b\u3093\u3002&lt;\/p>\";\n  document.getElementById(\"storyProgress\").innerHTML=text;\n}\n\n\/* =========================================\n   12) UI\u306e\u4e00\u62ec\u66f4\u65b0\n========================================= *\/\nfunction updateAllUI(){\n  updateHeroUI();\n  updatePartyUI();\n  updateSkillTreeUI();\n  updateCraftUI();\n  updateQuestsUI();\n  updateMapUI();\n  updateAchievementsUI();\n  updateStoryProgress();\n}\n&lt;\/script>\n&lt;\/body>\n&lt;\/html>\n<\/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":[80,87,96,44],"tags":[],"class_list":{"0":"post-25882","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"hentry","6":"category-html","7":"category-web","9":"category-44"},"aioseo_notices":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/25882","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=25882"}],"version-history":[{"count":1,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/25882\/revisions"}],"predecessor-version":[{"id":25883,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/25882\/revisions\/25883"}],"wp:attachment":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=25882"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=25882"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=25882"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}