initial commit

This commit is contained in:
2026-07-08 12:26:05 -06:00
commit 6885ed5a5b
5 changed files with 633 additions and 0 deletions
+109
View File
@@ -0,0 +1,109 @@
$(function () {
// Positions are 1-indexed to match how the original tool (and the
// in-game name entry screen) describes trainer-name character slots.
// Position 1 is never read by the glitch; it's rendered but inert.
var SPECIES_POSITIONS = [3, 5, 7];
var LEVEL_POSITIONS = [2, 4, 6];
var ROLE_LABEL = {
1: "unused",
2: "level 1",
3: "species 1",
4: "level 2",
5: "species 2",
6: "level 3",
7: "species 3"
};
// Symbols not present on a standard keyboard; everything else usable
// in a name (letters, digits, ( ) : ; [ ] ! - ? . / ,) can just be typed.
var SYMBOL_BUTTONS = ["PK", "MN", "×", "♂", "♀"];
var FALLBACK_SPECIES = "MissingNo.";
var FALLBACK_LEVEL = 127;
var charmap = {};
var $nameRow = $("#name-row");
var $results = $("#results");
for (var i = 1; i <= 7; i++) {
var $slot = $('<div class="slot"></div>');
$slot.append('<div class="index">char ' + i + '</div>');
$slot.append('<div class="role">' + ROLE_LABEL[i] + '</div>');
var $input = $('<input type="text" maxlength="2" autocomplete="off" spellcheck="false">')
.attr("id", "char" + i)
.data("pos", i);
$slot.append($input);
if (i > 1) {
var $symbols = $('<div class="symbols"></div>');
SYMBOL_BUTTONS.forEach(function (sym) {
var $btn = $('<button type="button"></button>').text(sym);
$btn.on("click", function () {
$input.val($(this).text());
decode();
focusNext(i);
});
$symbols.append($btn);
});
$slot.append($symbols);
}
$nameRow.append($slot);
}
$nameRow.on("input", "input", function () {
var pos = $(this).data("pos");
if (this.value.length >= 1) {
focusNext(pos);
}
decode();
});
function focusNext(pos) {
if (pos < 7) {
$("#char" + (pos + 1)).trigger("focus");
}
}
function lookup(token) {
if (!token) return null;
return charmap[token] || null;
}
function decode() {
var chars = [];
for (var i = 1; i <= 7; i++) {
chars[i] = $("#char" + i).val();
}
$results.empty();
for (var pair = 0; pair < 3; pair++) {
var speciesPos = SPECIES_POSITIONS[pair];
var levelPos = LEVEL_POSITIONS[pair];
var speciesEntry = lookup(chars[speciesPos]);
var levelEntry = lookup(chars[levelPos]);
var species = speciesEntry ? speciesEntry.pokemon : FALLBACK_SPECIES;
var level = levelEntry ? levelEntry.decimal : FALLBACK_LEVEL;
var $card = $('<div class="encounter"></div>');
$card.append('<div class="from">from char ' + speciesPos + ' &amp; char ' + levelPos + '</div>');
$card.append('<div class="species">' + escapeHtml(species) + '</div>');
$card.append('<div class="level">Level ' + level + '</div>');
$results.append($card);
}
}
function escapeHtml(str) {
return $("<div>").text(str).html();
}
$.getJSON("data/charmap.json")
.done(function (data) {
charmap = data;
decode();
})
.fail(function () {
$("#load-error").show();
});
});
+318
View File
@@ -0,0 +1,318 @@
{
"A": {
"decimal": 128,
"pokemon": "Golduck"
},
"B": {
"decimal": 129,
"pokemon": "Hypno"
},
"C": {
"decimal": 130,
"pokemon": "Golbat"
},
"D": {
"decimal": 131,
"pokemon": "Mewtwo"
},
"E": {
"decimal": 132,
"pokemon": "Snorlax"
},
"F": {
"decimal": 133,
"pokemon": "Magikarp"
},
"G": {
"decimal": 134,
"pokemon": "MissingNo."
},
"H": {
"decimal": 135,
"pokemon": "MissingNo."
},
"I": {
"decimal": 136,
"pokemon": "Muk"
},
"J": {
"decimal": 137,
"pokemon": "MissingNo."
},
"K": {
"decimal": 138,
"pokemon": "Kingler"
},
"L": {
"decimal": 139,
"pokemon": "Cloyster"
},
"M": {
"decimal": 140,
"pokemon": "MissingNo."
},
"N": {
"decimal": 141,
"pokemon": "Electrode"
},
"O": {
"decimal": 142,
"pokemon": "Clefable"
},
"P": {
"decimal": 143,
"pokemon": "Weezing"
},
"Q": {
"decimal": 144,
"pokemon": "Persian"
},
"R": {
"decimal": 145,
"pokemon": "Marowak"
},
"S": {
"decimal": 146,
"pokemon": "MissingNo."
},
"T": {
"decimal": 147,
"pokemon": "Haunter"
},
"U": {
"decimal": 148,
"pokemon": "Abra"
},
"V": {
"decimal": 149,
"pokemon": "Alakazam"
},
"W": {
"decimal": 150,
"pokemon": "Pidgeotto"
},
"X": {
"decimal": 151,
"pokemon": "Pidgeot"
},
"Y": {
"decimal": 152,
"pokemon": "Starmie"
},
"Z": {
"decimal": 153,
"pokemon": "Bulbasaur"
},
"(": {
"decimal": 154,
"pokemon": "Venusaur"
},
")": {
"decimal": 155,
"pokemon": "Tentacruel"
},
":": {
"decimal": 156,
"pokemon": "MissingNo."
},
";": {
"decimal": 157,
"pokemon": "Goldeen"
},
"[": {
"decimal": 158,
"pokemon": "Seaking"
},
"]": {
"decimal": 159,
"pokemon": "MissingNo."
},
"a": {
"decimal": 160,
"pokemon": "MissingNo."
},
"b": {
"decimal": 161,
"pokemon": "MissingNo."
},
"c": {
"decimal": 162,
"pokemon": "MissingNo."
},
"d": {
"decimal": 163,
"pokemon": "Ponyta"
},
"e": {
"decimal": 164,
"pokemon": "Rapidash"
},
"f": {
"decimal": 165,
"pokemon": "Rattata"
},
"g": {
"decimal": 166,
"pokemon": "Raticate"
},
"h": {
"decimal": 167,
"pokemon": "Nidorino"
},
"i": {
"decimal": 168,
"pokemon": "Nidorina"
},
"j": {
"decimal": 169,
"pokemon": "Geodude"
},
"k": {
"decimal": 170,
"pokemon": "Porygon"
},
"l": {
"decimal": 171,
"pokemon": "Aerodactyl"
},
"m": {
"decimal": 172,
"pokemon": "MissingNo."
},
"n": {
"decimal": 173,
"pokemon": "Magnemite"
},
"o": {
"decimal": 174,
"pokemon": "MissingNo."
},
"p": {
"decimal": 175,
"pokemon": "MissingNo."
},
"q": {
"decimal": 176,
"pokemon": "Charmander"
},
"r": {
"decimal": 177,
"pokemon": "Squirtle"
},
"s": {
"decimal": 178,
"pokemon": "Charmeleon"
},
"t": {
"decimal": 179,
"pokemon": "Wartortle"
},
"u": {
"decimal": 180,
"pokemon": "Charizard"
},
"v": {
"decimal": 181,
"pokemon": "MissingNo."
},
"w": {
"decimal": 182,
"pokemon": "Kabutops Fossil"
},
"x": {
"decimal": 183,
"pokemon": "Aerodactyl Fossil"
},
"y": {
"decimal": 184,
"pokemon": "Ghost"
},
"z": {
"decimal": 185,
"pokemon": "Oddish"
},
"PK": {
"decimal": 225,
"pokemon": "Rival (#1)"
},
"MN": {
"decimal": 226,
"pokemon": "Professor Oak"
},
"-": {
"decimal": 227,
"pokemon": "Chief"
},
"?": {
"decimal": 230,
"pokemon": "Rocket"
},
"!": {
"decimal": 231,
"pokemon": "Cooltrainer Male"
},
"♂": {
"decimal": 239,
"pokemon": "Blaine"
},
"×": {
"decimal": 241,
"pokemon": "Gentleman"
},
".": {
"decimal": 242,
"pokemon": "Rival (#2)"
},
"/": {
"decimal": 243,
"pokemon": "Rival (Final)"
},
",": {
"decimal": 244,
"pokemon": "Lorelei"
},
"♀": {
"decimal": 245,
"pokemon": "Channeler"
},
"0": {
"decimal": 246,
"pokemon": "Agatha"
},
"1": {
"decimal": 247,
"pokemon": "Lance"
},
"2": {
"decimal": 248,
"pokemon": "Glitch - Freezes Game"
},
"3": {
"decimal": 249,
"pokemon": "Glitch - Freezes Game"
},
"4": {
"decimal": 250,
"pokemon": "Glitch - Freezes Game"
},
"5": {
"decimal": 251,
"pokemon": "Glitch Trainer (Can be used for ZZAZZ Glitch)"
},
"6": {
"decimal": 252,
"pokemon": "Glitch Trainer (Can be used for ZZAZZ Glitch)"
},
"7": {
"decimal": 253,
"pokemon": "Glitch Trainer (Freezes after battle)"
},
"8": {
"decimal": 254,
"pokemon": "Glitch Trainer (Can be used for ZZAZZ Glitch)"
},
"9": {
"decimal": 255,
"pokemon": "Glitch Trainer (Can be used for ZZAZZ Glitch)"
}
}
+203
View File
@@ -0,0 +1,203 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Old Man Glitch Decoder — Pokémon Red/Blue/Yellow</title>
<script src="./jquery-4.0.0.min.js"></script>
<style>
:root {
color-scheme: light dark;
--bg: #f4f1e8;
--panel: #ffffff;
--ink: #202020;
--muted: #6b6b6b;
--accent: #cc0000;
--accent-dim: #e8d7d7;
--border: #202020;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #16171a;
--panel: #232529;
--ink: #eceff1;
--muted: #9aa0a6;
--accent: #ff5c5c;
--accent-dim: #3a2626;
--border: #4a4d52;
}
}
* { box-sizing: border-box; }
body {
margin: 0;
padding: 2rem 1rem 4rem;
background: var(--bg);
color: var(--ink);
font-family: "Courier New", Courier, monospace;
display: flex;
justify-content: center;
}
main { width: 100%; max-width: 720px; }
h1 {
font-size: 1.4rem;
margin: 0 0 0.25rem;
letter-spacing: 0.02em;
}
.subtitle {
color: var(--muted);
margin: 0 0 1.5rem;
font-size: 0.9rem;
}
.panel {
background: var(--panel);
border: 2px solid var(--border);
border-radius: 6px;
padding: 1.25rem 1.5rem;
margin-bottom: 1.5rem;
}
.name-row {
display: flex;
gap: 0.6rem;
flex-wrap: wrap;
margin-bottom: 0.5rem;
}
.slot {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.35rem;
}
.slot .index {
font-size: 0.7rem;
color: var(--muted);
}
.slot .role {
font-size: 0.65rem;
color: var(--accent);
font-weight: bold;
min-height: 1em;
}
.slot input {
width: 2.4rem;
height: 2.6rem;
text-align: center;
font-size: 1.3rem;
font-family: inherit;
background: var(--bg);
color: var(--ink);
border: 2px solid var(--border);
border-radius: 4px;
}
.slot input:focus {
outline: none;
border-color: var(--accent);
}
.symbols {
display: flex;
gap: 0.2rem;
flex-wrap: wrap;
justify-content: center;
max-width: 2.4rem;
}
.symbols button {
font-family: inherit;
font-size: 0.7rem;
padding: 0.1rem 0.3rem;
cursor: pointer;
background: var(--accent-dim);
color: var(--ink);
border: 1px solid var(--border);
border-radius: 3px;
}
.symbols button:hover { background: var(--accent); color: #fff; }
.hint {
font-size: 0.75rem;
color: var(--muted);
margin-top: 0.75rem;
}
.results {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 0.75rem;
margin-top: 1rem;
}
.encounter {
border: 1px solid var(--border);
border-radius: 4px;
padding: 0.75rem 0.9rem;
background: var(--bg);
}
.encounter .from {
font-size: 0.7rem;
color: var(--muted);
margin-bottom: 0.35rem;
}
.encounter .species {
font-size: 1.1rem;
font-weight: bold;
}
.encounter .level {
color: var(--accent);
font-size: 0.95rem;
}
h2 {
font-size: 1rem;
margin: 0 0 0.5rem;
}
p { line-height: 1.5; font-size: 0.9rem; }
code {
background: var(--accent-dim);
padding: 0.1rem 0.3rem;
border-radius: 3px;
}
footer {
color: var(--muted);
font-size: 0.75rem;
text-align: center;
margin-top: 2rem;
}
#load-error {
display: none;
color: var(--accent);
font-size: 0.85rem;
margin-top: 0.75rem;
}
</style>
</head>
<body>
<main>
<h1>Old Man Glitch Decoder</h1>
<p class="subtitle">Pokémon Red / Blue / Yellow — trainer-name encounter preview</p>
<div class="panel">
<div class="name-row" id="name-row">
<!-- 7 slots injected by app.js -->
</div>
<div class="hint">
Type up to 7 characters (letters, digits, or the symbols below each box). The 1st character has no effect — the glitch never reads it.
</div>
<div id="load-error">Couldn't load <code>data/charmap.json</code>. If you opened this file directly, the browser blocks local JSON loads — serve the folder instead, e.g. <code>python3 -m http.server</code> from this directory, then open <code>http://localhost:8000</code>.</div>
<div class="results" id="results"></div>
</div>
<div class="panel">
<h2>How this works</h2>
<p>
In Gen&nbsp;1, walking away from the Old Man in Viridian City right after he "shows you how to catch a Pokémon" can trigger a glitch encounter built from bytes of your trainer's name. Positions <strong>3, 5, and 7</strong> of the name are read as a Pokémon species byte, and positions <strong>2, 4, and 6</strong> are read as that encounter's level byte — three separate (species, level) pairs.
</p>
<p>
The species comes from the game's internal index table: every nameable character (A&#8211;Z, a&#8211;z, digits, and a handful of symbols) has a raw byte value from 128&#8211;255, and that byte doubles as a species ID in the Pokémon index.
</p>
<p>
The "level" isn't computed at all &mdash; it's <em>the same raw byte value</em>, printed as-is. That's why these encounters always show absurd levels (128&#8211;255) instead of the normal 1&#8211;100: the game is misreading a name-entry character as if it were a level.
</p>
</div>
<footer>
Data source: internal Gen 1 index table (decimal / R-B-Y Pokémon / R-B-Y Letter columns). Based on the "Old Man Name Generator 2" tool archived from Glitch City Laboratories.
</footer>
</main>
<script src="app.js"></script>
</body>
</html>
+2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long