initial commit
This commit is contained in:
@@ -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 + ' & 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();
|
||||
});
|
||||
});
|
||||
@@ -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
@@ -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 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–Z, a–z, digits, and a handful of symbols) has a raw byte value from 128–255, and that byte doubles as a species ID in the Pokémon index.
|
||||
</p>
|
||||
<p>
|
||||
The "level" isn't computed at all — it's <em>the same raw byte value</em>, printed as-is. That's why these encounters always show absurd levels (128–255) instead of the normal 1–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>
|
||||
Vendored
+2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user