Mario Annotation

by kthornbloom

HTML

<div id="toolbar">
  <strong>Tiles:</strong>
  <button class="tile-btn active" data-type="ground">Ground</button>
  <button class="tile-btn" data-type="enemy1">Enemy 1</button>
  <button class="tile-btn" data-type="enemy2">Enemy 2</button>
  <button class="tile-btn" data-type="enemy3">Enemy 3</button>
  <button class="tile-btn" data-type="powerup">Powerup</button>
  <button class="tile-btn" data-type="goal">Level End</button>
  <button class="tile-btn" data-type="tubeTop">Tube Top</button>
  <button class="tile-btn" data-type="tubeBody">Tube Body</button>
  <button class="tile-btn" data-type="empty">Empty</button>

  <span style="margin-left: 16px;">
    Cols:
    <input type="number" id="colsInput" min="5" max="120" value="40" style="width: 60px;">
    <button id="applyColsBtn">Set</button>
  </span>

  <button id="copyLevelBtn" style="margin-left: 16px;">Copy level as text</button>
  <span id="copyStatus" style="margin-left: 8px; font-size: 0.9em;"></span>
</div>

<br>

<div id="levelGrid"></div>

CSS

body {
  font-family: monospace;
}

#toolbar {
  margin-bottom: 10px;
}

.tile-btn {
  cursor: pointer;
  margin-right: 4px;
  padding: 4px 8px;
  border-radius: 4px;
  border: 1px solid #ccc;
  background: #f5f5f5;
}

.tile-btn.active {
  outline: 2px solid #333;
  background: #e0e0e0;
}

#levelGrid {
  display: inline-block;
  background: #fafafa;
}

.row {
  white-space: nowrap;
  margin: 0;
  padding: 0;
  height: 30px;
}

/* Bigger, visually styled cells */
.cell {
  display: inline-block;
  width: 30px;
  height: 30px;
  box-sizing: border-box;
  border: 1px solid #ddd;
  vertical-align: mirgb(232, 232, 232);
}

.cell:hover {
  outline: 2px solid #999;
  outline-offset: -2px;
}

/* Tile types (display only) */
.cell-empty {
  background: #ffffff;
}

.cell-ground {
  background: #8b4513; /* brown */
}

.cell-enemy1 {
  background: #e74c3c; /* red */
}

.cell-enemy2 {
  background: #9b59b6; /* purple */
}

.cell-enemy3 {
  background: #e67e22; /* orange */
}

.cell-powerup {
  background: #f1c40f; /* yellow */
}

.cell-goal {
  background: #3498db; /* blue */
}

/* Tubes */
.cell-tubeTop {
  background: #27ae60;
  border-bottom: 3px solid #145a32;
}

.cell-tubeBody {
  background: #2ecc71;
}

JavaScript

// ----- CONFIG -----
const ROWS = 10;   // fixed height
let COLS = 40;     // adjustable by input

// Mapping from logical tile type -> export character & CSS class
const tileDefs = {
  ground:   { char: "▦", class: "cell-ground" },
  enemy1:   { char: "▢", class: "cell-enemy1" },
  enemy2:   { char: "▤", class: "cell-enemy2" },
  enemy3:   { char: "▥", class: "cell-enemy3" },
  powerup:  { char: "▣", class: "cell-powerup" },
  goal:     { char: "⚿", class: "cell-goal" },
  tubeTop:  { char: "╦", class: "cell-tubeTop" },
  tubeBody: { char: "║", class: "cell-tubeBody" },
  empty:    { char: " ", class: "cell-empty" }
};

const allTileClasses = Object.values(tileDefs).map(def => def.class);

// ----- STATE -----
let grid = Array.from({ length: ROWS }, () =>
  Array(COLS).fill("empty")
);

const levelGridEl  = document.getElementById("levelGrid");
const tileButtons  = document.querySelectorAll(".tile-btn");
const copyBtn      = document.getElementById("copyLevelBtn");
const copyStatusEl = document.getElementById("copyStatus");
const colsInput    = document.getElementById("colsInput");
const applyColsBtn = document.getElementById("applyColsBtn");

let activeType = "ground"; // default tool
let isMouseDown = false;
const cellEls = []; // [row][col] references if needed

// ----- HELPERS -----

function applyCellStyle(cell, type) {
  cell.classList.remove(...allTileClasses);
  cell.classList.add(tileDefs[type].class);
}

// Build / rebuild the grid DOM from the current grid state
function createGrid() {
  levelGridEl.innerHTML = "";
  cellEls.length = 0;

  for (let r = 0; r < ROWS; r++) {
    const rowDiv = document.createElement("div");
    rowDiv.className = "row";

    const rowCells = [];

    for (let c = 0; c < COLS; c++) {
      const cell = document.createElement("span");
      cell.className = "cell";
      cell.dataset.row = r;
      cell.dataset.col = c;

      const type = grid[r][c];
     ...