Port the fishing rods and wild Magikarp
The Old, Good and Super Rods run FishFunction facing water: the map's fish group and Fish pick no fish, no bite, or a Pokémon from FishGroups and the time of day's TimeFishGroups. The player casts and jiggles with the fishing frames drawn from LoadFishingGFX's tiles, shows "!" on a bite, and the hooked Pokémon attacks as BATTLETYPE_FISH. Facing pieces now use signed offsets, which the rod above and left of the player needs. Wild Magikarp no longer stop a battle: LoadEnemyMon redraws their DVs through CalcMagikarpLength and its filters, as the original does. The length routine matches the original in the reference emulator. Part of #27 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4cb4676c7d
commit
397dcef44c
20 files changed
+681
-47
No files matched your search
+23
-2
@@ -126,6 +126,7 @@ export class CrystalAssets {
|
||||
environment: r.byte(header + 2),
|
||||
paletteMode: r.byte(header + 7) & 15,
|
||||
phoneService: r.byte(header + 7) >> 4,
|
||||
fishGroup: r.byte(header + 8),
|
||||
attributes,
|
||||
blocks: r.slice(r.far(attributes + 3), blockWidth * blockHeight),
|
||||
warps: [],
|
||||
@@ -500,6 +501,24 @@ export class CrystalAssets {
|
||||
r.byte(entry + 2),
|
||||
);
|
||||
}
|
||||
/** The first vTiles0 tile an emote's graphics are loaded to. */
|
||||
emoteTile(index: number): number {
|
||||
if (index < 0 || index > 11) throw new Error("Unknown emote.");
|
||||
return (this.reader.word(SYMBOLS.emotes + index * 6 + 4) - 0x8000) >> 4;
|
||||
}
|
||||
/** LoadFishingGFX: the player's sprite (24 tiles) with the lower halves
|
||||
* of the standing frames replaced by FishingGFX or KrisFishingGFX, then
|
||||
* the two rod tiles that the fishing frames place at $fc and $fd. All
|
||||
* of them are in VRAM bank 1, as the player's sprite is. */
|
||||
fishingSprite(graphics: Uint8Array, kris: boolean): Uint8Array {
|
||||
const source = kris ? OVERWORLD.KrisFishingGFX : OVERWORLD.FishingGFX;
|
||||
const result = new Uint8Array(26 * 16);
|
||||
result.set(graphics.subarray(0, 24 * 16));
|
||||
[2, 6, 10, 24].forEach((tile, chunk) =>
|
||||
result.set(this.reader.slice(source + chunk * 32, 32), tile * 16),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
/** HeadbuttTreeGFX: the shaking tree's two 2x2 frames, eight tiles. */
|
||||
headbuttTree(): Uint8Array {
|
||||
return this.reader.slice(OVERWORLD.HeadbuttTreeGFX, 8 * 16);
|
||||
@@ -553,9 +572,11 @@ export class CrystalAssets {
|
||||
pointer = r.near(SYMBOLS.facings + facing * 2, r.bank(SYMBOLS.facings));
|
||||
return Array.from({ length: r.byte(pointer) }, (_, i) => {
|
||||
const p = pointer + 1 + i * 4;
|
||||
// The offsets are added to the sprite's position modulo 256, so
|
||||
// they act as signed bytes.
|
||||
return {
|
||||
y: r.byte(p),
|
||||
x: r.byte(p + 1),
|
||||
y: (r.byte(p) << 24) >> 24,
|
||||
x: (r.byte(p + 1) << 24) >> 24,
|
||||
attributes: r.byte(p + 2),
|
||||
tile: r.byte(p + 3),
|
||||
};
|
||||
|
||||
@@ -161,6 +161,8 @@ export const RAM = {
|
||||
visitedSpawns: 0xdca5,
|
||||
tempWildMonSpecies: 0xd22e,
|
||||
coins: 0xd855,
|
||||
/** hBGMapMode: HRAM, like hMoneyTemp below. */
|
||||
bgMapMode: 0xffd4,
|
||||
blueCardBalance: 0xdc4b,
|
||||
/** hMoneyTemp: HRAM, not WRAM like the other entries above. */
|
||||
moneyTemp: 0xffc3,
|
||||
@@ -274,6 +276,16 @@ export const OVERWORLD = {
|
||||
"_BasementKey.BasementKeyScript": 0x507e1,
|
||||
"_Squirtbottle.SquirtbottleScript": 0x5073c,
|
||||
"_Squirtbottle.CheckCanUseSquirtbottle": 0x50753,
|
||||
FishGroups: 0x92488,
|
||||
TimeFishGroups: 0x9266f,
|
||||
Script_GotABite: 0xd035,
|
||||
Script_NotEvenANibble: 0xd01e,
|
||||
Script_NotEvenANibble2: 0xd027,
|
||||
Fishing_CheckFacingUp: 0xd06c,
|
||||
LoadFishingGFX: 0xb84b3,
|
||||
PutTheRodAway: 0xd095,
|
||||
FishingGFX: 0xb84f2,
|
||||
KrisFishingGFX: 0xb8582,
|
||||
Flypoints: 0x91c5e,
|
||||
"TownMapBubble.Where": 0x91bf4,
|
||||
RuinsOfAlphKabutoChamber_MapAttributes: 0x958d0,
|
||||
@@ -531,6 +543,9 @@ export const MENU = {
|
||||
CardKeyEffect: 0xf745,
|
||||
BasementKeyEffect: 0xf74c,
|
||||
SquirtbottleEffect: 0xf73e,
|
||||
OldRodEffect: 0xf5a5,
|
||||
GoodRodEffect: 0xf5a9,
|
||||
SuperRodEffect: 0xf5ad,
|
||||
VitaminEffect: 0xee3d,
|
||||
RareCandyEffect: 0xef14,
|
||||
ItemStatRoseText: 0xeea6,
|
||||
@@ -548,6 +563,8 @@ export const MENU = {
|
||||
/** Battle engine tables and texts, keyed by their PRET labels so that
|
||||
* \`npm run check:reference\` can verify each address by name. */
|
||||
export const BATTLE = {
|
||||
HookedPokemonAttackedText: 0x8075c,
|
||||
MagikarpLengths: 0xfbca8,
|
||||
ItemUsedText: 0xf83d,
|
||||
BallBrokeFreeText: 0xedb5,
|
||||
BallAppearedCaughtText: 0xedba,
|
||||
|
||||
Reference in new issue
Block a user