Last active
February 25, 2025 14:42
-
-
Save opparco/21ce5bc98ce4e1453d164d6302de42b1 to your computer and use it in GitHub Desktop.
Dungeon Hack generated by Claude 3.7 Sonnet lang:ja
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="ja"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>ダンジョンハック</title> | |
<style> | |
body { | |
font-family: 'Helvetica Neue', Arial, sans-serif; | |
background-color: #1a1a2e; | |
color: #e6e6ff; | |
margin: 0; | |
padding: 20px; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
min-height: 100vh; | |
} | |
.container { | |
max-width: 800px; | |
width: 100%; | |
} | |
h1 { | |
color: #4db8ff; | |
text-align: center; | |
text-shadow: 0 0 10px rgba(77, 184, 255, 0.5); | |
} | |
.game-area { | |
background-color: #252547; | |
border-radius: 10px; | |
padding: 20px; | |
margin-bottom: 20px; | |
box-shadow: 0 0 15px rgba(77, 184, 255, 0.3); | |
} | |
.character-sheet { | |
display: grid; | |
grid-template-columns: 1fr 1fr; | |
gap: 10px; | |
margin-bottom: 20px; | |
} | |
.status-section, .skills-section { | |
background-color: #333366; | |
padding: 15px; | |
border-radius: 8px; | |
} | |
.dungeon-area { | |
position: relative; | |
min-height: 200px; | |
background-color: #333366; | |
padding: 15px; | |
border-radius: 8px; | |
margin-bottom: 20px; | |
} | |
.action-buttons { | |
display: grid; | |
grid-template-columns: 1fr 1fr; | |
gap: 10px; | |
margin-bottom: 20px; | |
} | |
button { | |
background-color: #4d4dff; | |
color: white; | |
border: none; | |
padding: 10px; | |
border-radius: 5px; | |
cursor: pointer; | |
transition: background-color 0.3s; | |
} | |
button:hover { | |
background-color: #3333cc; | |
} | |
button:disabled { | |
background-color: #666699; | |
cursor: not-allowed; | |
} | |
.log-area { | |
background-color: #1a1a2e; | |
padding: 15px; | |
border-radius: 8px; | |
height: 150px; | |
overflow-y: auto; | |
border: 1px solid #4d4dff; | |
} | |
.log-entry { | |
margin-bottom: 5px; | |
border-bottom: 1px solid #333366; | |
padding-bottom: 5px; | |
} | |
.success { | |
color: #66ff66; | |
} | |
.failure { | |
color: #ff6666; | |
} | |
.critical { | |
color: #ffcc00; | |
font-weight: bold; | |
} | |
.skill-item { | |
display: flex; | |
justify-content: space-between; | |
margin-bottom: 5px; | |
} | |
.stat-item { | |
display: flex; | |
justify-content: space-between; | |
margin-bottom: 8px; | |
} | |
.hp-mp { | |
display: flex; | |
justify-content: space-between; | |
margin-bottom: 15px; | |
} | |
.hp-mp div { | |
flex: 1; | |
text-align: center; | |
} | |
.progress { | |
height: 20px; | |
background-color: #1a1a2e; | |
border-radius: 5px; | |
margin-top: 5px; | |
} | |
.progress-fill-hp { | |
height: 100%; | |
background-color: #ff5050; | |
border-radius: 5px; | |
transition: width 0.3s; | |
} | |
.progress-fill-mp { | |
height: 100%; | |
background-color: #5050ff; | |
border-radius: 5px; | |
transition: width 0.3s; | |
} | |
.monster { | |
margin-bottom: 15px; | |
padding: 10px; | |
background-color: #3d3d5c; | |
border-radius: 5px; | |
} | |
.monster-stats { | |
display: flex; | |
justify-content: space-between; | |
} | |
.monster-hp { | |
margin-top: 5px; | |
} | |
.treasure { | |
background-color: #665600; | |
color: #ffdd00; | |
padding: 10px; | |
border-radius: 5px; | |
margin-bottom: 15px; | |
text-align: center; | |
} | |
.level-display { | |
text-align: center; | |
margin-bottom: 15px; | |
font-size: 1.2em; | |
color: #4db8ff; | |
} | |
.game-over { | |
text-align: center; | |
color: #ff5050; | |
font-size: 1.5em; | |
font-weight: bold; | |
margin: 20px 0; | |
} | |
.levelup-container button { | |
margin: 5px; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>ダンジョンハック</h1> | |
<div class="game-area"> | |
<div class="level-display">ダンジョン階層: <span id="level">1</span></div> | |
<div class="character-sheet"> | |
<div class="status-section"> | |
<h3>ステータス</h3> | |
<div class="hp-mp"> | |
<div> | |
<div>HP: <span id="current-hp">30</span>/<span id="max-hp">30</span></div> | |
<div class="progress"> | |
<div id="hp-bar" class="progress-fill-hp" style="width: 100%"></div> | |
</div> | |
</div> | |
<div> | |
<div>MP: <span id="current-mp">20</span>/<span id="max-mp">20</span></div> | |
<div class="progress"> | |
<div id="mp-bar" class="progress-fill-mp" style="width: 100%"></div> | |
</div> | |
</div> | |
</div> | |
<div class="stat-item"> | |
<div>筋力(STR):</div> | |
<div id="str">3</div> | |
</div> | |
<div class="stat-item"> | |
<div>敏捷(DEX):</div> | |
<div id="dex">3</div> | |
</div> | |
<div class="stat-item"> | |
<div>知力(INT):</div> | |
<div id="int">3</div> | |
</div> | |
<div class="stat-item"> | |
<div>精神(MND):</div> | |
<div id="mnd">3</div> | |
</div> | |
</div> | |
<div class="skills-section"> | |
<h3>特技</h3> | |
<div class="skill-item"> | |
<div>剣術:</div> | |
<div id="sword-skill">1</div> | |
</div> | |
<div class="skill-item"> | |
<div>魔法:</div> | |
<div id="magic-skill">1</div> | |
</div> | |
<div class="skill-item"> | |
<div>罠回避:</div> | |
<div id="trap-skill">1</div> | |
</div> | |
<div class="skill-item"> | |
<div>探索:</div> | |
<div id="search-skill">1</div> | |
</div> | |
<div class="skill-item"> | |
<div>交渉:</div> | |
<div id="negotiation-skill">1</div> | |
</div> | |
<div> | |
<div>経験値: <span id="xp">0</span></div> | |
<div>レベル: <span id="char-level">1</span></div> | |
</div> | |
</div> | |
</div> | |
<div class="dungeon-area" id="dungeon-content"> | |
ダンジョンに入る準備はできましたか? | |
</div> | |
<div class="action-buttons"> | |
<button id="explore-btn">探索する</button> | |
<button id="rest-btn">休憩する</button> | |
<button id="attack-btn" disabled>攻撃</button> | |
<button id="magic-btn" disabled>魔法</button> | |
<button id="negotiate-btn" disabled>交渉</button> | |
<button id="next-level-btn" disabled>次の階層へ</button> | |
</div> | |
<div class="log-area" id="log"> | |
<div class="log-entry">ゲーム開始!ダンジョンの探索を始めましょう。</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// ゲームの状態 | |
const gameState = { | |
character: { | |
hp: 125, | |
maxHp: 125, | |
mp: 50, | |
maxMp: 50, | |
stats: { | |
str: 5, | |
dex: 1, | |
int: 1, | |
mnd: 3 | |
}, | |
skills: { | |
sword: 2, | |
magic: 1, | |
trap: 1, | |
search: 2, | |
negotiation: 2 | |
}, | |
xp: 0, | |
level: 1, | |
statPoints: 0, | |
skillPoints: 0 | |
}, | |
dungeon: { | |
level: 1, | |
enemyPresent: false, | |
treasurePresent: false, | |
currentEnemy: null, | |
clearedRooms: 0, | |
roomsToNextLevel: 5 | |
} | |
}; | |
// モンスターのテンプレート | |
const monsterTemplates = [ | |
{ name: "ゴブリン", hp: 15, attack: 3, defense: 1, xp: 5 }, | |
{ name: "スケルトン", hp: 20, attack: 4, defense: 2, xp: 8 }, | |
{ name: "オーク", hp: 30, attack: 5, defense: 3, xp: 12 }, | |
{ name: "リザードマン", hp: 25, attack: 6, defense: 2, xp: 10 }, | |
{ name: "ウィスプ", hp: 15, attack: 7, defense: 1, xp: 7 }, | |
{ name: "ダークエルフ", hp: 22, attack: 5, defense: 3, xp: 15 }, | |
{ name: "ミミック", hp: 35, attack: 8, defense: 4, xp: 20 }, | |
{ name: "シャドウ", hp: 28, attack: 6, defense: 2, xp: 18 }, | |
{ name: "ゴーレム", hp: 50, attack: 10, defense: 5, xp: 25 }, | |
{ name: "ワイバーン", hp: 40, attack: 12, defense: 3, xp: 30 } | |
]; | |
// イベントのテンプレート | |
const eventTemplates = [ | |
{ type: "trap", name: "落とし穴", difficulty: 10, damage: 5 }, | |
{ type: "trap", name: "毒矢の罠", difficulty: 12, damage: 8 }, | |
{ type: "trap", name: "爆発の魔法陣", difficulty: 15, damage: 10 }, | |
{ type: "treasure", name: "宝箱", contents: "回復薬", effect: "HP +10" }, | |
{ type: "treasure", name: "魔法の泉", contents: "魔力回復", effect: "MP +10" }, | |
{ type: "treasure", name: "古代の遺物", contents: "経験値", effect: "XP +15" } | |
]; | |
// DOM要素 | |
const dungeonContent = document.getElementById('dungeon-content'); | |
const logArea = document.getElementById('log'); | |
const exploreBtn = document.getElementById('explore-btn'); | |
const restBtn = document.getElementById('rest-btn'); | |
const attackBtn = document.getElementById('attack-btn'); | |
const magicBtn = document.getElementById('magic-btn'); | |
const negotiateBtn = document.getElementById('negotiate-btn'); | |
const nextLevelBtn = document.getElementById('next-level-btn'); | |
// ステータス表示の更新 | |
function updateStats() { | |
document.getElementById('current-hp').textContent = gameState.character.hp; | |
document.getElementById('max-hp').textContent = gameState.character.maxHp; | |
document.getElementById('current-mp').textContent = gameState.character.mp; | |
document.getElementById('max-mp').textContent = gameState.character.maxMp; | |
document.getElementById('hp-bar').style.width = | |
`${(gameState.character.hp / gameState.character.maxHp) * 100}%`; | |
document.getElementById('mp-bar').style.width = | |
`${(gameState.character.mp / gameState.character.maxMp) * 100}%`; | |
document.getElementById('str').textContent = gameState.character.stats.str; | |
document.getElementById('dex').textContent = gameState.character.stats.dex; | |
document.getElementById('int').textContent = gameState.character.stats.int; | |
document.getElementById('mnd').textContent = gameState.character.stats.mnd; | |
document.getElementById('sword-skill').textContent = gameState.character.skills.sword; | |
document.getElementById('magic-skill').textContent = gameState.character.skills.magic; | |
document.getElementById('trap-skill').textContent = gameState.character.skills.trap; | |
document.getElementById('search-skill').textContent = gameState.character.skills.search; | |
document.getElementById('negotiation-skill').textContent = gameState.character.skills.negotiation; | |
document.getElementById('xp').textContent = gameState.character.xp; | |
document.getElementById('char-level').textContent = gameState.character.level; | |
document.getElementById('level').textContent = gameState.dungeon.level; | |
} | |
// ログにエントリを追加 | |
function addLog(text, className = '') { | |
const entry = document.createElement('div'); | |
entry.className = `log-entry ${className}`; | |
entry.textContent = text; | |
logArea.appendChild(entry); | |
logArea.scrollTop = logArea.scrollHeight; | |
} | |
// ダイスロール | |
function rollDice(sides = 10) { | |
return Math.floor(Math.random() * sides) + 1; | |
} | |
// 判定ロール | |
function checkRoll(stat, skill, difficulty) { | |
const roll = rollDice(); | |
const total = stat + skill + roll; | |
return { | |
success: total >= difficulty, | |
critical: roll === 10, | |
fumble: roll === 1, | |
roll: roll, | |
total: total | |
}; | |
} | |
// モンスターの生成 | |
function generateMonster() { | |
const level = gameState.dungeon.level; | |
let template = monsterTemplates[Math.floor(Math.random() * monsterTemplates.length)]; | |
// ダンジョンレベルに応じて強化 | |
const monster = { | |
name: template.name, | |
hp: template.hp + (level - 1) * 5, | |
maxHp: template.hp + (level - 1) * 5, | |
attack: template.attack + Math.floor((level - 1) / 2), | |
defense: template.defense + Math.floor((level - 1) / 3), | |
xp: template.xp + (level - 1) * 3 | |
}; | |
return monster; | |
} | |
// 敵の表示 | |
function showEnemy() { | |
const enemy = gameState.dungeon.currentEnemy; | |
dungeonContent.innerHTML = ` | |
<div class="monster"> | |
<h3>${enemy.name}が現れた!</h3> | |
<div class="monster-stats"> | |
<div>攻撃力: ${enemy.attack}</div> | |
<div>防御力: ${enemy.defense}</div> | |
</div> | |
<div class="monster-hp"> | |
HP: ${enemy.hp}/${enemy.maxHp} | |
<div class="progress"> | |
<div class="progress-fill-hp" style="width: ${(enemy.hp / enemy.maxHp) * 100}%"></div> | |
</div> | |
</div> | |
</div> | |
<p>どうする?</p> | |
`; | |
exploreBtn.disabled = true; | |
restBtn.disabled = true; | |
attackBtn.disabled = false; | |
magicBtn.disabled = false; | |
negotiateBtn.disabled = false; | |
nextLevelBtn.disabled = true; | |
} | |
// 宝物の表示 | |
function showTreasure() { | |
const treasureIndex = Math.floor(Math.random() * 3); | |
let treasure; | |
switch (treasureIndex) { | |
case 0: | |
treasure = { name: "回復薬", effect: "HPが10回復した!" }; | |
gameState.character.hp = Math.min(gameState.character.hp + 10, gameState.character.maxHp); | |
break; | |
case 1: | |
treasure = { name: "魔力の結晶", effect: "MPが10回復した!" }; | |
gameState.character.mp = Math.min(gameState.character.mp + 10, gameState.character.maxMp); | |
break; | |
case 2: | |
const xpGain = 5 + gameState.dungeon.level * 2; | |
treasure = { name: "古代の遺物", effect: `${xpGain}の経験値を獲得した!` }; | |
gainXP(xpGain); | |
break; | |
} | |
dungeonContent.innerHTML = ` | |
<div class="treasure"> | |
<h3>${treasure.name}を見つけた!</h3> | |
<p>${treasure.effect}</p> | |
</div> | |
`; | |
addLog(`${treasure.name}を見つけました!${treasure.effect}`, 'critical'); | |
gameState.dungeon.treasurePresent = false; | |
gameState.dungeon.clearedRooms += 1; | |
exploreBtn.disabled = false; | |
restBtn.disabled = false; | |
attackBtn.disabled = true; | |
magicBtn.disabled = true; | |
negotiateBtn.disabled = true; | |
nextLevelBtn.disabled = gameState.dungeon.clearedRooms < gameState.dungeon.roomsToNextLevel; | |
updateStats(); | |
} | |
// 部屋の表示 | |
function showRoom() { | |
if (gameState.dungeon.enemyPresent) { | |
showEnemy(); | |
} else if (gameState.dungeon.treasurePresent) { | |
showTreasure(); | |
} else { | |
dungeonContent.innerHTML = ` | |
<p>あなたは暗い部屋に立っています。先に進むか、休むか選択してください。</p> | |
`; | |
exploreBtn.disabled = false; | |
restBtn.disabled = false; | |
attackBtn.disabled = true; | |
magicBtn.disabled = true; | |
negotiateBtn.disabled = true; | |
nextLevelBtn.disabled = gameState.dungeon.clearedRooms < gameState.dungeon.roomsToNextLevel; | |
} | |
} | |
// キャラクターのレベルアップ | |
function levelUp() { | |
gameState.character.level += 1; | |
gameState.character.maxHp += 5; | |
gameState.character.hp = gameState.character.maxHp; | |
gameState.character.maxMp += 3; | |
gameState.character.mp = gameState.character.maxMp; | |
gameState.character.statPoints += 1; | |
gameState.character.skillPoints += 2; | |
addLog(`レベルアップ!レベル${gameState.character.level}になりました!`, 'critical'); | |
// レベルアップメニューを表示 | |
showLevelUpMenu(); | |
} | |
// レベルアップメニューを表示 | |
function showLevelUpMenu() { | |
dungeonContent.innerHTML = ` | |
<h3>レベルアップ</h3> | |
<p>ステータスポイント: ${gameState.character.statPoints}</p> | |
<div class="levelup-container"> | |
<button onclick="increaseStat('str')">筋力+</button> | |
<button onclick="increaseStat('dex')">敏捷+</button> | |
<button onclick="increaseStat('int')">知力+</button> | |
<button onclick="increaseStat('mnd')">精神+</button> | |
</div> | |
<p>特技ポイント: ${gameState.character.skillPoints}</p> | |
<div class="levelup-container"> | |
<button onclick="increaseSkill('sword')">剣術+</button> | |
<button onclick="increaseSkill('magic')">魔法+</button> | |
<button onclick="increaseSkill('trap')">罠回避+</button> | |
<button onclick="increaseSkill('search')">探索+</button> | |
<button onclick="increaseSkill('negotiation')">交渉+</button> | |
</div> | |
<button onclick="finishLevelUp()">完了</button> | |
`; | |
exploreBtn.disabled = true; | |
restBtn.disabled = true; | |
attackBtn.disabled = true; | |
magicBtn.disabled = true; | |
negotiateBtn.disabled = true; | |
nextLevelBtn.disabled = true; | |
} | |
// ステータスを上げる | |
window.increaseStat = function(stat) { | |
if (gameState.character.statPoints > 0) { | |
gameState.character.stats[stat] += 1; | |
gameState.character.statPoints -= 1; | |
showLevelUpMenu(); | |
updateStats(); | |
} | |
} | |
// 特技を上げる | |
window.increaseSkill = function(skill) { | |
if (gameState.character.skillPoints > 0) { | |
gameState.character.skills[skill] += 1; | |
gameState.character.skillPoints -= 1; | |
showLevelUpMenu(); | |
updateStats(); | |
} | |
} | |
// レベルアップ完了 | |
window.finishLevelUp = function() { | |
addLog("レベルアップが完了しました!探索を続けましょう。"); | |
showRoom(); | |
} | |
// 経験値の獲得 | |
function gainXP(amount) { | |
gameState.character.xp += amount; | |
addLog(`${amount}の経験値を獲得しました!`, 'success'); | |
// レベルアップの条件 | |
const xpNeeded = gameState.character.level * 20; | |
if (gameState.character.xp >= xpNeeded) { | |
gameState.character.xp -= xpNeeded; | |
levelUp(); | |
} | |
updateStats(); | |
} | |
// 敵を倒した時の処理 | |
function defeatEnemy() { | |
const enemy = gameState.dungeon.currentEnemy; | |
addLog(`${enemy.name}を倒した!`, 'critical'); | |
// 経験値獲得 | |
gainXP(enemy.xp); | |
// 部屋クリア | |
gameState.dungeon.enemyPresent = false; | |
gameState.dungeon.currentEnemy = null; | |
gameState.dungeon.clearedRooms += 1; | |
// 次の階層への進行チェック | |
checkNextLevel(); | |
// 部屋表示更新 | |
showRoom(); | |
} | |
// 次の階層への進行チェック | |
function checkNextLevel() { | |
if (gameState.dungeon.clearedRooms >= gameState.dungeon.roomsToNextLevel) { | |
nextLevelBtn.disabled = false; | |
addLog("次の階層に進むことができます!", 'success'); | |
} | |
} | |
// ゲームオーバー処理 | |
function gameOver() { | |
dungeonContent.innerHTML = ` | |
<div class="game-over">ゲームオーバー</div> | |
<p>あなたは冒険の途中で倒れました。最終ダンジョン階層: ${gameState.dungeon.level}</p> | |
<button onclick="resetGame()">もう一度挑戦する</button> | |
`; | |
exploreBtn.disabled = true; | |
restBtn.disabled = true; | |
attackBtn.disabled = true; | |
magicBtn.disabled = true; | |
negotiateBtn.disabled = true; | |
nextLevelBtn.disabled = true; | |
addLog("ゲームオーバー...", 'failure'); | |
} | |
// ゲームリセット | |
window.resetGame = function() { | |
gameState.character = { | |
hp: 30, | |
maxHp: 30, | |
mp: 20, | |
maxMp: 20, | |
stats: { | |
str: 3, | |
dex: 3, | |
int: 3, | |
mnd: 3 | |
}, | |
skills: { | |
sword: 1, | |
magic: 1, | |
trap: 1, | |
search: 1, | |
negotiation: 1 | |
}, | |
xp: 0, | |
level: 1, | |
statPoints: 0, | |
skillPoints: 0 | |
}; | |
gameState.dungeon = { | |
level: 1, | |
enemyPresent: false, | |
treasurePresent: false, | |
currentEnemy: null, | |
clearedRooms: 0, | |
roomsToNextLevel: 5 | |
}; | |
updateStats(); | |
showRoom(); | |
// ログをクリア | |
logArea.innerHTML = ''; | |
addLog("ゲーム開始!ダンジョンの探索を始めましょう。"); | |
} | |
// 特殊イベント - レアモンスター | |
function spawnRareMonster() { | |
const rareMonster = { | |
name: "古代のガーディアン", | |
hp: 100 + gameState.dungeon.level * 10, | |
maxHp: 100 + gameState.dungeon.level * 10, | |
attack: 15 + gameState.dungeon.level, | |
defense: 8 + Math.floor(gameState.dungeon.level / 2), | |
xp: 50 + gameState.dungeon.level * 5 | |
}; | |
gameState.dungeon.enemyPresent = true; | |
gameState.dungeon.currentEnemy = rareMonster; | |
addLog("床が振動し、壁から光が漏れ出す...", 'critical'); | |
addLog("強大なエネルギーを感じる!", 'critical'); | |
addLog(`${rareMonster.name}が現れた!`, 'critical'); | |
showEnemy(); | |
} | |
// 特殊イベント - 祝福の泉 | |
function blessingFountain() { | |
dungeonContent.innerHTML = ` | |
<div class="treasure"> | |
<h3>祝福の泉を見つけた!</h3> | |
<p>神秘的な力があなたを包み込む...</p> | |
<div class="levelup-container"> | |
<button onclick="chooseFountainEffect('hp')">体力強化</button> | |
<button onclick="chooseFountainEffect('mp')">魔力強化</button> | |
<button onclick="chooseFountainEffect('stat')">能力強化</button> | |
</div> | |
</div> | |
`; | |
exploreBtn.disabled = true; | |
restBtn.disabled = true; | |
attackBtn.disabled = true; | |
magicBtn.disabled = true; | |
negotiateBtn.disabled = true; | |
nextLevelBtn.disabled = true; | |
addLog("祝福の泉を見つけました!特別な力を得ることができます。", 'critical'); | |
} | |
// 祝福の泉の効果選択 | |
window.chooseFountainEffect = function(effect) { | |
switch (effect) { | |
case 'hp': | |
gameState.character.maxHp += 10; | |
gameState.character.hp = gameState.character.maxHp; | |
addLog("体力が強化されました!最大HPが10増加し、HPが全回復しました。", 'success'); | |
break; | |
case 'mp': | |
gameState.character.maxMp += 8; | |
gameState.character.mp = gameState.character.maxMp; | |
addLog("魔力が強化されました!最大MPが8増加し、MPが全回復しました。", 'success'); | |
break; | |
case 'stat': | |
// すべての能力値を+1 | |
gameState.character.stats.str += 1; | |
gameState.character.stats.dex += 1; | |
gameState.character.stats.int += 1; | |
gameState.character.stats.mnd += 1; | |
addLog("能力が強化されました!すべての能力値が1増加しました。", 'success'); | |
break; | |
} | |
updateStats(); | |
gameState.dungeon.clearedRooms += 1; | |
showRoom(); | |
} | |
// 5%の確率で特殊イベントを発生させる関数 | |
function checkSpecialEvent() { | |
if (Math.random() < 0.05) { | |
// 特殊イベントの種類をランダムに選ぶ | |
const eventType = Math.random(); | |
if (eventType < 0.5) { | |
spawnRareMonster(); | |
} else { | |
blessingFountain(); | |
} | |
return true; | |
} | |
return false; | |
} | |
// 攻撃ボタンの処理 | |
attackBtn.addEventListener('click', function() { | |
if (!gameState.dungeon.enemyPresent) return; | |
const enemy = gameState.dungeon.currentEnemy; | |
// プレイヤーの攻撃 | |
const attackResult = checkRoll( | |
gameState.character.stats.str, | |
gameState.character.skills.sword, | |
10 | |
); | |
if (attackResult.critical) { | |
const damage = gameState.character.stats.str * 2 + gameState.character.skills.sword * 2 - enemy.defense; | |
enemy.hp = Math.max(0, enemy.hp - damage); | |
addLog(`クリティカルヒット!${enemy.name}に${damage}ダメージ!`, 'critical'); | |
} else if (attackResult.success) { | |
const damage = Math.max(1, gameState.character.stats.str + gameState.character.skills.sword - enemy.defense); | |
enemy.hp = Math.max(0, enemy.hp - damage); | |
addLog(`攻撃成功!${enemy.name}に${damage}ダメージ!`, 'success'); | |
} else if (attackResult.fumble) { | |
addLog(`ファンブル!攻撃が大きく外れた!`, 'failure'); | |
} else { | |
addLog(`攻撃が外れた!`, 'failure'); | |
} | |
// 敵の反撃(敵がまだ生きていれば) | |
if (enemy.hp > 0) { | |
const defenseResult = checkRoll( | |
gameState.character.stats.dex, | |
gameState.character.skills.sword, | |
10 | |
); | |
if (defenseResult.success) { | |
addLog(`${enemy.name}の攻撃を回避した!`, 'success'); | |
} else { | |
const damage = Math.max(1, enemy.attack - Math.floor(gameState.character.stats.str / 2)); | |
gameState.character.hp = Math.max(0, gameState.character.hp - damage); | |
addLog(`${enemy.name}の攻撃で${damage}ダメージを受けた!`, 'failure'); | |
if (gameState.character.hp <= 0) { | |
gameOver(); | |
return; | |
} | |
} | |
} else { | |
// 敵を倒した | |
defeatEnemy(); | |
return; | |
} | |
updateStats(); | |
showEnemy(); | |
}); | |
// 魔法ボタンの処理 | |
magicBtn.addEventListener('click', function() { | |
if (!gameState.dungeon.enemyPresent) return; | |
const mpCost = 5; | |
if (gameState.character.mp < mpCost) { | |
addLog("MPが足りません!", 'failure'); | |
return; | |
} | |
gameState.character.mp -= mpCost; | |
const enemy = gameState.dungeon.currentEnemy; | |
// 魔法攻撃 | |
const magicResult = checkRoll( | |
gameState.character.stats.int, | |
gameState.character.skills.magic, | |
10 | |
); | |
if (magicResult.critical) { | |
const damage = gameState.character.stats.int * 2 + gameState.character.skills.magic * 2; | |
enemy.hp = Math.max(0, enemy.hp - damage); | |
addLog(`クリティカル魔法!${enemy.name}に${damage}ダメージ!`, 'critical'); | |
} else if (magicResult.success) { | |
const damage = gameState.character.stats.int + gameState.character.skills.magic; | |
enemy.hp = Math.max(0, enemy.hp - damage); | |
addLog(`魔法攻撃が命中!${enemy.name}に${damage}ダメージ!`, 'success'); | |
} else if (magicResult.fumble) { | |
const damage = Math.floor(gameState.character.stats.int / 2); | |
gameState.character.hp = Math.max(0, gameState.character.hp - damage); | |
addLog(`ファンブル!魔法が暴走して自分に${damage}ダメージ!`, 'failure'); | |
if (gameState.character.hp <= 0) { | |
gameOver(); | |
return; | |
} | |
} else { | |
addLog(`魔法が失敗した!`, 'failure'); | |
} | |
// 敵の反撃(敵がまだ生きていれば) | |
if (enemy.hp > 0) { | |
const defenseResult = checkRoll( | |
gameState.character.stats.dex, | |
gameState.character.skills.trap, | |
10 | |
); | |
if (defenseResult.success) { | |
addLog(`${enemy.name}の攻撃を回避した!`, 'success'); | |
} else { | |
const damage = Math.max(1, enemy.attack - Math.floor(gameState.character.stats.int / 3)); | |
gameState.character.hp = Math.max(0, gameState.character.hp - damage); | |
addLog(`${enemy.name}の攻撃で${damage}ダメージを受けた!`, 'failure'); | |
if (gameState.character.hp <= 0) { | |
gameOver(); | |
return; | |
} | |
} | |
} else { | |
// 敵を倒した | |
defeatEnemy(); | |
return; | |
} | |
updateStats(); | |
showEnemy(); | |
}); | |
// 交渉ボタンの処理 | |
negotiateBtn.addEventListener('click', function() { | |
if (!gameState.dungeon.enemyPresent) return; | |
const enemy = gameState.dungeon.currentEnemy; | |
// 交渉判定 | |
const negotiationResult = checkRoll( | |
gameState.character.stats.mnd, | |
gameState.character.skills.negotiation, | |
10 + Math.floor(enemy.attack / 2) | |
); | |
if (negotiationResult.critical) { | |
// 完全成功 | |
addLog(`クリティカル交渉!${enemy.name}が味方になった!`, 'critical'); | |
// ボーナス経験値 | |
gainXP(enemy.xp / 2); | |
// 部屋クリア | |
gameState.dungeon.enemyPresent = false; | |
gameState.dungeon.currentEnemy = null; | |
gameState.dungeon.clearedRooms += 1; | |
// 次の階層への進行チェック | |
checkNextLevel(); | |
// 部屋表示更新 | |
showRoom(); | |
} else if (negotiationResult.success) { | |
// 成功 | |
addLog(`交渉成功!${enemy.name}は去っていった。`, 'success'); | |
// 少しだけ経験値 | |
gainXP(Math.floor(enemy.xp / 4)); | |
// 部屋クリア | |
gameState.dungeon.enemyPresent = false; | |
gameState.dungeon.currentEnemy = null; | |
gameState.dungeon.clearedRooms += 1; | |
// 次の階層への進行チェック | |
checkNextLevel(); | |
// 部屋表示更新 | |
showRoom(); | |
} else { | |
// 失敗 | |
addLog(`交渉失敗!${enemy.name}は怒っている!`, 'failure'); | |
// 敵の攻撃 | |
const damage = Math.max(1, enemy.attack - Math.floor(gameState.character.stats.mnd / 3)); | |
gameState.character.hp = Math.max(0, gameState.character.hp - damage); | |
addLog(`${enemy.name}の攻撃で${damage}ダメージを受けた!`, 'failure'); | |
if (gameState.character.hp <= 0) { | |
gameOver(); | |
return; | |
} | |
updateStats(); | |
showEnemy(); | |
} | |
}); | |
// 探索ボタンの処理 | |
exploreBtn.addEventListener('click', function() { | |
// 特殊イベントのチェック | |
if (checkSpecialEvent()) { | |
return; | |
} | |
// 罠のチェック | |
const trapCheck = Math.random() < 0.2; | |
if (trapCheck) { | |
const trapIndex = Math.floor(Math.random() * 3); | |
let trap; | |
switch (trapIndex) { | |
case 0: | |
trap = { name: "落とし穴", difficulty: 8 + gameState.dungeon.level, damage: 3 + gameState.dungeon.level }; | |
break; | |
case 1: | |
trap = { name: "毒矢の罠", difficulty: 10 + gameState.dungeon.level, damage: 5 + gameState.dungeon.level }; | |
break; | |
case 2: | |
trap = { name: "爆発の魔法陣", difficulty: 12 + gameState.dungeon.level, damage: 8 + gameState.dungeon.level }; | |
break; | |
} | |
addLog(`${trap.name}を発見しました!罠回避の判定が必要です。`); | |
const result = checkRoll( | |
gameState.character.stats.dex, | |
gameState.character.skills.trap, | |
trap.difficulty | |
); | |
if (result.success) { | |
addLog(`罠回避判定成功!罠を回避しました。(${result.roll}を出して合計${result.total})`, 'success'); | |
} else { | |
const damage = trap.damage; | |
gameState.character.hp = Math.max(0, gameState.character.hp - damage); | |
addLog(`罠回避判定失敗!${damage}ダメージを受けました。(${result.roll}を出して合計${result.total})`, 'failure'); | |
if (gameState.character.hp <= 0) { | |
gameOver(); | |
return; | |
} | |
} | |
updateStats(); | |
} | |
// 部屋の内容を決定 | |
const roomType = Math.random(); | |
if (roomType < 0.6) { // 60%の確率で敵 | |
gameState.dungeon.enemyPresent = true; | |
gameState.dungeon.currentEnemy = generateMonster(); | |
addLog(`${gameState.dungeon.currentEnemy.name}が現れた!`); | |
showEnemy(); | |
} else if (roomType < 0.9) { // 30%の確率で宝物 | |
gameState.dungeon.treasurePresent = true; | |
showTreasure(); | |
} else { // 10%の確率で何もなし | |
addLog("何もない部屋だった。"); | |
gameState.dungeon.clearedRooms += 1; | |
showRoom(); | |
} | |
// 次の階層への進行チェック | |
checkNextLevel(); | |
}); | |
// 休憩ボタンの処理 | |
restBtn.addEventListener('click', function() { | |
const hpRecover = Math.floor(gameState.character.maxHp * 0.2); | |
const mpRecover = Math.floor(gameState.character.maxMp * 0.2); | |
gameState.character.hp = Math.min(gameState.character.hp + hpRecover, gameState.character.maxHp); | |
gameState.character.mp = Math.min(gameState.character.mp + mpRecover, gameState.character.maxMp); | |
addLog(`休憩しました。HPが${hpRecover}、MPが${mpRecover}回復しました。`, 'success'); | |
updateStats(); | |
// 休憩中に敵が現れる確率 | |
if (Math.random() < 0.3) { | |
gameState.dungeon.enemyPresent = true; | |
gameState.dungeon.currentEnemy = generateMonster(); | |
addLog(`休憩中に${gameState.dungeon.currentEnemy.name}に襲われた!`, 'failure'); | |
showEnemy(); | |
} | |
}); | |
// 次の階層へボタンの処理 | |
nextLevelBtn.addEventListener('click', function() { | |
gameState.dungeon.level += 1; | |
gameState.dungeon.clearedRooms = 0; | |
gameState.dungeon.roomsToNextLevel = 5 + gameState.dungeon.level; | |
gameState.dungeon.enemyPresent = false; | |
gameState.dungeon.treasurePresent = false; | |
addLog(`ダンジョン階層${gameState.dungeon.level}に到達しました!`, 'critical'); | |
updateStats(); | |
showRoom(); | |
}); | |
// ゲーム初期化 | |
updateStats(); | |
showRoom(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment