Untitled

 avatar
unknown
plain_text
9 months ago
11 kB
16
Indexable
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mini "Pokémon Yellow" Inspired Demo</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<style>
  html,body{height:100%;margin:0;font-family:Arial,Helvetica,sans-serif;background:#f0f4f8}
  .wrap{display:flex;gap:16px;align-items:flex-start;padding:16px}
  canvas{background:#8fc3ff;border:4px solid #333;image-rendering:pixelated}
  .ui{width:360px}
  h1{margin:0 0 8px;font-size:18px}
  .panel{background:#fff;border:1px solid #ccc;padding:8px;border-radius:6px;box-shadow:0 2px 6px rgba(0,0,0,.06)}
  .controls small{color:#666}
  button{padding:6px 10px;border-radius:6px;border:1px solid #888;background:#eee;cursor:pointer}
  .log{height:220px;overflow:auto;font-size:13px;padding:8px;background:#111;color:#0f0;border-radius:4px}
</style>
</head>
<body>
<div class="wrap">
  <div>
    <canvas id="game" width="384" height="384"></canvas>
  </div>
  <div class="ui">
    <div class="panel">
      <h1>Mini Pokémon Yellow — Inspired Demo</h1>
      <p>Move with arrow keys / WASD. Press <strong>Space</strong> to interact. Walk around to find wild encounters.</p>
      <div style="display:flex;gap:8px;margin-top:8px">
        <button id="btnStart">Reset</button>
        <button id="btnBattle">Trigger Battle</button>
      </div>
      <div style="margin-top:10px">
        <strong>Player</strong>: <span id="playerInfo">Level 5</span><br/>
        <strong>Buddy</strong>: <span id="buddyInfo">Pika (happy)</span>
      </div>
    </div>

    <div class="panel" style="margin-top:10px">
      <h1>Battle Log</h1>
      <div class="log" id="log"></div>
    </div>

    <div class="panel" style="margin-top:10px">
      <h1>Notes</h1>
      <ul style="margin:0 0 0 16px;padding:0">
        <li>Everything here is drawable pixels — expand sprites freely.</li>
        <li>Open the file locally in a browser. No server required.</li>
      </ul>
    </div>
  </div>
</div>

<script>
/* -----------------------------
   Tiny "Pokémon Yellow" inspired demo
   - tile-based map (simple)
   - player and buddy follow
   - simple wild battle
   ----------------------------- */

const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const tileSize = 32;
const cols = canvas.width / tileSize;
const rows = canvas.height / tileSize;

let state = {
  player: {x: 6, y: 6, dir: 0, level: 5, hp: 20},
  buddy:  {x: 6, y: 7, mood: 'happy'},
  map: [],
  keys: {},
  inBattle: false,
  wild: null
};

const logEl = document.getElementById('log');
function log(s){ logEl.innerText = s + "\\n" + logEl.innerText; }

// simple map: 0 = grass, 1 = ground, 2 = tree, 3 = water
function generateMap(){
  const m = [];
  for(let y=0;y<rows;y++){
    const row=[];
    for(let x=0;x<cols;x++){
      if (y<2 || y>rows-3 || x<2 || x>cols-3) row.push(2); // border trees
      else {
        // random grass patches
        const r = Math.random();
        if (r < 0.12) row.push(3);
        else if (r < 0.45) row.push(0);
        else row.push(1);
      }
    }
    m.push(row);
  }
  // add a small house (walkable ground with a door)
  for(let y=3;y<6;y++) for(let x=2;x<6;x++) m[y][x]=1;
  m[5][3] = 1; // door area
  return m;
}

function reset(){
  state.map = generateMap();
  state.player.x = 6; state.player.y = 6;
  state.buddy.x = 6; state.buddy.y = 7; state.buddy.mood = 'happy';
  state.inBattle = false; state.wild = null;
  document.getElementById('playerInfo').innerText = `Level ${state.player.level}`;
  document.getElementById('buddyInfo').innerText = `Pika (${state.buddy.mood})`;
  log('Game reset.');
}

reset();

/* --- drawing helpers --- */
function drawTile(t,x,y){
  const px = x*tileSize, py = y*tileSize;
  if (t === 0) { // grass
    ctx.fillStyle = '#7fc97f';
    ctx.fillRect(px,py,tileSize,tileSize);
    // little grass blades
    ctx.fillStyle = '#5aa45a';
    ctx.fillRect(px+6,py+22,2,4);
    ctx.fillRect(px+18,py+20,2,5);
  } else if (t === 1) { // ground
    ctx.fillStyle = '#e6d3a3';
    ctx.fillRect(px,py,tileSize,tileSize);
  } else if (t === 2) { // tree
    ctx.fillStyle = '#2f6b2f';
    ctx.fillRect(px,py, tileSize, tileSize);
    ctx.fillStyle = '#1f471f';
    ctx.fillRect(px+6,py+6,20,10);
  } else if (t === 3) { // water
    ctx.fillStyle = '#4bb0dd';
    ctx.fillRect(px,py,tileSize,tileSize);
    ctx.fillStyle = '#3a97c0';
    ctx.fillRect(px+4,py+18,16,3);
  }
}

/* draw a simple player pixel sprite */
function drawPlayer(x,y){
  const px = x*tileSize, py = y*tileSize;
  // shadow
  ctx.fillStyle = 'rgba(0,0,0,0.15)'; ctx.fillRect(px+8,py+24,16,6);
  // body
  ctx.fillStyle = '#222'; ctx.fillRect(px+12,py+8,8,12);
  // jacket
  ctx.fillStyle = '#c33'; ctx.fillRect(px+10,py+14,12,8);
  // head
  ctx.fillStyle = '#ffd79c'; ctx.fillRect(px+12,py+4,8,8);
}

/* buddy drawn as a tiny yellow creature */
function drawBuddy(x,y){
  const px = x*tileSize, py = y*tileSize;
  ctx.fillStyle = 'rgba(0,0,0,0.12)';
  ctx.fillRect(px+10,py+26,12,4);
  // yellow body
  ctx.fillStyle = '#ffd400'; ctx.fillRect(px+10,py+10,12,12);
  // ears
  ctx.fillRect(px+9,py+6,3,6);
  ctx.fillRect(px+18,py+6,3,6);
  // cheeks
  ctx.fillStyle = '#ff6b6b'; ctx.fillRect(px+11,py+16,3,3); ctx.fillRect(px+18,py+16,3,3);
  // eyes
  ctx.fillStyle = '#111'; ctx.fillRect(px+13,py+12,2,2); ctx.fillRect(px+17,py+12,2,2);
}

/* --- input --- */
window.addEventListener('keydown', e => {
  state.keys[e.key.toLowerCase()] = true;
  if (e.key === ' ') { e.preventDefault(); interact(); }
});
window.addEventListener('keyup', e => state.keys[e.key.toLowerCase()] = false);

/* --- simple collision: trees and water block movement --- */
function canWalk(x,y){
  if (x<0||x>=cols||y<0||y>=rows) return false;
  const t = state.map[y][x];
  return t === 0 || t === 1; // grass or ground are walkable
}

/* --- movement & buddy follow --- */
let moveCooldown = 0;
function update(dt){
  if (state.inBattle) return; // freeze world when in battle
  moveCooldown -= dt;
  if (moveCooldown <= 0){
    let dx=0, dy=0;
    if (state.keys['arrowup']||state.keys['w']) dy=-1;
    else if (state.keys['arrowdown']||state.keys['s']) dy=1;
    if (state.keys['arrowleft']||state.keys['a']) dx=-1;
    else if (state.keys['arrowright']||state.keys['d']) dx=1;

    if (dx!==0||dy!==0){
      const nx = state.player.x + dx, ny = state.player.y + dy;
      if (canWalk(nx,ny)){
        state.player.x = nx; state.player.y = ny;
        // buddy tries to follow: move toward player's old position
        moveBuddyToward(state.player.x - dx, state.player.y - dy);
        moveCooldown = 120; // ms between tile moves
        // random encounter on grass
        if (state.map[ny][nx] === 0 && Math.random() < 0.12) {
          triggerWild();
        }
      }
    }
  }
}

/* buddy AI: simple pathing toward target tile (no A* for brevity) */
function moveBuddyToward(targetX, targetY){
  const bx = state.buddy.x, by = state.buddy.y;
  const dx = targetX - bx, dy = targetY - by;
  let moved = false;
  if (Math.abs(dx) > Math.abs(dy)){
    const sx = Math.sign(dx);
    if (canWalk(bx+sx,by)){ state.buddy.x += sx; moved = true; }
  }
  if (!moved && dy !== 0){
    const sy = Math.sign(dy);
    if (canWalk(bx,by+sy)){ state.buddy.y += sy; moved = true; }
  }
  if (!moved){
    // stay put
  }
}

/* --- rendering --- */
function render(){
  ctx.clearRect(0,0,canvas.width,canvas.height);
  // draw map
  for(let y=0;y<rows;y++) for(let x=0;x<cols;x++) drawTile(state.map[y][x],x,y);
  // draw player and buddy. Buddy follows and drawn before player so it may be behind/aside
  drawBuddy(state.buddy.x, state.buddy.y);
  drawPlayer(state.player.x, state.player.y);

  // if in battle, overlay
  if (state.inBattle && state.wild){
    ctx.fillStyle = 'rgba(0,0,0,0.6)';
    ctx.fillRect(0,0,canvas.width,canvas.height);
    // battle box
    ctx.fillStyle = '#fff'; ctx.fillRect(24,24,canvas.width-48,canvas.height-48);
    ctx.strokeStyle = '#333'; ctx.strokeRect(24,24,canvas.width-48,canvas.height-48);
    ctx.fillStyle = '#000'; ctx.font = '14px Arial';
    ctx.fillText(`Wild ${state.wild.name} appeared!`, 40, 60);
    ctx.fillText(`Wild HP: ${state.wild.hp}`, 40, 90);
    ctx.fillText(`Your HP: ${state.player.hp}`, 40, 120);
    ctx.fillText(`Press "A" to Attack, "R" to Run`, 40, 160);
  }
}

/* --- very small battle system --- */
function triggerWild(){
  // generate a tiny wild creature
  const wilds = [
    {name:'Pidgey-like', maxHp:8, atk:2},
    {name:'Rattata-like', maxHp:6, atk:3},
    {name:'Caterpie-like', maxHp:5, atk:1}
  ];
  const pick = wilds[Math.floor(Math.random()*wilds.length)];
  state.wild = {name: pick.name, hp: pick.maxHp, atk: pick.atk, maxHp: pick.maxHp};
  state.inBattle = true;
  log(`A wild ${state.wild.name} appeared!`);
  // update info
  document.getElementById('buddyInfo').innerText = `Pika (alert)`;
}

function battlePlayerAttack(){
  if (!state.inBattle || !state.wild) return;
  // player attack — trivial damage scaling by level
  const dmg = Math.max(1, Math.floor(state.player.level * 0.8));
  state.wild.hp -= dmg;
  log(`You hit wild ${state.wild.name} for ${dmg} damage.`);
  if (state.wild.hp <= 0){
    log(`Wild ${state.wild.name} fainted! You won.`);
    state.inBattle = false; state.wild = null;
    document.getElementById('buddyInfo').innerText = `Pika (happy)`;
  } else {
    // wild attacks back
    const dmg2 = state.wild.atk;
    state.player.hp -= dmg2;
    log(`${state.wild.name} hits you for ${dmg2} damage.`);
    if (state.player.hp <= 0){
      log(`You fainted! Resetting HP.`);
      state.player.hp = Math.max(10, state.player.level * 4);
      state.inBattle = false; state.wild = null;
      document.getElementById('playerInfo').innerText = `Level ${state.player.level}`;
      document.getElementById('buddyInfo').innerText = `Pika (sad)`;
    }
  }
}

/* user interact (space) triggers simple action if in front of player */
function interact(){
  if (state.inBattle) return;
  // check tile in front for "door"
  const fx = state.player.x, fy = state.player.y;
  // for demonstration: stepping on certain ground gives message
  if (state.map[fy][fx] === 1){
    log('This is soft ground. Your buddy naps a little.');
    state.buddy.mood = 'sleepy';
    document.getElementById('buddyInfo').innerText = `Pika (${state.buddy.mood})`;
  } else {
    log('Nothing interesting here.');
  }
}

/* keyboard for battle */
window.addEventListener('keydown', e => {
  if (!state.inBattle) return;
  if (e.key.toLowerCase() === 'a'){ battlePlayerAttack(); }
  if (e.key.toLowerCase() === 'r'){ // run
    if (Math.random() < 0.6){ log('Got away safely!'); state.inBattle=false; state.wild=null; document.getElementById('buddyInfo').innerText = `Pika (relieved)`; }
    else { log('Could not escape!'); const dmg2 = state.wild.atk; state.player.hp -= dmg2; log(`${state.wild.name} hits you for ${dmg2} damage.`); }
  }
});

/* UI buttons */
document.getElementById('btnStart').onclick = reset;
document.getElementById('btnBattle').onclick = () => { if(!state.inBattle) triggerWild(); };

/* game loop */
let last = performance.now();
function loop(now){
  const dt = now - last; last = now;
  update(dt);
  render();
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
</script>
</body>
</html>
Editor is loading...
Leave a Comment