const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

const introOverlay = document.getElementById('introOverlay');
const dialogueOverlay = document.getElementById('dialogueOverlay');
const endingOverlay = document.getElementById('endingOverlay');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const dialogueClose = document.getElementById('dialogueClose');

const dialogueTitle = document.getElementById('dialogueTitle');
const dialogueSpeaker = document.getElementById('dialogueSpeaker');
const dialogueText = document.getElementById('dialogueText');
const choiceButtons = document.getElementById('choiceButtons');
const dialogueActions = document.getElementById('dialogueActions');
const npcPortrait = document.getElementById('npcPortrait');
const npcPlaceholder = document.getElementById('npcPlaceholder');
const npcLabel = document.getElementById('npcLabel');
const statusCount = document.getElementById('statusCount');
const objectiveBox = document.getElementById('objectiveBox');

const images = {};
const imageSources = {
  heroSprite: 'assets/hero_sprite.png',
  heroPortrait: 'assets/hero_portrait.png',
  idolPortrait: 'assets/idol_otaku_portrait.png',
  idolBadge: 'assets/idol_otaku_badge.png',
  audioPortrait: 'assets/audio_otaku_portrait.png',
  audioBadge: 'assets/audio_otaku_badge.png',
  animePortrait: 'assets/anime_otaku_portrait.png',
  animeBadge: 'assets/anime_otaku_badge.png'
};

const WORLD = { width: 1600, height: 1200 };
const VIEW = { width: canvas.width, height: canvas.height };
const PLAYER_SIZE = 42;
const PLAYER_SPEED = 3.2;
const ICON_SIZE = 54;

const roads = {
  v1: { x: 680, y: 0, w: 240, h: 1200 },
  v2: { x: 1120, y: 0, w: 200, h: 1200 },
  h1: { x: 0, y: 180, w: 1600, h: 140 },
  h2: { x: 0, y: 500, w: 1600, h: 140 },
  h3: { x: 0, y: 860, w: 1600, h: 140 }
};

const buildings = [
  { x: 0, y: 0, w: 680, h: 180, label: 'Maid Cafe Row', color: '#f5bfd5' },
  { x: 920, y: 0, w: 200, h: 180, label: 'Station Plaza', color: '#ced2ea' },
  { x: 1320, y: 0, w: 280, h: 180, label: 'Live House Area', color: '#f6d68d' },
  { x: 0, y: 320, w: 680, h: 180, label: 'Electronics Market', color: '#bde6ff' },
  { x: 920, y: 320, w: 200, h: 180, label: 'Figure Tower', color: '#f3c1ff' },
  { x: 1320, y: 320, w: 280, h: 180, label: 'Arcade Block', color: '#ffcba6' },
  { x: 0, y: 640, w: 680, h: 220, label: 'Cafe & Book Zone', color: '#d7e9bc' },
  { x: 920, y: 640, w: 200, h: 220, label: 'Audio Salon', color: '#bfe0d6' },
  { x: 1320, y: 640, w: 280, h: 220, label: 'PC Parts Strip', color: '#bed0ff' },
  { x: 0, y: 1000, w: 680, h: 200, label: 'Anime Specialty Lane', color: '#f0bdd4' },
  { x: 920, y: 1000, w: 200, h: 200, label: 'Retro Hobby', color: '#d2c7f6' },
  { x: 1320, y: 1000, w: 280, h: 200, label: 'Subculture Gate', color: '#ffd4d4' }
];

let npcs = [];
let player;
let companions;
let keys = {};
let camera = { x: 0, y: 0 };
let dialogueState = null;
let gameWon = false;
let started = false;

function makeNpcData() {
  return [
    {
      id: 'maid-cafe', label: 'メイドカフェ', x: 620, y: 250, type: 'hint', icon: '☕', color: '#ff8dc0',
      speaker: 'メイド店員',
      hint: '「アイドルオタクさんは “推しへの愛と節度ある応援” って言葉に弱いですよっ！」\n\nアイドル系の重要キャラは、ライブ愛とマナーを大切にするみたい。'
    },
    {
      id: 'pc-parts', label: 'PCパーツ店', x: 760, y: 430, type: 'hint', icon: '🖥️', color: '#78c7ff',
      speaker: 'パーツ店の常連',
      hint: '「オーディオ派は “高い機材だから良い” よりも “自分の耳で確かめる” を信じる人が多いよ。」'
    },
    {
      id: 'figure-shop', label: 'フィギュア店', x: 1040, y: 250, type: 'hint', icon: '🧸', color: '#d792ff',
      speaker: 'フィギュア店員',
      hint: '「アニメオタクさんには、作品の好きなシーンやキャラ愛を具体的に語ると刺さるかも！」'
    },
    {
      id: 'arcade', label: 'ゲームセンター', x: 1270, y: 430, type: 'hint', icon: '🎮', color: '#ffb268',
      speaker: 'ゲーセン店員',
      hint: '「秋葉原は情報の街！ いろんなお店を回って答えを集めるのが近道だよ。」'
    },
    {
      id: 'ramen', label: 'ラーメン店', x: 820, y: 740, type: 'hint', icon: '🍜', color: '#ffd468',
      speaker: 'ラーメン職人',
      hint: '「腹が減っては転生できぬ！ でも今はまず仲間集めだ。重要キャラはそれぞれの趣味を尊重するといい。」'
    },
    {
      id: 'capsule', label: 'カプセルトイ', x: 1190, y: 930, type: 'hint', icon: '🪀', color: '#85f0be',
      speaker: 'カプセルトイ好き',
      hint: '「重要キャラは3人。全員集めると街のゲートが開くって噂だよ！」'
    },
    {
      id: 'idol-important', label: 'アイドルグッズ館', x: 780, y: 120, type: 'important', icon: '🎤', color: '#ff6fb1',
      speaker: 'アイドルオタク', portrait: 'idolPortrait', badge: 'idolBadge', companionName: 'アイドルオタク',
      intro: '「おっ、キミも現場の熱を感じに来たのかい！？ じゃあ質問だ！\n\nライブ現場で一番大切なのは何だと思う？」',
      success: '「わかってるじゃないか！ 推しへの愛と節度、これぞ最高の現場力！\n\nよし、オレもキミについていく！ アイドルオタク、参戦だ！」',
      choices: [
        { text: '推しへの愛と、まわりへの配慮を忘れない応援', correct: true, response: '正解！ その心意気に感動した！' },
        { text: 'とにかく最前列を奪うこと', correct: false, response: 'うーん、それだけじゃダメだ！ 推し活にはマナーも必要だよ！' },
        { text: 'グッズを一番たくさん買うことだけ', correct: false, response: '熱量は大事だけど、それだけじゃ推し活は語れない！' }
      ]
    },
    {
      id: 'audio-important', label: '高音質オーディオサロン', x: 1190, y: 760, type: 'important', icon: '🎧', color: '#71d7d0',
      speaker: 'オーディオオタク', portrait: 'audioPortrait', badge: 'audioBadge', companionName: 'オーディオオタク',
      intro: '「足を止めたってことは、キミも音に興味があるのかな。では問題。\n\n良い音を語るとき、一番大事なのはどれだ？」',
      success: '「その通り。音はスペックだけじゃなく、自分の耳で味わうものだ。\n\n気に入った！ これからはオレが音の道案内をしよう。」',
      choices: [
        { text: '値段が高い機材を信じること', correct: false, response: '価格は参考にはなるが、答えそのものじゃない。' },
        { text: '自分の耳で違いを確かめ、楽しむこと', correct: true, response: '正解。耳で確かめる、それが一番大切だ。' },
        { text: '見た目が重厚なら音も良いと決めつけること', correct: false, response: '外見だけでは本質はわからない。もっと耳を澄ませてごらん。' }
      ]
    },
    {
      id: 'anime-important', label: 'アニメ専門店', x: 590, y: 930, type: 'important', icon: '📚', color: '#8a8dff',
      speaker: 'アニメオタク', portrait: 'animePortrait', badge: 'animeBadge', companionName: 'アニメオタク',
      intro: '「おお、この店に来るとは見どころがあるね。さっそく質問だよ！\n\n推し作品への愛をいちばん伝えられるのはどれ？」',
      success: '「最高だ！ 好きな場面を具体的に語れる人に悪い人はいない！\n\nボクも仲間になるよ。さあ、次は異世界だ！」',
      choices: [
        { text: 'タイトルだけ大量に並べること', correct: false, response: '量も迫力だけど、愛はもっと具体的な言葉に宿るんだ。' },
        { text: '好きな場面やキャラの魅力を具体的に語ること', correct: true, response: '正解！ それこそ本物の作品愛だよ。' },
        { text: 'ネタバレを気にせず語り尽くすこと', correct: false, response: 'それは危険！ 作品愛には相手への思いやりも必要だよ。' }
      ]
    }
  ];
}

function resetGame() {
  player = {
    x: 800,
    y: 570,
    size: PLAYER_SIZE,
    speed: PLAYER_SPEED,
    moving: false,
    frame: 0,
    frameTimer: 0
  };
  companions = [];
  npcs = makeNpcData().map(n => ({ ...n, visited: false, recruited: false }));
  keys = {};
  camera = { x: 0, y: 0 };
  dialogueState = null;
  gameWon = false;
  updateStatusText();
  objectiveBox.textContent = '重要キャラを探そう！';
  endingOverlay.classList.remove('visible');
  dialogueOverlay.classList.remove('visible');
}

function loadImages() {
  const entries = Object.entries(imageSources);
  return Promise.all(entries.map(([key, src]) => new Promise(resolve => {
    const img = new Image();
    img.onload = () => { images[key] = img; resolve(); };
    img.onerror = () => { console.warn('Image load failed:', src); resolve(); };
    img.src = src;
  })));
}

function clamp(value, min, max) {
  return Math.max(min, Math.min(max, value));
}

function rectsOverlap(a, b) {
  return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
}

function collidesWithBuildings(x, y) {
  const box = { x: x - player.size / 2, y: y - player.size / 2, w: player.size, h: player.size };
  return buildings.some(b => rectsOverlap(box, b));
}

function updatePlayer() {
  if (!started || dialogueState || gameWon) return;

  let dx = 0;
  let dy = 0;
  if (keys['ArrowLeft'] || keys['a'] || keys['A']) dx -= 1;
  if (keys['ArrowRight'] || keys['d'] || keys['D']) dx += 1;
  if (keys['ArrowUp'] || keys['w'] || keys['W']) dy -= 1;
  if (keys['ArrowDown'] || keys['s'] || keys['S']) dy += 1;

  player.moving = dx !== 0 || dy !== 0;

  if (player.moving) {
    const length = Math.hypot(dx, dy) || 1;
    dx = (dx / length) * player.speed;
    dy = (dy / length) * player.speed;

    const nx = clamp(player.x + dx, player.size / 2, WORLD.width - player.size / 2);
    if (!collidesWithBuildings(nx, player.y)) player.x = nx;

    const ny = clamp(player.y + dy, player.size / 2, WORLD.height - player.size / 2);
    if (!collidesWithBuildings(player.x, ny)) player.y = ny;

    player.frameTimer += 1;
    if (player.frameTimer > 10) {
      player.frame = (player.frame + 1) % 4;
      player.frameTimer = 0;
    }
  } else {
    player.frame = 0;
  }

  camera.x = clamp(player.x - VIEW.width / 2, 0, WORLD.width - VIEW.width);
  camera.y = clamp(player.y - VIEW.height / 2, 0, WORLD.height - VIEW.height);

  for (const npc of npcs) {
    if ((npc.visited && npc.type === 'hint') || npc.recruited) continue;
    const dist = Math.hypot(player.x - npc.x, player.y - npc.y);
    if (dist < 42) {
      beginDialogue(npc);
      break;
    }
  }
}

function beginDialogue(npc) {
  dialogueState = { npc, phase: npc.type === 'important' ? 'intro' : 'hint' };
  dialogueOverlay.classList.add('visible');
  renderDialogue();
}

function renderDialogue() {
  if (!dialogueState) return;
  const npc = dialogueState.npc;

  dialogueTitle.textContent = npc.label;
  dialogueSpeaker.textContent = npc.speaker || 'NPC';
  choiceButtons.innerHTML = '';
  dialogueActions.innerHTML = '';

  if (npc.type === 'important') {
    npcPortrait.style.display = 'block';
    npcPortrait.src = imageSources[npc.portrait];
    npcPlaceholder.style.display = 'none';
    npcLabel.textContent = npc.companionName;
  } else {
    npcPortrait.style.display = 'none';
    npcPlaceholder.style.display = 'grid';
    npcPlaceholder.textContent = `${npc.label}\n${npc.icon}`;
    npcLabel.textContent = npc.speaker;
  }

  if (npc.type === 'hint') {
    dialogueText.textContent = npc.hint;
    const okBtn = document.createElement('button');
    okBtn.className = 'primary-btn';
    okBtn.textContent = '会話を終える';
    okBtn.onclick = () => {
      npc.visited = true;
      closeDialogue();
    };
    dialogueActions.appendChild(okBtn);
    return;
  }

  if (dialogueState.phase === 'intro') {
    dialogueText.textContent = npc.intro;
    const nextBtn = document.createElement('button');
    nextBtn.className = 'primary-btn';
    nextBtn.textContent = '答える';
    nextBtn.onclick = () => {
      dialogueState.phase = 'question';
      renderDialogue();
    };
    dialogueActions.appendChild(nextBtn);
    return;
  }

  if (dialogueState.phase === 'question') {
    dialogueText.textContent = '3つの選択肢から、いちばん良い答えを選んでください。';
    npc.choices.forEach(choice => {
      const btn = document.createElement('button');
      btn.className = 'choice-btn';
      btn.textContent = choice.text;
      btn.onclick = () => handleChoice(choice);
      choiceButtons.appendChild(btn);
    });
    return;
  }

  if (dialogueState.phase === 'retry') {
    dialogueText.textContent = dialogueState.feedback;
    const retryBtn = document.createElement('button');
    retryBtn.className = 'secondary-btn';
    retryBtn.textContent = 'もう一度考える';
    retryBtn.onclick = () => {
      dialogueState.phase = 'question';
      renderDialogue();
    };
    dialogueActions.appendChild(retryBtn);
    return;
  }

  if (dialogueState.phase === 'success') {
    dialogueText.textContent = npc.success;
    const joinBtn = document.createElement('button');
    joinBtn.className = 'primary-btn';
    joinBtn.textContent = '仲間になる';
    joinBtn.onclick = () => {
      recruitNpc(npc);
      closeDialogue();
    };
    dialogueActions.appendChild(joinBtn);
  }
}

function handleChoice(choice) {
  if (choice.correct) {
    dialogueState.phase = 'success';
  } else {
    dialogueState.phase = 'retry';
    dialogueState.feedback = choice.response;
  }
  renderDialogue();
}

function recruitNpc(npc) {
  npc.recruited = true;
  if (!companions.find(c => c.id === npc.id)) {
    companions.push({ id: npc.id, name: npc.companionName, badge: npc.badge });
  }
  updateStatusText();

  if (companions.length >= 3) {
    objectiveBox.textContent = '3人そろった！ 異世界ゲートが開く…';
    gameWon = true;
    setTimeout(() => endingOverlay.classList.add('visible'), 450);
  } else {
    objectiveBox.textContent = `${npc.companionName}が仲間になった！ 残り ${3 - companions.length} 人`;
  }
}

function updateStatusText() {
  statusCount.textContent = `仲間 ${companions.length} / 3`;
}

function closeDialogue() {
  dialogueState = null;
  dialogueOverlay.classList.remove('visible');
}

function drawWorld() {
  ctx.clearRect(0, 0, VIEW.width, VIEW.height);

  // Base ground
  ctx.fillStyle = '#e7edf5';
  ctx.fillRect(0, 0, VIEW.width, VIEW.height);

  // Draw buildings first
  buildings.forEach(b => {
    const x = b.x - camera.x;
    const y = b.y - camera.y;
    ctx.fillStyle = b.color;
    roundRect(ctx, x, y, b.w, b.h, 22, true, false);
    ctx.fillStyle = 'rgba(0,0,0,0.08)';
    ctx.fillRect(x + 14, y + 16, b.w - 28, 8);
    ctx.fillStyle = 'rgba(255,255,255,0.35)';
    for (let iy = y + 34; iy < y + b.h - 12; iy += 32) {
      for (let ix = x + 18; ix < x + b.w - 18; ix += 56) {
        ctx.fillRect(ix, iy, 28, 18);
      }
    }
    ctx.fillStyle = 'rgba(8,12,32,0.76)';
    ctx.font = 'bold 20px sans-serif';
    ctx.fillText(b.label, x + 18, y + 28);
  });

  drawRoad(roads.v1, '#969ca9');
  drawRoad(roads.v2, '#8f96a5');
  drawRoad(roads.h1, '#9aa1af');
  drawRoad(roads.h2, '#8f96a5');
  drawRoad(roads.h3, '#9aa1af');

  drawCrosswalk(680, 190, 240, 120, true);
  drawCrosswalk(1120, 190, 200, 120, true);
  drawCrosswalk(680, 510, 240, 120, true);
  drawCrosswalk(1120, 510, 200, 120, true);
  drawCrosswalk(680, 870, 240, 120, true);
  drawCrosswalk(1120, 870, 200, 120, true);

  drawStreetLabel('AKIBA MAIN STREET', 720, 72, '#ffffff', 24);
  drawStreetLabel('ELECTRIC AVE', 40, 580, '#f4f9ff', 22);
  drawStreetLabel('OTAKU AVE', 40, 940, '#f4f9ff', 22);
  drawStreetLabel('SOUND STREET', 1145, 70, '#ffffff', 22);

  npcs.forEach(drawNpc);
  drawPlayer();
  drawCompanions();
}

function drawRoad(r, color) {
  const x = r.x - camera.x;
  const y = r.y - camera.y;
  ctx.fillStyle = color;
  ctx.fillRect(x, y, r.w, r.h);
  ctx.strokeStyle = 'rgba(255,255,255,0.85)';
  ctx.lineWidth = 3;
  ctx.setLineDash([18, 16]);
  if (r.w > r.h) {
    ctx.beginPath();
    ctx.moveTo(x + 14, y + r.h / 2);
    ctx.lineTo(x + r.w - 14, y + r.h / 2);
    ctx.stroke();
  } else {
    ctx.beginPath();
    ctx.moveTo(x + r.w / 2, y + 14);
    ctx.lineTo(x + r.w / 2, y + r.h - 14);
    ctx.stroke();
  }
  ctx.setLineDash([]);
}

function drawCrosswalk(x, y, w, h, vertical) {
  const sx = x - camera.x;
  const sy = y - camera.y;
  ctx.fillStyle = 'rgba(255,255,255,0.9)';
  if (vertical) {
    for (let i = 0; i < 8; i++) {
      ctx.fillRect(sx + 12 + i * 28, sy + 16, 16, h - 32);
    }
  } else {
    for (let i = 0; i < 8; i++) {
      ctx.fillRect(sx + 16, sy + 12 + i * 24, w - 32, 14);
    }
  }
}

function drawStreetLabel(text, x, y, color, size) {
  const sx = x - camera.x;
  const sy = y - camera.y;
  ctx.save();
  ctx.fillStyle = color;
  ctx.globalAlpha = 0.75;
  ctx.font = `bold ${size}px sans-serif`;
  ctx.fillText(text, sx, sy);
  ctx.restore();
}

function drawNpc(npc) {
  if ((npc.type === 'hint' && npc.visited) || npc.recruited) return;
  const x = npc.x - camera.x;
  const y = npc.y - camera.y;

  ctx.save();
  ctx.shadowColor = 'rgba(0,0,0,0.28)';
  ctx.shadowBlur = 12;
  ctx.shadowOffsetY = 4;
  roundRect(ctx, x - ICON_SIZE / 2, y - ICON_SIZE / 2, ICON_SIZE, ICON_SIZE, 16, true, false, npc.color);
  ctx.restore();

  // awning
  ctx.fillStyle = 'rgba(255,255,255,0.92)';
  roundRect(ctx, x - 24, y - 22, 48, 14, 7, true, false);
  ctx.fillStyle = 'rgba(255,255,255,0.24)';
  for (let i = 0; i < 4; i++) {
    ctx.fillRect(x - 22 + i * 12, y - 22, 6, 14);
  }
  ctx.fillStyle = '#1e2545';
  ctx.font = '28px sans-serif';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText(npc.icon, x, y + 7);

  ctx.fillStyle = 'rgba(18,22,38,0.82)';
  roundRect(ctx, x - 62, y + 34, 124, 26, 12, true, false);
  ctx.fillStyle = '#fff';
  ctx.font = 'bold 13px sans-serif';
  ctx.fillText(npc.label, x, y + 47);

  // pulse circle
  const pulse = 10 + Math.sin(Date.now() / 220) * 4;
  ctx.strokeStyle = npc.color;
  ctx.globalAlpha = 0.38;
  ctx.lineWidth = 3;
  ctx.beginPath();
  ctx.arc(x, y, 40 + pulse * 0.2, 0, Math.PI * 2);
  ctx.stroke();
  ctx.globalAlpha = 1;
  ctx.textAlign = 'start';
  ctx.textBaseline = 'alphabetic';
}

function drawPlayer() {
  const x = player.x - camera.x;
  const y = player.y - camera.y;
  const sprite = images.heroSprite;
  if (sprite) {
    const frameWidth = sprite.width / 4;
    const frameHeight = sprite.height;
    const sx = player.frame * frameWidth;
    ctx.drawImage(sprite, sx, 0, frameWidth, frameHeight, x - 28, y - 34, 56, 56);
  } else {
    ctx.fillStyle = '#ff6fb1';
    ctx.beginPath();
    ctx.arc(x, y, 20, 0, Math.PI * 2);
    ctx.fill();
  }

  ctx.fillStyle = 'rgba(0,0,0,0.20)';
  ctx.beginPath();
  ctx.ellipse(x, y + 22, 18, 8, 0, 0, Math.PI * 2);
  ctx.fill();

  ctx.fillStyle = 'rgba(15,18,32,0.88)';
  roundRect(ctx, x - 42, y - 54, 84, 22, 11, true, false);
  ctx.fillStyle = '#fff';
  ctx.font = 'bold 12px sans-serif';
  ctx.textAlign = 'center';
  ctx.fillText('主人公', x, y - 39);
  ctx.textAlign = 'start';
}

function drawCompanions() {
  ctx.save();
  const baseX = 18;
  const baseY = VIEW.height - 86;
  ctx.fillStyle = 'rgba(13,18,35,0.78)';
  roundRect(ctx, 12, VIEW.height - 98, 240, 78, 16, true, false);
  ctx.fillStyle = '#fff';
  ctx.font = 'bold 14px sans-serif';
  ctx.fillText('仲間', 24, VIEW.height - 72);

  companions.forEach((companion, index) => {
    const img = images[companion.badge];
    const x = baseX + index * 42;
    const y = baseY;
    ctx.save();
    ctx.shadowColor = 'rgba(0,0,0,0.35)';
    ctx.shadowBlur = 10;
    ctx.fillStyle = '#ffffff';
    ctx.beginPath();
    ctx.arc(x + 24, y + 24, 26, 0, Math.PI * 2);
    ctx.fill();
    ctx.clip();
    if (img) {
      ctx.drawImage(img, x, y, 48, 48);
    } else {
      ctx.fillStyle = '#ddd';
      ctx.fillRect(x, y, 48, 48);
    }
    ctx.restore();
  });

  // empty slots
  for (let i = companions.length; i < 3; i++) {
    const x = baseX + i * 42;
    const y = baseY;
    ctx.fillStyle = 'rgba(255,255,255,0.08)';
    ctx.beginPath();
    ctx.arc(x + 24, y + 24, 24, 0, Math.PI * 2);
    ctx.fill();
    ctx.strokeStyle = 'rgba(255,255,255,0.15)';
    ctx.stroke();
  }
  ctx.restore();
}

function roundRect(context, x, y, width, height, radius, fill, stroke, fillStyleOverride) {
  if (fillStyleOverride) context.fillStyle = fillStyleOverride;
  const r = Math.min(radius, width / 2, height / 2);
  context.beginPath();
  context.moveTo(x + r, y);
  context.arcTo(x + width, y, x + width, y + height, r);
  context.arcTo(x + width, y + height, x, y + height, r);
  context.arcTo(x, y + height, x, y, r);
  context.arcTo(x, y, x + width, y, r);
  context.closePath();
  if (fill) context.fill();
  if (stroke) context.stroke();
}

function gameLoop() {
  updatePlayer();
  drawWorld();
  requestAnimationFrame(gameLoop);
}

window.addEventListener('keydown', (e) => {
  keys[e.key] = true;
  if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(e.key)) {
    e.preventDefault();
  }
});
window.addEventListener('keyup', (e) => {
  keys[e.key] = false;
});

startButton.addEventListener('click', () => {
  started = true;
  introOverlay.classList.remove('visible');
});

restartButton.addEventListener('click', () => {
  resetGame();
  started = true;
  endingOverlay.classList.remove('visible');
});

dialogueClose.addEventListener('click', () => {
  if (!dialogueState) return;
  if (dialogueState.npc.type === 'hint') {
    dialogueState.npc.visited = true;
  }
  closeDialogue();
});

loadImages().then(() => {
  resetGame();
  gameLoop();
});
