Port Headbutt and Rock Smash encounters
Facing a Headbutt tree asks to HEADBUTT it, and HEADBUTT from the party menu skips the question. ShakeHeadbuttTree shakes the tree as a sprite over grass, then TreeMonEncounter scores the facing tile against the trainer ID and draws from the map's common or rare tree table. A Pokémon that falls fights as BATTLETYPE_TREE: "fell out of the tree!", and the time of day's AsleepTreeMons species start asleep. Smashable rocks run SmashRockScript, and ROCK SMASH from the party menu acts on the facing rock; after the rock breaks, RockMonEncounter gives the map's rock set a 40% chance of a wild battle. Closes #26 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ee55d344af
commit
f259163da9
18 files changed
+789
-36
No files matched your search
@@ -175,6 +175,8 @@ export interface BattleOptions {
|
||||
caughtData?: (level: number) => number;
|
||||
/** BATTLETYPE_TUTORIAL: the Dude's catching demonstration. */
|
||||
tutorial?: boolean;
|
||||
/** BATTLETYPE_TREE: a wild Pokémon that fell from a Headbutt tree. */
|
||||
tree?: boolean;
|
||||
/** GetWorldMapLocation for the current map (LevelUpHappinessMod). */
|
||||
landmark?: number;
|
||||
/** wPartyMenuCursor as the last party menu outside battle left it. */
|
||||
@@ -476,6 +478,7 @@ export class Battle {
|
||||
this.partyMenuCursor = options.partyMenuCursor ?? 0;
|
||||
if (options.trainer) this.battleMode = C.TRAINER_BATTLE;
|
||||
if (options.tutorial) this.battleType = C.BATTLETYPE_TUTORIAL;
|
||||
else if (options.tree) this.battleType = C.BATTLETYPE_TREE;
|
||||
}
|
||||
private get reader(): RomReader {
|
||||
return this.options.data.reader;
|
||||
@@ -659,7 +662,8 @@ export class Battle {
|
||||
pp: moves.map((move) => (move ? data.move(move).pp : 0)),
|
||||
happiness: C.BASE_HAPPINESS,
|
||||
level,
|
||||
status: 0,
|
||||
// .TreeMon: some Pokémon fall from trees asleep.
|
||||
status: this.sleepingTreeMon(species) ? C.TREEMON_SLEEP_TURNS : 0,
|
||||
hp: stats[0],
|
||||
maxHp: stats[0],
|
||||
stats: stats.slice(1),
|
||||
@@ -676,6 +680,21 @@ export class Battle {
|
||||
this.wildMoves = [...moves];
|
||||
this.wildPP = [...this.enemy.mon.pp];
|
||||
}
|
||||
/** CheckSleepingTreeMon: in a tree battle, the time of day's
|
||||
* AsleepTreeMons list names the species that sleep. */
|
||||
private sleepingTreeMon(species: number) {
|
||||
if (this.battleType !== C.BATTLETYPE_TREE) return false;
|
||||
const time = this.options.timeOfDay;
|
||||
let p =
|
||||
time < C.DAY_F
|
||||
? BATTLE.AsleepTreeMonsMorn
|
||||
: time === C.DAY_F
|
||||
? BATTLE.AsleepTreeMonsDay
|
||||
: BATTLE.AsleepTreeMonsNite;
|
||||
for (const r = this.reader; r.byte(p) !== 0xff; p++)
|
||||
if (r.byte(p) === species) return true;
|
||||
return false;
|
||||
}
|
||||
/** InitBattleMon from the party, then status penalties. */
|
||||
private initBattleMon() {
|
||||
const mon = this.partyMon,
|
||||
@@ -773,7 +792,12 @@ export class Battle {
|
||||
if (trainer) {
|
||||
yield* this.say(BATTLE.WantsToBattleText);
|
||||
yield* this.say(BATTLE.BattleText_EnemySentOut);
|
||||
} else yield* this.say(BATTLE.WildPokemonAppearedText);
|
||||
} else
|
||||
yield* this.say(
|
||||
this.battleType === C.BATTLETYPE_TREE
|
||||
? BATTLE.PokemonFellFromTreeText
|
||||
: BATTLE.WildPokemonAppearedText,
|
||||
);
|
||||
this.resetBattleParticipants();
|
||||
this.initBattleMon();
|
||||
const amuletCoin =
|
||||
|
||||
Reference in new issue
Block a user