From f275790aec4a796ed969b099364abfdf25385967 Mon Sep 17 00:00:00 2001 From: Rangi Date: Thu, 2 Jul 2020 23:30:21 -0400 Subject: Add subdirectories to engine/ similar to pokecrystal --- engine/items/get_bag_item_quantity.asm | 18 + engine/items/item_effects.asm | 2986 ++++++++++++++++++++++++++++++++ engine/items/items.asm | 2986 -------------------------------- engine/items/subtract_paid_money.asm | 17 + engine/items/town_map.asm | 618 +++++++ 5 files changed, 3639 insertions(+), 2986 deletions(-) create mode 100644 engine/items/get_bag_item_quantity.asm create mode 100755 engine/items/item_effects.asm delete mode 100755 engine/items/items.asm create mode 100644 engine/items/subtract_paid_money.asm create mode 100755 engine/items/town_map.asm (limited to 'engine/items') diff --git a/engine/items/get_bag_item_quantity.asm b/engine/items/get_bag_item_quantity.asm new file mode 100644 index 00000000..f10df1a0 --- /dev/null +++ b/engine/items/get_bag_item_quantity.asm @@ -0,0 +1,18 @@ +GetQuantityOfItemInBag: +; In: b = item ID +; Out: b = how many of that item are in the bag + call GetPredefRegisters + ld hl, wNumBagItems +.loop + inc hl + ld a, [hli] + cp $ff + jr z, .notInBag + cp b + jr nz, .loop + ld a, [hl] + ld b, a + ret +.notInBag + ld b, 0 + ret diff --git a/engine/items/item_effects.asm b/engine/items/item_effects.asm new file mode 100755 index 00000000..6e7bed1e --- /dev/null +++ b/engine/items/item_effects.asm @@ -0,0 +1,2986 @@ +UseItem_:: + ld a, 1 + ld [wActionResultOrTookBattleTurn], a ; initialise to success value + ld a, [wcf91] ;contains item_ID + cp HM_01 + jp nc, ItemUseTMHM + ld hl, ItemUsePtrTable + dec a + add a + ld c, a + ld b, 0 + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + jp hl + +ItemUsePtrTable: + dw ItemUseBall ; MASTER_BALL + dw ItemUseBall ; ULTRA_BALL + dw ItemUseBall ; GREAT_BALL + dw ItemUseBall ; POKE_BALL + dw ItemUseTownMap ; TOWN_MAP + dw ItemUseBicycle ; BICYCLE + dw ItemUseSurfboard ; out-of-battle Surf effect + dw ItemUseBall ; SAFARI_BALL + dw ItemUsePokedex ; POKEDEX + dw ItemUseEvoStone ; MOON_STONE + dw ItemUseMedicine ; ANTIDOTE + dw ItemUseMedicine ; BURN_HEAL + dw ItemUseMedicine ; ICE_HEAL + dw ItemUseMedicine ; AWAKENING + dw ItemUseMedicine ; PARLYZ_HEAL + dw ItemUseMedicine ; FULL_RESTORE + dw ItemUseMedicine ; MAX_POTION + dw ItemUseMedicine ; HYPER_POTION + dw ItemUseMedicine ; SUPER_POTION + dw ItemUseMedicine ; POTION + dw ItemUseBait ; BOULDERBADGE + dw ItemUseRock ; CASCADEBADGE + dw UnusableItem ; THUNDERBADGE + dw UnusableItem ; RAINBOWBADGE + dw UnusableItem ; SOULBADGE + dw UnusableItem ; MARSHBADGE + dw UnusableItem ; VOLCANOBADGE + dw UnusableItem ; EARTHBADGE + dw ItemUseEscapeRope ; ESCAPE_ROPE + dw ItemUseRepel ; REPEL + dw UnusableItem ; OLD_AMBER + dw ItemUseEvoStone ; FIRE_STONE + dw ItemUseEvoStone ; THUNDER_STONE + dw ItemUseEvoStone ; WATER_STONE + dw ItemUseVitamin ; HP_UP + dw ItemUseVitamin ; PROTEIN + dw ItemUseVitamin ; IRON + dw ItemUseVitamin ; CARBOS + dw ItemUseVitamin ; CALCIUM + dw ItemUseVitamin ; RARE_CANDY + dw UnusableItem ; DOME_FOSSIL + dw UnusableItem ; HELIX_FOSSIL + dw UnusableItem ; SECRET_KEY + dw UnusableItem + dw UnusableItem ; BIKE_VOUCHER + dw ItemUseXAccuracy ; X_ACCURACY + dw ItemUseEvoStone ; LEAF_STONE + dw ItemUseCardKey ; CARD_KEY + dw UnusableItem ; NUGGET + dw UnusableItem ; ??? PP_UP + dw ItemUsePokedoll ; POKE_DOLL + dw ItemUseMedicine ; FULL_HEAL + dw ItemUseMedicine ; REVIVE + dw ItemUseMedicine ; MAX_REVIVE + dw ItemUseGuardSpec ; GUARD_SPEC + dw ItemUseSuperRepel ; SUPER_REPL + dw ItemUseMaxRepel ; MAX_REPEL + dw ItemUseDireHit ; DIRE_HIT + dw UnusableItem ; COIN + dw ItemUseMedicine ; FRESH_WATER + dw ItemUseMedicine ; SODA_POP + dw ItemUseMedicine ; LEMONADE + dw UnusableItem ; S_S_TICKET + dw UnusableItem ; GOLD_TEETH + dw ItemUseXStat ; X_ATTACK + dw ItemUseXStat ; X_DEFEND + dw ItemUseXStat ; X_SPEED + dw ItemUseXStat ; X_SPECIAL + dw ItemUseCoinCase ; COIN_CASE + dw ItemUseOaksParcel ; OAKS_PARCEL + dw ItemUseItemfinder ; ITEMFINDER + dw UnusableItem ; SILPH_SCOPE + dw ItemUsePokeflute ; POKE_FLUTE + dw UnusableItem ; LIFT_KEY + dw UnusableItem ; EXP_ALL + dw ItemUseOldRod ; OLD_ROD + dw ItemUseGoodRod ; GOOD_ROD + dw ItemUseSuperRod ; SUPER_ROD + dw ItemUsePPUp ; PP_UP (real one) + dw ItemUsePPRestore ; ETHER + dw ItemUsePPRestore ; MAX_ETHER + dw ItemUsePPRestore ; ELIXER + dw ItemUsePPRestore ; MAX_ELIXER + +ItemUseBall: + +; Balls can't be used out of battle. + ld a, [wIsInBattle] + and a + jp z, ItemUseNotTime + +; Balls can't catch trainers' Pokémon. + dec a + jp nz, ThrowBallAtTrainerMon + +; If this is for the old man battle, skip checking if the party & box are full. + ld a, [wBattleType] + dec a + jr z, .canUseBall + + ld a, [wPartyCount] ; is party full? + cp PARTY_LENGTH + jr nz, .canUseBall + ld a, [wNumInBox] ; is box full? + cp MONS_PER_BOX + jp z, BoxFullCannotThrowBall + +.canUseBall + xor a + ld [wCapturedMonSpecies], a + + ld a, [wBattleType] + cp BATTLE_TYPE_SAFARI + jr nz, .skipSafariZoneCode + +.safariZone + ld hl, wNumSafariBalls + dec [hl] ; remove a Safari Ball + +.skipSafariZoneCode + call RunDefaultPaletteCommand + + ld a, $43 ; successful capture value + ld [wPokeBallAnimData], a + + call LoadScreenTilesFromBuffer1 + ld hl, ItemUseText00 + call PrintText + +; If the player is fighting an unidentified ghost, set the value that indicates +; the Pokémon can't be caught and skip the capture calculations. + callab IsGhostBattle + ld b, $10 ; can't be caught value + jp z, .setAnimData + + ld a, [wBattleType] + dec a + jr nz, .notOldManBattle + +.oldManBattle + ld hl, wGrassRate + ld de, wPlayerName + ld bc, NAME_LENGTH + call CopyData ; save the player's name in the Wild Monster data (part of the Cinnabar Island Missingno. glitch) + jp .captured + +.notOldManBattle +; If the player is fighting the ghost Marowak, set the value that indicates the +; Pokémon can't be caught and skip the capture calculations. + ld a, [wCurMap] + cp POKEMON_TOWER_6F + jr nz, .loop + ld a, [wEnemyMonSpecies2] + cp MAROWAK + ld b, $10 ; can't be caught value + jp z, .setAnimData + +; Get the first random number. Let it be called Rand1. +; Rand1 must be within a certain range according the kind of ball being thrown. +; The ranges are as follows. +; Poké Ball: [0, 255] +; Great Ball: [0, 200] +; Ultra/Safari Ball: [0, 150] +; Loop until an acceptable number is found. + +.loop + call Random + ld b, a + +; Get the item ID. + ld hl, wcf91 + ld a, [hl] + +; The Master Ball always succeeds. + cp MASTER_BALL + jp z, .captured + +; Anything will do for the basic Poké Ball. + cp POKE_BALL + jr z, .checkForAilments + +; If it's a Great/Ultra/Safari Ball and Rand1 is greater than 200, try again. + ld a, 200 + cp b + jr c, .loop + +; Less than or equal to 200 is good enough for a Great Ball. + ld a, [hl] + cp GREAT_BALL + jr z, .checkForAilments + +; If it's an Ultra/Safari Ball and Rand1 is greater than 150, try again. + ld a, 150 + cp b + jr c, .loop + +.checkForAilments +; Pokémon can be caught more easily with a status ailment. +; Depending on the status ailment, a certain value will be subtracted from +; Rand1. Let this value be called Status. +; The larger Status is, the more easily the Pokémon can be caught. +; no status ailment: Status = 0 +; Burn/Paralysis/Poison: Status = 12 +; Freeze/Sleep: Status = 25 +; If Status is greater than Rand1, the Pokémon will be caught for sure. + ld a, [wEnemyMonStatus] + and a + jr z, .skipAilmentValueSubtraction ; no ailments + and 1 << FRZ | SLP + ld c, 12 + jr z, .notFrozenOrAsleep + ld c, 25 +.notFrozenOrAsleep + ld a, b + sub c + jp c, .captured + ld b, a + +.skipAilmentValueSubtraction + push bc ; save (Rand1 - Status) + +; Calculate MaxHP * 255. + xor a + ld [H_MULTIPLICAND], a + ld hl, wEnemyMonMaxHP + ld a, [hli] + ld [H_MULTIPLICAND + 1], a + ld a, [hl] + ld [H_MULTIPLICAND + 2], a + ld a, 255 + ld [H_MULTIPLIER], a + call Multiply + +; Determine BallFactor. It's 8 for Great Balls and 12 for the others. + ld a, [wcf91] + cp GREAT_BALL + ld a, 12 + jr nz, .skip1 + ld a, 8 + +.skip1 +; Note that the results of all division operations are floored. + +; Calculate (MaxHP * 255) / BallFactor. + ld [H_DIVISOR], a + ld b, 4 ; number of bytes in dividend + call Divide + +; Divide the enemy's current HP by 4. HP is not supposed to exceed 999 so +; the result should fit in a. If the division results in a quotient of 0, +; change it to 1. + ld hl, wEnemyMonHP + ld a, [hli] + ld b, a + ld a, [hl] + srl b + rr a + srl b + rr a + and a + jr nz, .skip2 + inc a + +.skip2 +; Let W = ((MaxHP * 255) / BallFactor) / max(HP / 4, 1). Calculate W. + ld [H_DIVISOR], a + ld b, 4 + call Divide + +; If W > 255, store 255 in [H_QUOTIENT + 3]. +; Let X = min(W, 255) = [H_QUOTIENT + 3]. + ld a, [H_QUOTIENT + 2] + and a + jr z, .skip3 + ld a, 255 + ld [H_QUOTIENT + 3], a + +.skip3 + pop bc ; b = Rand1 - Status + +; If Rand1 - Status > CatchRate, the ball fails to capture the Pokémon. + ld a, [wEnemyMonActualCatchRate] + cp b + jr c, .failedToCapture + +; If W > 255, the ball captures the Pokémon. + ld a, [H_QUOTIENT + 2] + and a + jr nz, .captured + + call Random ; Let this random number be called Rand2. + +; If Rand2 > X, the ball fails to capture the Pokémon. + ld b, a + ld a, [H_QUOTIENT + 3] + cp b + jr c, .failedToCapture + +.captured + jr .skipShakeCalculations + +.failedToCapture + ld a, [H_QUOTIENT + 3] + ld [wPokeBallCaptureCalcTemp], a ; Save X. + +; Calculate CatchRate * 100. + xor a + ld [H_MULTIPLICAND], a + ld [H_MULTIPLICAND + 1], a + ld a, [wEnemyMonActualCatchRate] + ld [H_MULTIPLICAND + 2], a + ld a, 100 + ld [H_MULTIPLIER], a + call Multiply + +; Determine BallFactor2. +; Poké Ball: BallFactor2 = 255 +; Great Ball: BallFactor2 = 200 +; Ultra/Safari Ball: BallFactor2 = 150 + ld a, [wcf91] + ld b, 255 + cp POKE_BALL + jr z, .skip4 + ld b, 200 + cp GREAT_BALL + jr z, .skip4 + ld b, 150 + cp ULTRA_BALL + jr z, .skip4 + +.skip4 +; Let Y = (CatchRate * 100) / BallFactor2. Calculate Y. + ld a, b + ld [H_DIVISOR], a + ld b, 4 + call Divide + +; If Y > 255, there are 3 shakes. +; Note that this shouldn't be possible. +; The maximum value of Y is (255 * 100) / 150 = 170. + ld a, [H_QUOTIENT + 2] + and a + ld b, $63 ; 3 shakes + jr nz, .setAnimData + +; Calculate X * Y. + ld a, [wPokeBallCaptureCalcTemp] + ld [H_MULTIPLIER], a + call Multiply + +; Calculate (X * Y) / 255. + ld a, 255 + ld [H_DIVISOR], a + ld b, 4 + call Divide + +; Determine Status2. +; no status ailment: Status2 = 0 +; Burn/Paralysis/Poison: Status2 = 5 +; Freeze/Sleep: Status2 = 10 + ld a, [wEnemyMonStatus] + and a + jr z, .skip5 + and 1 << FRZ | SLP + ld b, 5 + jr z, .addAilmentValue + ld b, 10 + +.addAilmentValue +; If the Pokémon has a status ailment, add Status2. + ld a, [H_QUOTIENT + 3] + add b + ld [H_QUOTIENT + 3], a + +.skip5 +; Finally determine the number of shakes. +; Let Z = ((X * Y) / 255) + Status2 = [H_QUOTIENT + 3]. +; The number of shakes depend on the range Z is in. +; 0 ≤ Z < 10: 0 shakes (the ball misses) +; 10 ≤ Z < 30: 1 shake +; 30 ≤ Z < 70: 2 shakes +; 70 ≤ Z: 3 shakes + ld a, [H_QUOTIENT + 3] + cp 10 + ld b, $20 + jr c, .setAnimData + cp 30 + ld b, $61 + jr c, .setAnimData + cp 70 + ld b, $62 + jr c, .setAnimData + ld b, $63 + +.setAnimData + ld a, b + ld [wPokeBallAnimData], a + +.skipShakeCalculations + ld c, 20 + call DelayFrames + +; Do the animation. + ld a, TOSS_ANIM + ld [wAnimationID], a + xor a + ld [H_WHOSETURN], a + ld [wAnimationType], a + ld [wDamageMultipliers], a + ld a, [wWhichPokemon] + push af + ld a, [wcf91] + push af + predef MoveAnimation + pop af + ld [wcf91], a + pop af + ld [wWhichPokemon], a + +; Determine the message to display from the animation. + ld a, [wPokeBallAnimData] + cp $10 + ld hl, ItemUseBallText00 + jp z, .printMessage + cp $20 + ld hl, ItemUseBallText01 + jp z, .printMessage + cp $61 + ld hl, ItemUseBallText02 + jp z, .printMessage + cp $62 + ld hl, ItemUseBallText03 + jp z, .printMessage + cp $63 + ld hl, ItemUseBallText04 + jp z, .printMessage + +; Save current HP. + ld hl, wEnemyMonHP + ld a, [hli] + push af + ld a, [hli] + push af + +; Save status ailment. + inc hl + ld a, [hl] + push af + + push hl + +; If the Pokémon is transformed, the Pokémon is assumed to be a Ditto. +; This is a bug because a wild Pokémon could have used Transform via +; Mirror Move even though the only wild Pokémon that knows Transform is Ditto. + ld hl, wEnemyBattleStatus3 + bit TRANSFORMED, [hl] + jr z, .notTransformed + ld a, DITTO + ld [wEnemyMonSpecies2], a + jr .skip6 + +.notTransformed +; If the Pokémon is not transformed, set the transformed bit and copy the +; DVs to wTransformedEnemyMonOriginalDVs so that LoadEnemyMonData won't generate +; new DVs. + set TRANSFORMED, [hl] + ld hl, wTransformedEnemyMonOriginalDVs + ld a, [wEnemyMonDVs] + ld [hli], a + ld a, [wEnemyMonDVs + 1] + ld [hl], a + +.skip6 + ld a, [wcf91] + push af + ld a, [wEnemyMonSpecies2] + ld [wcf91], a + ld a, [wEnemyMonLevel] + ld [wCurEnemyLVL], a + callab LoadEnemyMonData + pop af + ld [wcf91], a + pop hl + pop af + ld [hld], a + dec hl + pop af + ld [hld], a + pop af + ld [hl], a + ld a, [wEnemyMonSpecies] + ld [wCapturedMonSpecies], a + ld [wcf91], a + ld [wd11e], a + ld a, [wBattleType] + dec a ; is this the old man battle? + jr z, .oldManCaughtMon ; if so, don't give the player the caught Pokémon + + ld hl, ItemUseBallText05 + call PrintText + +; Add the caught Pokémon to the Pokédex. + predef IndexToPokedex + ld a, [wd11e] + dec a + ld c, a + ld b, FLAG_TEST + ld hl, wPokedexOwned + predef FlagActionPredef + ld a, c + push af + ld a, [wd11e] + dec a + ld c, a + ld b, FLAG_SET + predef FlagActionPredef + pop af + + and a ; was the Pokémon already in the Pokédex? + jr nz, .skipShowingPokedexData ; if so, don't show the Pokédex data + + ld hl, ItemUseBallText06 + call PrintText + call ClearSprites + ld a, [wEnemyMonSpecies] + ld [wd11e], a + predef ShowPokedexData + +.skipShowingPokedexData + ld a, [wPartyCount] + cp PARTY_LENGTH ; is party full? + jr z, .sendToBox + xor a ; PLAYER_PARTY_DATA + ld [wMonDataLocation], a + call ClearSprites + call AddPartyMon + jr .done + +.sendToBox + call ClearSprites + call SendNewMonToBox + ld hl, ItemUseBallText07 + CheckEvent EVENT_MET_BILL + jr nz, .printTransferredToPCText + ld hl, ItemUseBallText08 +.printTransferredToPCText + call PrintText + jr .done + +.oldManCaughtMon + ld hl, ItemUseBallText05 + +.printMessage + call PrintText + call ClearSprites + +.done + ld a, [wBattleType] + and a ; is this the old man battle? + ret nz ; if so, don't remove a ball from the bag + +; Remove a ball from the bag. + ld hl, wNumBagItems + inc a + ld [wItemQuantity], a + jp RemoveItemFromInventory + +ItemUseBallText00: +;"It dodged the thrown ball!" +;"This pokemon can't be caught" + TX_FAR _ItemUseBallText00 + db "@" +ItemUseBallText01: +;"You missed the pokemon!" + TX_FAR _ItemUseBallText01 + db "@" +ItemUseBallText02: +;"Darn! The pokemon broke free!" + TX_FAR _ItemUseBallText02 + db "@" +ItemUseBallText03: +;"Aww! It appeared to be caught!" + TX_FAR _ItemUseBallText03 + db "@" +ItemUseBallText04: +;"Shoot! It was so close too!" + TX_FAR _ItemUseBallText04 + db "@" +ItemUseBallText05: +;"All right! {MonName} was caught!" +;play sound + TX_FAR _ItemUseBallText05 + TX_SFX_CAUGHT_MON + TX_BLINK + db "@" +ItemUseBallText07: +;"X was transferred to Bill's PC" + TX_FAR _ItemUseBallText07 + db "@" +ItemUseBallText08: +;"X was transferred to someone's PC" + TX_FAR _ItemUseBallText08 + db "@" + +ItemUseBallText06: +;"New DEX data will be added..." +;play sound + TX_FAR _ItemUseBallText06 + TX_SFX_DEX_PAGE_ADDED + TX_BLINK + db "@" + +ItemUseTownMap: + ld a, [wIsInBattle] + and a + jp nz, ItemUseNotTime + jpba DisplayTownMap + +ItemUseBicycle: + ld a, [wIsInBattle] + and a + jp nz, ItemUseNotTime + ld a, [wWalkBikeSurfState] + ld [wWalkBikeSurfStateCopy], a + cp 2 ; is the player surfing? + jp z, ItemUseNotTime + dec a ; is player already bicycling? + jr nz, .tryToGetOnBike +.getOffBike + call ItemUseReloadOverworldData + xor a + ld [wWalkBikeSurfState], a ; change player state to walking + call PlayDefaultMusic ; play walking music + ld hl, GotOffBicycleText + jr .printText +.tryToGetOnBike + call IsBikeRidingAllowed + jp nc, NoCyclingAllowedHere + call ItemUseReloadOverworldData + xor a ; no keys pressed + ld [hJoyHeld], a ; current joypad state + inc a + ld [wWalkBikeSurfState], a ; change player state to bicycling + ld hl, GotOnBicycleText + call PlayDefaultMusic ; play bike riding music +.printText + jp PrintText + +; used for Surf out-of-battle effect +ItemUseSurfboard: + ld a, [wWalkBikeSurfState] + ld [wWalkBikeSurfStateCopy], a + cp 2 ; is the player already surfing? + jr z, .tryToStopSurfing +.tryToSurf + call IsNextTileShoreOrWater + jp c, SurfingAttemptFailed + ld hl, TilePairCollisionsWater + call CheckForTilePairCollisions + jp c, SurfingAttemptFailed +.surf + call .makePlayerMoveForward + ld hl, wd730 + set 7, [hl] + ld a, 2 + ld [wWalkBikeSurfState], a ; change player state to surfing + call PlayDefaultMusic ; play surfing music + ld hl, SurfingGotOnText + jp PrintText +.tryToStopSurfing + xor a + ld [hSpriteIndexOrTextID], a + ld d, 16 ; talking range in pixels (normal range) + call IsSpriteInFrontOfPlayer2 + res 7, [hl] + ld a, [hSpriteIndexOrTextID] + and a ; is there a sprite in the way? + jr nz, .cannotStopSurfing + ld hl, TilePairCollisionsWater + call CheckForTilePairCollisions + jr c, .cannotStopSurfing + ld hl, wTilesetCollisionPtr ; pointer to list of passable tiles + ld a, [hli] + ld h, [hl] + ld l, a ; hl now points to passable tiles + ld a, [wTileInFrontOfPlayer] ; tile in front of the player + ld b, a +.passableTileLoop + ld a, [hli] + cp b + jr z, .stopSurfing + cp $ff + jr nz, .passableTileLoop +.cannotStopSurfing + ld hl, SurfingNoPlaceToGetOffText + jp PrintText +.stopSurfing + call .makePlayerMoveForward + ld hl, wd730 + set 7, [hl] + xor a + ld [wWalkBikeSurfState], a ; change player state to walking + dec a + ld [wJoyIgnore], a + call PlayDefaultMusic ; play walking music + jp LoadWalkingPlayerSpriteGraphics +; uses a simulated button press to make the player move forward +.makePlayerMoveForward + ld a, [wPlayerDirection] ; direction the player is going + bit PLAYER_DIR_BIT_UP, a + ld b, D_UP + jr nz, .storeSimulatedButtonPress + bit PLAYER_DIR_BIT_DOWN, a + ld b, D_DOWN + jr nz, .storeSimulatedButtonPress + bit PLAYER_DIR_BIT_LEFT, a + ld b, D_LEFT + jr nz, .storeSimulatedButtonPress + ld b, D_RIGHT +.storeSimulatedButtonPress + ld a, b + ld [wSimulatedJoypadStatesEnd], a + xor a + ld [wWastedByteCD39], a + inc a + ld [wSimulatedJoypadStatesIndex], a + ret + +SurfingGotOnText: + TX_FAR _SurfingGotOnText + db "@" + +SurfingNoPlaceToGetOffText: + TX_FAR _SurfingNoPlaceToGetOffText + db "@" + +ItemUsePokedex: + predef_jump ShowPokedexMenu + +ItemUseEvoStone: + ld a, [wIsInBattle] + and a + jp nz, ItemUseNotTime + ld a, [wWhichPokemon] + push af + ld a, [wcf91] + ld [wEvoStoneItemID], a + push af + ld a, EVO_STONE_PARTY_MENU + ld [wPartyMenuTypeOrMessageID], a + ld a, $ff + ld [wUpdateSpritesEnabled], a + call DisplayPartyMenu + pop bc + jr c, .canceledItemUse + ld a, b + ld [wcf91], a + ld a, $01 + ld [wForceEvolution], a + ld a, SFX_HEAL_AILMENT + call PlaySoundWaitForCurrent + call WaitForSoundToFinish + callab TryEvolvingMon ; try to evolve pokemon + ld a, [wEvolutionOccurred] + and a + jr z, .noEffect + pop af + ld [wWhichPokemon], a + ld hl, wNumBagItems + ld a, 1 ; remove 1 stone + ld [wItemQuantity], a + jp RemoveItemFromInventory +.noEffect + call ItemUseNoEffect +.canceledItemUse + xor a + ld [wActionResultOrTookBattleTurn], a ; item not used + pop af + ret + +ItemUseVitamin: + ld a, [wIsInBattle] + and a + jp nz, ItemUseNotTime + +ItemUseMedicine: + ld a, [wPartyCount] + and a + jp z, .emptyParty + ld a, [wWhichPokemon] + push af + ld a, [wcf91] + push af + ld a, USE_ITEM_PARTY_MENU + ld [wPartyMenuTypeOrMessageID], a + ld a, $ff + ld [wUpdateSpritesEnabled], a + ld a, [wPseudoItemID] + and a ; using Softboiled? + jr z, .notUsingSoftboiled +; if using softboiled + call GoBackToPartyMenu + jr .getPartyMonDataAddress +.emptyParty + ld hl, .emptyPartyText + xor a + ld [wActionResultOrTookBattleTurn], a ; item use failed + jp PrintText +.emptyPartyText + text "You don't have" + line "any #MON!" + prompt +.notUsingSoftboiled + call DisplayPartyMenu +.getPartyMonDataAddress + jp c, .canceledItemUse + ld hl, wPartyMons + ld bc, wPartyMon2 - wPartyMon1 + ld a, [wWhichPokemon] + call AddNTimes + ld a, [wWhichPokemon] + ld [wUsedItemOnWhichPokemon], a + ld d, a + ld a, [wcf91] + ld e, a + ld [wd0b5], a + pop af + ld [wcf91], a + pop af + ld [wWhichPokemon], a + ld a, [wPseudoItemID] + and a ; using Softboiled? + jr z, .checkItemType +; if using softboiled + ld a, [wWhichPokemon] + cp d ; is the pokemon trying to use softboiled on itself? + jr z, ItemUseMedicine ; if so, force another choice +.checkItemType + ld a, [wcf91] + cp REVIVE + jr nc, .healHP ; if it's a Revive or Max Revive + cp FULL_HEAL + jr z, .cureStatusAilment ; if it's a Full Heal + cp HP_UP + jp nc, .useVitamin ; if it's a vitamin or Rare Candy + cp FULL_RESTORE + jr nc, .healHP ; if it's a Full Restore or one of the potions +; fall through if it's one of the status-specific healing items +.cureStatusAilment + ld bc, wPartyMon1Status - wPartyMon1 + add hl, bc ; hl now points to status + ld a, [wcf91] + lb bc, ANTIDOTE_MSG, 1 << PSN + cp ANTIDOTE + jr z, .checkMonStatus + lb bc, BURN_HEAL_MSG, 1 << BRN + cp BURN_HEAL + jr z, .checkMonStatus + lb bc, ICE_HEAL_MSG, 1 << FRZ + cp ICE_HEAL + jr z, .checkMonStatus + lb bc, AWAKENING_MSG, SLP + cp AWAKENING + jr z, .checkMonStatus + lb bc, PARALYZ_HEAL_MSG, 1 << PAR + cp PARLYZ_HEAL + jr z, .checkMonStatus + lb bc, FULL_HEAL_MSG, $ff ; Full Heal +.checkMonStatus + ld a, [hl] ; pokemon's status + and c ; does the pokemon have a status ailment the item can cure? + jp z, .healingItemNoEffect +; if the pokemon has a status the item can heal + xor a + ld [hl], a ; remove the status ailment in the party data + ld a, b + ld [wPartyMenuTypeOrMessageID], a ; the message to display for the item used + ld a, [wPlayerMonNumber] + cp d ; is pokemon the item was used on active in battle? + jp nz, .doneHealing +; if it is active in battle + xor a + ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data + push hl + ld hl, wPlayerBattleStatus3 + res BADLY_POISONED, [hl] ; heal Toxic status + pop hl + ld bc, wPartyMon1Stats - wPartyMon1Status + add hl, bc ; hl now points to party stats + ld de, wBattleMonStats + ld bc, NUM_STATS * 2 + call CopyData ; copy party stats to in-battle stat data + predef DoubleOrHalveSelectedStats + jp .doneHealing +.healHP + inc hl ; hl = address of current HP + ld a, [hli] + ld b, a + ld [wHPBarOldHP+1], a + ld a, [hl] + ld c, a + ld [wHPBarOldHP], a ; current HP stored at wHPBarOldHP (2 bytes, big-endian) + or b + jr nz, .notFainted +.fainted + ld a, [wcf91] + cp REVIVE + jr z, .updateInBattleFaintedData + cp MAX_REVIVE + jr z, .updateInBattleFaintedData + jp .healingItemNoEffect +.updateInBattleFaintedData + ld a, [wIsInBattle] + and a + jr z, .compareCurrentHPToMaxHP + push hl + push de + push bc + ld a, [wUsedItemOnWhichPokemon] + ld c, a + ld hl, wPartyFoughtCurrentEnemyFlags + ld b, FLAG_TEST + predef FlagActionPredef + ld a, c + and a + jr z, .next + ld a, [wUsedItemOnWhichPokemon] + ld c, a + ld hl, wPartyGainExpFlags + ld b, FLAG_SET + predef FlagActionPredef +.next + pop bc + pop de + pop hl + jr .compareCurrentHPToMaxHP +.notFainted + ld a, [wcf91] + cp REVIVE + jp z, .healingItemNoEffect + cp MAX_REVIVE + jp z, .healingItemNoEffect +.compareCurrentHPToMaxHP + push hl + push bc + ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1) + add hl, bc ; hl now points to max HP + pop bc + ld a, [hli] + cp b + jr nz, .skipComparingLSB ; no need to compare the LSB's if the MSB's don't match + ld a, [hl] + cp c +.skipComparingLSB + pop hl + jr nz, .notFullHP +.fullHP ; if the pokemon's current HP equals its max HP + ld a, [wcf91] + cp FULL_RESTORE + jp nz, .healingItemNoEffect + inc hl + inc hl + ld a, [hld] ; status ailment + and a ; does the pokemon have a status ailment? + jp z, .healingItemNoEffect + ld a, FULL_HEAL + ld [wcf91], a + dec hl + dec hl + dec hl + jp .cureStatusAilment +.notFullHP ; if the pokemon's current HP doesn't equal its max HP + xor a + ld [wLowHealthAlarm], a ;disable low health alarm + ld [wChannelSoundIDs + Ch5], a + push hl + push de + ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1) + add hl, bc ; hl now points to max HP + ld a, [hli] + ld [wHPBarMaxHP+1], a + ld a, [hl] + ld [wHPBarMaxHP], a ; max HP stored at wHPBarMaxHP (2 bytes, big-endian) + ld a, [wPseudoItemID] + and a ; using Softboiled? + jp z, .notUsingSoftboiled2 +; if using softboiled + ld hl, wHPBarMaxHP + ld a, [hli] + push af + ld a, [hli] + push af + ld a, [hli] + push af + ld a, [hl] + push af + ld hl, wPartyMon1MaxHP + ld a, [wWhichPokemon] + ld bc, wPartyMon2 - wPartyMon1 + call AddNTimes + ld a, [hli] + ld [wHPBarMaxHP + 1], a + ld [H_DIVIDEND], a + ld a, [hl] + ld [wHPBarMaxHP], a + ld [H_DIVIDEND + 1], a + ld a, 5 + ld [H_DIVISOR], a + ld b, 2 ; number of bytes + call Divide ; get 1/5 of max HP of pokemon that used Softboiled + ld bc, (wPartyMon1HP + 1) - (wPartyMon1MaxHP + 1) + add hl, bc ; hl now points to LSB of current HP of pokemon that used Softboiled +; subtract 1/5 of max HP from current HP of pokemon that used Softboiled + ld a, [H_QUOTIENT + 3] + push af + ld b, a + ld a, [hl] + ld [wHPBarOldHP], a + sub b + ld [hld], a + ld [wHPBarNewHP], a + ld a, [H_QUOTIENT + 2] + ld b, a + ld a, [hl] + ld [wHPBarOldHP+1], a + sbc b + ld [hl], a + ld [wHPBarNewHP+1], a + coord hl, 4, 1 + ld a, [wWhichPokemon] + ld bc, 2 * SCREEN_WIDTH + call AddNTimes ; calculate coordinates of HP bar of pokemon that used Softboiled + ld a, SFX_HEAL_HP + call PlaySoundWaitForCurrent + ld a, [hFlags_0xFFF6] + set 0, a + ld [hFlags_0xFFF6], a + ld a, $02 + ld [wHPBarType], a + predef UpdateHPBar2 ; animate HP bar decrease of pokemon that used Softboiled + ld a, [hFlags_0xFFF6] + res 0, a + ld [hFlags_0xFFF6], a + pop af + ld b, a ; store heal amount (1/5 of max HP) + ld hl, wHPBarOldHP + 1 + pop af + ld [hld], a + pop af + ld [hld], a + pop af + ld [hld], a + pop af + ld [hl], a + jr .addHealAmount +.notUsingSoftboiled2 + ld a, [wcf91] + cp SODA_POP + ld b, 60 ; Soda Pop heal amount + jr z, .addHealAmount + ld b, 80 ; Lemonade heal amount + jr nc, .addHealAmount + cp FRESH_WATER + ld b, 50 ; Fresh Water heal amount + jr z, .addHealAmount + cp SUPER_POTION + ld b, 200 ; Hyper Potion heal amount + jr c, .addHealAmount + ld b, 50 ; Super Potion heal amount + jr z, .addHealAmount + ld b, 20 ; Potion heal amount +.addHealAmount + pop de + pop hl + ld a, [hl] + add b + ld [hld], a + ld [wHPBarNewHP], a + ld a, [hl] + ld [wHPBarNewHP+1], a + jr nc, .noCarry + inc [hl] + ld a, [hl] + ld [wHPBarNewHP + 1], a +.noCarry + push de + inc hl + ld d, h + ld e, l ; de now points to current HP + ld hl, (wPartyMon1MaxHP + 1) - (wPartyMon1HP + 1) + add hl, de ; hl now points to max HP + ld a, [wcf91] + cp REVIVE + jr z, .setCurrentHPToHalfMaxHP + ld a, [hld] + ld b, a + ld a, [de] + sub b + dec de + ld b, [hl] + ld a, [de] + sbc b + jr nc, .setCurrentHPToMaxHp ; if current HP exceeds max HP after healing + ld a, [wcf91] + cp HYPER_POTION + jr c, .setCurrentHPToMaxHp ; if using a Full Restore or Max Potion + cp MAX_REVIVE + jr z, .setCurrentHPToMaxHp ; if using a Max Revive + jr .updateInBattleData +.setCurrentHPToHalfMaxHP + dec hl + dec de + ld a, [hli] + srl a + ld [de], a + ld [wHPBarNewHP+1], a + ld a, [hl] + rr a + inc de + ld [de], a + ld [wHPBarNewHP], a + dec de + jr .doneHealingPartyHP +.setCurrentHPToMaxHp + ld a, [hli] + ld [de], a + ld [wHPBarNewHP+1], a + inc de + ld a, [hl] + ld [de], a + ld [wHPBarNewHP], a + dec de +.doneHealingPartyHP ; done updating the pokemon's current HP in the party data structure + ld a, [wcf91] + cp FULL_RESTORE + jr nz, .updateInBattleData + ld bc, wPartyMon1Status - (wPartyMon1MaxHP + 1) + add hl, bc + xor a + ld [hl], a ; remove the status ailment in the party data +.updateInBattleData + ld h, d + ld l, e + pop de + ld a, [wPlayerMonNumber] + cp d ; is pokemon the item was used on active in battle? + jr nz, .calculateHPBarCoords +; copy party HP to in-battle HP + ld a, [hli] + ld [wBattleMonHP], a + ld a, [hld] + ld [wBattleMonHP + 1], a + ld a, [wcf91] + cp FULL_RESTORE + jr nz, .calculateHPBarCoords + xor a + ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data +.calculateHPBarCoords + ld hl, wOAMBuffer + $90 + ld bc, 2 * SCREEN_WIDTH + inc d +.calculateHPBarCoordsLoop + add hl, bc + dec d + jr nz, .calculateHPBarCoordsLoop + jr .doneHealing +.healingItemNoEffect + call ItemUseNoEffect + jp .done +.doneHealing + ld a, [wPseudoItemID] + and a ; using Softboiled? + jr nz, .skipRemovingItem ; no item to remove if using Softboiled + push hl + call RemoveUsedItem + pop hl +.skipRemovingItem + ld a, [wcf91] + cp FULL_RESTORE + jr c, .playStatusAilmentCuringSound + cp FULL_HEAL + jr z, .playStatusAilmentCuringSound + ld a, SFX_HEAL_HP + call PlaySoundWaitForCurrent + ld a, [hFlags_0xFFF6] + set 0, a + ld [hFlags_0xFFF6], a + ld a, $02 + ld [wHPBarType], a + predef UpdateHPBar2 ; animate the HP bar lengthening + ld a, [hFlags_0xFFF6] + res 0, a + ld [hFlags_0xFFF6], a + ld a, REVIVE_MSG + ld [wPartyMenuTypeOrMessageID], a + ld a, [wcf91] + cp REVIVE + jr z, .showHealingItemMessage + cp MAX_REVIVE + jr z, .showHealingItemMessage + ld a, POTION_MSG + ld [wPartyMenuTypeOrMessageID], a + jr .showHealingItemMessage +.playStatusAilmentCuringSound + ld a, SFX_HEAL_AILMENT + call PlaySoundWaitForCurrent +.showHealingItemMessage + xor a + ld [H_AUTOBGTRANSFERENABLED], a + call ClearScreen + dec a + ld [wUpdateSpritesEnabled], a + call RedrawPartyMenu ; redraws the party menu and displays the message + ld a, 1 + ld [H_AUTOBGTRANSFERENABLED], a + ld c, 50 + call DelayFrames + call WaitForTextScrollButtonPress + jr .done +.canceledItemUse + xor a + ld [wActionResultOrTookBattleTurn], a ; item use failed + pop af + pop af +.done + ld a, [wPseudoItemID] + and a ; using Softboiled? + ret nz ; if so, return + call GBPalWhiteOut + call z, RunDefaultPaletteCommand + ld a, [wIsInBattle] + and a + ret nz + jp ReloadMapData +.useVitamin + push hl + ld a, [hl] + ld [wd0b5], a + ld [wd11e], a + ld bc, wPartyMon1Level - wPartyMon1 + add hl, bc ; hl now points to level + ld a, [hl] ; a = level + ld [wCurEnemyLVL], a ; store level + call GetMonHeader + push de + ld a, d + ld hl, wPartyMonNicks + call GetPartyMonName + pop de + pop hl + ld a, [wcf91] + cp RARE_CANDY + jp z, .useRareCandy + push hl + sub HP_UP + add a + ld bc, wPartyMon1HPExp - wPartyMon1 + add hl, bc + add l + ld l, a + jr nc, .noCarry2 + inc h +.noCarry2 + ld a, 10 + ld b, a + ld a, [hl] ; a = MSB of stat experience of the appropriate stat + cp 100 ; is there already at least 25600 (256 * 100) stat experience? + jr nc, .vitaminNoEffect ; if so, vitamins can't add any more + add b ; add 2560 (256 * 10) stat experience + jr nc, .noCarry3 ; a carry should be impossible here, so this will always jump + ld a, 255 +.noCarry3 + ld [hl], a + pop hl + call .recalculateStats + ld hl, VitaminText + ld a, [wcf91] + sub HP_UP - 1 + ld c, a +.statNameLoop ; loop to get the address of the name of the stat the vitamin increases + dec c + jr z, .gotStatName +.statNameInnerLoop + ld a, [hli] + ld b, a + ld a, $50 + cp b + jr nz, .statNameInnerLoop + jr .statNameLoop +.gotStatName + ld de, wcf4b + ld bc, 10 + call CopyData ; copy the stat's name to wcf4b + ld a, SFX_HEAL_AILMENT + call PlaySound + ld hl, VitaminStatRoseText + call PrintText + jp RemoveUsedItem +.vitaminNoEffect + pop hl + ld hl, VitaminNoEffectText + call PrintText + jp GBPalWhiteOut +.recalculateStats + ld bc, wPartyMon1Stats - wPartyMon1 + add hl, bc + ld d, h + ld e, l ; de now points to stats + ld bc, (wPartyMon1Exp + 2) - wPartyMon1Stats + add hl, bc ; hl now points to LSB of experience + ld b, 1 + jp CalcStats ; recalculate stats +.useRareCandy + push hl + ld bc, wPartyMon1Level - wPartyMon1 + add hl, bc ; hl now points to level + ld a, [hl] ; a = level + cp MAX_LEVEL + jr z, .vitaminNoEffect ; can't raise level above 100 + inc a + ld [hl], a ; store incremented level + ld [wCurEnemyLVL], a + push hl + push de + ld d, a + callab CalcExperience ; calculate experience for next level and store it at $ff96 + pop de + pop hl + ld bc, wPartyMon1Exp - wPartyMon1Level + add hl, bc ; hl now points to MSB of experience +; update experience to minimum for new level + ld a, [hExperience] + ld [hli], a + ld a, [hExperience + 1] + ld [hli], a + ld a, [hExperience + 2] + ld [hl], a + pop hl + ld a, [wWhichPokemon] + push af + ld a, [wcf91] + push af + push de + push hl + ld bc, wPartyMon1MaxHP - wPartyMon1 + add hl, bc ; hl now points to MSB of max HP + ld a, [hli] + ld b, a + ld c, [hl] + pop hl + push bc + push hl + call .recalculateStats + pop hl + ld bc, (wPartyMon1MaxHP + 1) - wPartyMon1 + add hl, bc ; hl now points to LSB of max HP + pop bc + ld a, [hld] + sub c + ld c, a + ld a, [hl] + sbc b + ld b, a ; bc = the amount of max HP gained from leveling up +; add the amount gained to the current HP + ld de, (wPartyMon1HP + 1) - wPartyMon1MaxHP + add hl, de ; hl now points to LSB of current HP + ld a, [hl] + add c + ld [hld], a + ld a, [hl] + adc b + ld [hl], a + ld a, RARE_CANDY_MSG + ld [wPartyMenuTypeOrMessageID], a + call RedrawPartyMenu + pop de + ld a, d + ld [wWhichPokemon], a + ld a, e + ld [wd11e], a + xor a ; PLAYER_PARTY_DATA + ld [wMonDataLocation], a + call LoadMonData + ld d, $01 + callab PrintStatsBox ; display new stats text box + call WaitForTextScrollButtonPress ; wait for button press + xor a ; PLAYER_PARTY_DATA + ld [wMonDataLocation], a + predef LearnMoveFromLevelUp ; learn level up move, if any + xor a + ld [wForceEvolution], a + callab TryEvolvingMon ; evolve pokemon, if appropriate + ld a, $01 + ld [wUpdateSpritesEnabled], a + pop af + ld [wcf91], a + pop af + ld [wWhichPokemon], a + jp RemoveUsedItem + +VitaminStatRoseText: + TX_FAR _VitaminStatRoseText + db "@" + +VitaminNoEffectText: + TX_FAR _VitaminNoEffectText + db "@" + +VitaminText: + db "HEALTH@" + db "ATTACK@" + db "DEFENSE@" + db "SPEED@" + db "SPECIAL@" + +ItemUseBait: + ld hl, ThrewBaitText + call PrintText + ld hl, wEnemyMonActualCatchRate ; catch rate + srl [hl] ; halve catch rate + ld a, BAIT_ANIM + ld hl, wSafariBaitFactor ; bait factor + ld de, wSafariEscapeFactor ; escape factor + jr BaitRockCommon + +ItemUseRock: + ld hl, ThrewRockText + call PrintText + ld hl, wEnemyMonActualCatchRate ; catch rate + ld a, [hl] + add a ; double catch rate + jr nc, .noCarry + ld a, $ff +.noCarry + ld [hl], a + ld a, ROCK_ANIM + ld hl, wSafariEscapeFactor ; escape factor + ld de, wSafariBaitFactor ; bait factor + +BaitRockCommon: + ld [wAnimationID], a + xor a + ld [wAnimationType], a + ld [H_WHOSETURN], a + ld [de], a ; zero escape factor (for bait), zero bait factor (for rock) +.randomLoop ; loop until a random number less than 5 is generated + call Random + and 7 + cp 5 + jr nc, .randomLoop + inc a ; increment the random number, giving a range from 1 to 5 inclusive + ld b, a + ld a, [hl] + add b ; increase bait factor (for bait), increase escape factor (for rock) + jr nc, .noCarry + ld a, $ff +.noCarry + ld [hl], a + predef MoveAnimation ; do animation + ld c, 70 + jp DelayFrames + +ThrewBaitText: + TX_FAR _ThrewBaitText + db "@" + +ThrewRockText: + TX_FAR _ThrewRockText + db "@" + +; also used for Dig out-of-battle effect +ItemUseEscapeRope: + ld a, [wIsInBattle] + and a + jr nz, .notUsable + ld a, [wCurMap] + cp AGATHAS_ROOM + jr z, .notUsable + ld a, [wCurMapTileset] + ld b, a + ld hl, EscapeRopeTilesets +.loop + ld a, [hli] + cp $ff + jr z, .notUsable + cp b + jr nz, .loop + ld hl, wd732 + set 3, [hl] + set 6, [hl] + ld hl, wd72e + res 4, [hl] + ResetEvent EVENT_IN_SAFARI_ZONE + xor a + ld [wNumSafariBalls], a + ld [wSafariZoneGateCurScript], a + inc a + ld [wEscapedFromBattle], a + ld [wActionResultOrTookBattleTurn], a ; item used + ld a, [wPseudoItemID] + and a ; using Dig? + ret nz ; if so, return + call ItemUseReloadOverworldData + ld c, 30 + call DelayFrames + jp RemoveUsedItem +.notUsable + jp ItemUseNotTime + +EscapeRopeTilesets: + db FOREST, CEMETERY, CAVERN, FACILITY, INTERIOR + db $ff ; terminator + +ItemUseRepel: + ld b, 100 + +ItemUseRepelCommon: + ld a, [wIsInBattle] + and a + jp nz, ItemUseNotTime + ld a, b + ld [wRepelRemainingSteps], a + jp PrintItemUseTextAndRemoveItem + +; handles X Accuracy item +ItemUseXAccuracy: + ld a, [wIsInBattle] + and a + jp z, ItemUseNotTime + ld hl, wPlayerBattleStatus2 + set USING_X_ACCURACY, [hl] ; X Accuracy bit + jp PrintItemUseTextAndRemoveItem + +; This function is bugged and never works. It always jumps to ItemUseNotTime. +; The Card Key is handled in a different way. +ItemUseCardKey: + xor a + ld [wUnusedD71F], a + call GetTileAndCoordsInFrontOfPlayer + ld a, [GetTileAndCoordsInFrontOfPlayer] + cp $18 + jr nz, .next0 + ld hl, CardKeyTable1 + jr .next1 +.next0 + cp $24 + jr nz, .next2 + ld hl, CardKeyTable2 + jr .next1 +.next2 + cp $5e + jp nz, ItemUseNotTime + ld hl, CardKeyTable3 +.next1 + ld a, [wCurMap] + ld b, a +.loop + ld a, [hli] + cp $ff + jp z, ItemUseNotTime + cp b + jr nz, .nextEntry1 + ld a, [hli] + cp d + jr nz, .nextEntry2 + ld a, [hli] + cp e + jr nz, .nextEntry3 + ld a, [hl] + ld [wUnusedD71F], a + jr .done +.nextEntry1 + inc hl +.nextEntry2 + inc hl +.nextEntry3 + inc hl + jr .loop +.done + ld hl, ItemUseText00 + call PrintText + ld hl, wd728 + set 7, [hl] + ret + +; These tables are probably supposed to be door locations in Silph Co., +; but they are unused. +; The reason there are 3 tables is unknown. + +; Format: +; 00: Map ID +; 01: Y +; 02: X +; 03: ID? + +CardKeyTable1: + db SILPH_CO_2F,$04,$04,$00 + db SILPH_CO_2F,$04,$05,$01 + db SILPH_CO_4F,$0C,$04,$02 + db SILPH_CO_4F,$0C,$05,$03 + db SILPH_CO_7F,$06,$0A,$04 + db SILPH_CO_7F,$06,$0B,$05 + db SILPH_CO_9F,$04,$12,$06 + db SILPH_CO_9F,$04,$13,$07 + db SILPH_CO_10F,$08,$0A,$08 + db SILPH_CO_10F,$08,$0B,$09 + db $ff + +CardKeyTable2: + db SILPH_CO_3F,$08,$09,$0A + db SILPH_CO_3F,$09,$09,$0B + db SILPH_CO_5F,$04,$07,$0C + db SILPH_CO_5F,$05,$07,$0D + db SILPH_CO_6F,$0C,$05,$0E + db SILPH_CO_6F,$0D,$05,$0F + db SILPH_CO_8F,$08,$07,$10 + db SILPH_CO_8F,$09,$07,$11 + db SILPH_CO_9F,$08,$03,$12 + db SILPH_CO_9F,$09,$03,$13 + db $ff + +CardKeyTable3: + db SILPH_CO_11F,$08,$09,$14 + db SILPH_CO_11F,$09,$09,$15 + db $ff + +ItemUsePokedoll: + ld a, [wIsInBattle] + dec a + jp nz, ItemUseNotTime + ld a, $01 + ld [wEscapedFromBattle], a + jp PrintItemUseTextAndRemoveItem + +ItemUseGuardSpec: + ld a, [wIsInBattle] + and a + jp z, ItemUseNotTime + ld hl, wPlayerBattleStatus2 + set PROTECTED_BY_MIST, [hl] ; Mist bit + jp PrintItemUseTextAndRemoveItem + +ItemUseSuperRepel: + ld b, 200 + jp ItemUseRepelCommon + +ItemUseMaxRepel: + ld b, 250 + jp ItemUseRepelCommon + +ItemUseDireHit: + ld a, [wIsInBattle] + and a + jp z, ItemUseNotTime + ld hl, wPlayerBattleStatus2 + set GETTING_PUMPED, [hl] ; Focus Energy bit + jp PrintItemUseTextAndRemoveItem + +ItemUseXStat: + ld a, [wIsInBattle] + and a + jr nz, .inBattle + call ItemUseNotTime + ld a, 2 + ld [wActionResultOrTookBattleTurn], a ; item not used + ret +.inBattle + ld hl, wPlayerMoveNum + ld a, [hli] + push af ; save [wPlayerMoveNum] + ld a, [hl] + push af ; save [wPlayerMoveEffect] + push hl + ld a, [wcf91] + sub X_ATTACK - ATTACK_UP1_EFFECT + ld [hl], a ; store player move effect + call PrintItemUseTextAndRemoveItem + ld a, XSTATITEM_ANIM ; X stat item animation ID + ld [wPlayerMoveNum], a + call LoadScreenTilesFromBuffer1 ; restore saved screen + call Delay3 + xor a + ld [H_WHOSETURN], a ; set turn to player's turn + callba StatModifierUpEffect ; do stat increase move + pop hl + pop af + ld [hld], a ; restore [wPlayerMoveEffect] + pop af + ld [hl], a ; restore [wPlayerMoveNum] + ret + +ItemUsePokeflute: + ld a, [wIsInBattle] + and a + jr nz, .inBattle +; if not in battle + call ItemUseReloadOverworldData + ld a, [wCurMap] + cp ROUTE_12 + jr nz, .notRoute12 + CheckEvent EVENT_BEAT_ROUTE12_SNORLAX + jr nz, .noSnorlaxToWakeUp +; if the player hasn't beaten Route 12 Snorlax + ld hl, Route12SnorlaxFluteCoords + call ArePlayerCoordsInArray + jr nc, .noSnorlaxToWakeUp + ld hl, PlayedFluteHadEffectText + call PrintText + SetEvent EVENT_FIGHT_ROUTE12_SNORLAX + ret +.notRoute12 + cp ROUTE_16 + jr nz, .noSnorlaxToWakeUp + CheckEvent EVENT_BEAT_ROUTE16_SNORLAX + jr nz, .noSnorlaxToWakeUp +; if the player hasn't beaten Route 16 Snorlax + ld hl, Route16SnorlaxFluteCoords + call ArePlayerCoordsInArray + jr nc, .noSnorlaxToWakeUp + ld hl, PlayedFluteHadEffectText + call PrintText + SetEvent EVENT_FIGHT_ROUTE16_SNORLAX + ret +.noSnorlaxToWakeUp + ld hl, PlayedFluteNoEffectText + jp PrintText +.inBattle + xor a + ld [wWereAnyMonsAsleep], a + ld b, ~SLP & $ff + ld hl, wPartyMon1Status + call WakeUpEntireParty + ld a, [wIsInBattle] + dec a ; is it a trainer battle? + jr z, .skipWakingUpEnemyParty +; if it's a trainer battle + ld hl, wEnemyMon1Status + call WakeUpEntireParty +.skipWakingUpEnemyParty + ld hl, wBattleMonStatus + ld a, [hl] + and b ; remove Sleep status + ld [hl], a + ld hl, wEnemyMonStatus + ld a, [hl] + and b ; remove Sleep status + ld [hl], a + call LoadScreenTilesFromBuffer2 ; restore saved screen + ld a, [wWereAnyMonsAsleep] + and a ; were any pokemon asleep before playing the flute? + ld hl, PlayedFluteNoEffectText + jp z, PrintText ; if no pokemon were asleep +; if some pokemon were asleep + ld hl, PlayedFluteHadEffectText + call PrintText + ld a, [wLowHealthAlarm] + and $80 + jr nz, .skipMusic + call WaitForSoundToFinish ; wait for sound to end + callba Music_PokeFluteInBattle ; play in-battle pokeflute music +.musicWaitLoop ; wait for music to finish playing + ld a, [wChannelSoundIDs + Ch7] + and a ; music off? + jr nz, .musicWaitLoop +.skipMusic + ld hl, FluteWokeUpText + jp PrintText + +; wakes up all party pokemon +; INPUT: +; hl must point to status of first pokemon in party (player's or enemy's) +; b must equal ~SLP +; [wWereAnyMonsAsleep] should be initialized to 0 +; OUTPUT: +; [wWereAnyMonsAsleep]: set to 1 if any pokemon were asleep +WakeUpEntireParty: + ld de, 44 + ld c, 6 +.loop + ld a, [hl] + push af + and SLP ; is pokemon asleep? + jr z, .notAsleep + ld a, 1 + ld [wWereAnyMonsAsleep], a ; indicate that a pokemon had to be woken up +.notAsleep + pop af + and b ; remove Sleep status + ld [hl], a + add hl, de + dec c + jr nz, .loop + ret + +; Format: +; 00: Y +; 01: X +Route12SnorlaxFluteCoords: + db 62,9 ; one space West of Snorlax + db 61,10 ; one space North of Snorlax + db 63,10 ; one space South of Snorlax + db 62,11 ; one space East of Snorlax + db $ff ; terminator + +; Format: +; 00: Y +; 01: X +Route16SnorlaxFluteCoords: + db 10,27 ; one space East of Snorlax + db 10,25 ; one space West of Snorlax + db $ff ; terminator + +PlayedFluteNoEffectText: + TX_FAR _PlayedFluteNoEffectText + db "@" + +FluteWokeUpText: + TX_FAR _FluteWokeUpText + db "@" + +PlayedFluteHadEffectText: + TX_FAR _PlayedFluteHadEffectText + TX_BLINK + TX_ASM + ld a, [wIsInBattle] + and a + jr nz, .done +; play out-of-battle pokeflute music + ld a, $ff + call PlaySound ; turn off music + ld a, SFX_POKEFLUTE + ld c, BANK(SFX_Pokeflute) + call PlayMusic +.musicWaitLoop ; wait for music to finish playing + ld a, [wChannelSoundIDs + Ch3] + cp SFX_POKEFLUTE + jr z, .musicWaitLoop + call PlayDefaultMusic ; start playing normal music again +.done + jp TextScriptEnd ; end text + +ItemUseCoinCase: + ld a, [wIsInBattle] + and a + jp nz, ItemUseNotTime + ld hl, CoinCaseNumCoinsText + jp PrintText + +CoinCaseNumCoinsText: + TX_FAR _CoinCaseNumCoinsText + db "@" + +ItemUseOldRod: + call FishingInit + jp c, ItemUseNotTime + lb bc, 5, MAGIKARP + ld a, $1 ; set bite + jr RodResponse + +ItemUseGoodRod: + call FishingInit + jp c, ItemUseNotTime +.RandomLoop + call Random + srl a + jr c, .SetBite + and %11 + cp 2 + jr nc, .RandomLoop + ; choose which monster appears + ld hl, GoodRodMons + add a + ld c, a + ld b, 0 + add hl, bc + ld b, [hl] + inc hl + ld c, [hl] + and a +.SetBite + ld a, 0 + rla + xor 1 + jr RodResponse + +INCLUDE "data/good_rod.asm" + +ItemUseSuperRod: + call FishingInit + jp c, ItemUseNotTime + call ReadSuperRodData + ld a, e +RodResponse: + ld [wRodResponse], a + + dec a ; is there a bite? + jr nz, .next + ; if yes, store level and species data + ld a, 1 + ld [wMoveMissed], a + ld a, b ; level + ld [wCurEnemyLVL], a + ld a, c ; species + ld [wCurOpponent], a + +.next + ld hl, wWalkBikeSurfState + ld a, [hl] ; store the value in a + push af + push hl + ld [hl], 0 + callba FishingAnim + pop hl + pop af + ld [hl], a + ret + +; checks if fishing is possible and if so, runs initialization code common to all rods +; unsets carry if fishing is possible, sets carry if not +FishingInit: + ld a, [wIsInBattle] + and a + jr z, .notInBattle + scf ; can't fish during battle + ret +.notInBattle + call IsNextTileShoreOrWater + ret c + ld a, [wWalkBikeSurfState] + cp 2 ; Surfing? + jr z, .surfing + call ItemUseReloadOverworldData + ld hl, ItemUseText00 + call PrintText + ld a, SFX_HEAL_AILMENT + call PlaySound + ld c, 80 + call DelayFrames + and a + ret +.surfing + scf ; can't fish when surfing + ret + +ItemUseOaksParcel: + jp ItemUseNotYoursToUse + +ItemUseItemfinder: + ld a, [wIsInBattle] + and a + jp nz, ItemUseNotTime + call ItemUseReloadOverworldData + callba HiddenItemNear ; check for hidden items + ld hl, ItemfinderFoundNothingText + jr nc, .printText ; if no hidden items + ld c, 4 +.loop + ld a, SFX_HEALING_MACHINE + call PlaySoundWaitForCurrent + ld a, SFX_PURCHASE + call PlaySoundWaitForCurrent + dec c + jr nz, .loop + ld hl, ItemfinderFoundItemText +.printText + jp PrintText + +ItemfinderFoundItemText: + TX_FAR _ItemfinderFoundItemText + db "@" + +ItemfinderFoundNothingText: + TX_FAR _ItemfinderFoundNothingText + db "@" + +ItemUsePPUp: + ld a, [wIsInBattle] + and a + jp nz, ItemUseNotTime + +ItemUsePPRestore: + ld a, [wWhichPokemon] + push af + ld a, [wcf91] + ld [wPPRestoreItem], a +.chooseMon + xor a + ld [wUpdateSpritesEnabled], a + ld a, USE_ITEM_PARTY_MENU + ld [wPartyMenuTypeOrMessageID], a + call DisplayPartyMenu + jr nc, .chooseMove + jp .itemNotUsed +.chooseMove + ld a, [wPPRestoreItem] + cp ELIXER + jp nc, .useElixir ; if Elixir or Max Elixir + ld a, $02 + ld [wMoveMenuType], a + ld hl, RaisePPWhichTechniqueText + ld a, [wPPRestoreItem] + cp ETHER ; is it a PP Up? + jr c, .printWhichTechniqueMessage ; if so, print the raise PP message + ld hl, RestorePPWhichTechniqueText ; otherwise, print the restore PP message +.printWhichTechniqueMessage + call PrintText + xor a + ld [wPlayerMoveListIndex], a + callab MoveSelectionMenu ; move selection menu + ld a, 0 + ld [wPlayerMoveListIndex], a + jr nz, .chooseMon + ld hl, wPartyMon1Moves + ld bc, wPartyMon2 - wPartyMon1 + call GetSelectedMoveOffset + push hl + ld a, [hl] + ld [wd11e], a + call GetMoveName + call CopyStringToCF4B ; copy name to wcf4b + pop hl + ld a, [wPPRestoreItem] + cp ETHER + jr nc, .useEther ; if Ether or Max Ether +.usePPUp + ld bc, wPartyMon1PP - wPartyMon1Moves + add hl, bc + ld a, [hl] ; move PP + cp 3 << 6 ; have 3 PP Ups already been used? + jr c, .PPNotMaxedOut + ld hl, PPMaxedOutText + call PrintText + jr .chooseMove +.PPNotMaxedOut + ld a, [hl] + add 1 << 6 ; increase PP Up count by 1 + ld [hl], a + ld a, 1 ; 1 PP Up used + ld [wd11e], a + call RestoreBonusPP ; add the bonus PP to current PP + ld hl, PPIncreasedText + call PrintText +.done + pop af + ld [wWhichPokemon], a + call GBPalWhiteOut + call RunDefaultPaletteCommand + jp RemoveUsedItem +.afterRestoringPP ; after using a (Max) Ether/Elixir + ld a, [wWhichPokemon] + ld b, a + ld a, [wPlayerMonNumber] + cp b ; is the pokemon whose PP was restored active in battle? + jr nz, .skipUpdatingInBattleData + ld hl, wPartyMon1PP + ld bc, wPartyMon2 - wPartyMon1 + call AddNTimes + ld de, wBattleMonPP + ld bc, 4 + call CopyData ; copy party data to in-battle data +.skipUpdatingInBattleData + ld a, SFX_HEAL_AILMENT + call PlaySound + ld hl, PPRestoredText + call PrintText + jr .done +.useEther + call .restorePP + jr nz, .afterRestoringPP + jp .noEffect +; unsets zero flag if PP was restored, sets zero flag if not +; however, this is bugged for Max Ethers and Max Elixirs (see below) +.restorePP + xor a ; PLAYER_PARTY_DATA + ld [wMonDataLocation], a + call GetMaxPP + ld hl, wPartyMon1Moves + ld bc, wPartyMon2 - wPartyMon1 + call GetSelectedMoveOffset + ld bc, wPartyMon1PP - wPartyMon1Moves + add hl, bc ; hl now points to move's PP + ld a, [wMaxPP] + ld b, a + ld a, [wPPRestoreItem] + cp MAX_ETHER + jr z, .fullyRestorePP + ld a, [hl] ; move PP + and %00111111 ; lower 6 bit bits store current PP + cp b ; does current PP equal max PP? + ret z ; if so, return + add 10 ; increase current PP by 10 +; b holds the max PP amount and b will hold the new PP amount. +; So, if the new amount meets or exceeds the max amount, +; cap the amount to the max amount by leaving b unchanged. +; Otherwise, store the new amount in b. + cp b ; does the new amount meet or exceed the maximum? + jr nc, .storeNewAmount + ld b, a +.storeNewAmount + ld a, [hl] ; move PP + and %11000000 ; PP Up counter bits + add b + ld [hl], a + ret +.fullyRestorePP + ld a, [hl] ; move PP +; Note that this code has a bug. It doesn't mask out the upper two bits, which +; are used to count how many PP Ups have been used on the move. So, Max Ethers +; and Max Elixirs will not be detected as having no effect on a move with full +; PP if the move has had any PP Ups used on it. + cp b ; does current PP equal max PP? + ret z + jr .storeNewAmount +.useElixir +; decrement the item ID so that ELIXER becomes ETHER and MAX_ELIXER becomes MAX_ETHER + ld hl, wPPRestoreItem + dec [hl] + dec [hl] + xor a + ld hl, wCurrentMenuItem + ld [hli], a + ld [hl], a ; zero the counter for number of moves that had their PP restored + ld b, 4 +; loop through each move and restore PP +.elixirLoop + push bc + ld hl, wPartyMon1Moves + ld bc, wPartyMon2 - wPartyMon1 + call GetSelectedMoveOffset + ld a, [hl] + and a ; does the current slot have a move? + jr z, .nextMove + call .restorePP + jr z, .nextMove +; if some PP was restored + ld hl, wTileBehindCursor ; counter for number of moves that had their PP restored + inc [hl] +.nextMove + ld hl, wCurrentMenuItem + inc [hl] + pop bc + dec b + jr nz, .elixirLoop + ld a, [wTileBehindCursor] + and a ; did any moves have their PP restored? + jp nz, .afterRestoringPP +.noEffect + call ItemUseNoEffect +.itemNotUsed + call GBPalWhiteOut + call RunDefaultPaletteCommand + pop af + xor a + ld [wActionResultOrTookBattleTurn], a ; item use failed + ret + +RaisePPWhichTechniqueText: + TX_FAR _RaisePPWhichTechniqueText + db "@" + +RestorePPWhichTechniqueText: + TX_FAR _RestorePPWhichTechniqueText + db "@" + +PPMaxedOutText: + TX_FAR _PPMaxedOutText + db "@" + +PPIncreasedText: + TX_FAR _PPIncreasedText + db "@" + +PPRestoredText: + TX_FAR _PPRestoredText + db "@" + +; for items that can't be used from the Item menu +UnusableItem: + jp ItemUseNotTime + +ItemUseTMHM: + ld a, [wIsInBattle] + and a + jp nz, ItemUseNotTime + ld a, [wcf91] + sub TM_01 + push af + jr nc, .skipAdding + add 55 ; if item is an HM, add 55 +.skipAdding + inc a + ld [wd11e], a + predef TMToMove ; get move ID from TM/HM ID + ld a, [wd11e] + ld [wMoveNum], a + call GetMoveName + call CopyStringToCF4B ; copy name to wcf4b + pop af + ld hl, BootedUpTMText + jr nc, .printBootedUpMachineText + ld hl, BootedUpHMText +.printBootedUpMachineText + call PrintText + ld hl, TeachMachineMoveText + call PrintText + coord hl, 14, 7 + lb bc, 8, 15 + ld a, TWO_OPTION_MENU + ld [wTextBoxID], a + call DisplayTextBoxID ; yes/no menu + ld a, [wCurrentMenuItem] + and a + jr z, .useMachine + ld a, 2 + ld [wActionResultOrTookBattleTurn], a ; item not used + ret +.useMachine + ld a, [wWhichPokemon] + push af + ld a, [wcf91] + push af +.chooseMon + ld hl, wcf4b + ld de, wTempMoveNameBuffer + ld bc, 14 + call CopyData ; save the move name because DisplayPartyMenu will overwrite it + ld a, $ff + ld [wUpdateSpritesEnabled], a + ld a, TMHM_PARTY_MENU + ld [wPartyMenuTypeOrMessageID], a + call DisplayPartyMenu + push af + ld hl, wTempMoveNameBuffer + ld de, wcf4b + ld bc, 14 + call CopyData + pop af + jr nc, .checkIfAbleToLearnMove +; if the player canceled teaching the move + pop af + pop af + call GBPalWhiteOutWithDelay3 + call ClearSprites + call RunDefaultPaletteCommand + jp LoadScreenTilesFromBuffer1 ; restore saved screen +.checkIfAbleToLearnMove + predef CanLearnTM ; check if the pokemon can learn the move + push bc + ld a, [wWhichPokemon] + ld hl, wPartyMonNicks + call GetPartyMonName + pop bc + ld a, c + and a ; can the pokemon learn the move? + jr nz, .checkIfAlreadyLearnedMove +; if the pokemon can't learn the move + ld a, SFX_DENIED + call PlaySoundWaitForCurrent + ld hl, MonCannotLearnMachineMoveText + call PrintText + jr .chooseMon +.checkIfAlreadyLearnedMove + callab CheckIfMoveIsKnown ; check if the pokemon already knows the move + jr c, .chooseMon + predef LearnMove ; teach move + pop af + ld [wcf91], a + pop af + ld [wWhichPokemon], a + ld a, b + and a + ret z + ld a, [wcf91] + call IsItemHM + ret c + jp RemoveUsedItem + +BootedUpTMText: + TX_FAR _BootedUpTMText + db "@" + +BootedUpHMText: + TX_FAR _BootedUpHMText + db "@" + +TeachMachineMoveText: + TX_FAR _TeachMachineMoveText + db "@" + +MonCannotLearnMachineMoveText: + TX_FAR _MonCannotLearnMachineMoveText + db "@" + +PrintItemUseTextAndRemoveItem: + ld hl, ItemUseText00 + call PrintText + ld a, SFX_HEAL_AILMENT + call PlaySound + call WaitForTextScrollButtonPress ; wait for button press + +RemoveUsedItem: + ld hl, wNumBagItems + ld a, 1 ; one item + ld [wItemQuantity], a + jp RemoveItemFromInventory + +ItemUseNoEffect: + ld hl, ItemUseNoEffectText + jr ItemUseFailed + +ItemUseNotTime: + ld hl, ItemUseNotTimeText + jr ItemUseFailed + +ItemUseNotYoursToUse: + ld hl, ItemUseNotYoursToUseText + jr ItemUseFailed + +ThrowBallAtTrainerMon: + call RunDefaultPaletteCommand + call LoadScreenTilesFromBuffer1 ; restore saved screen + call Delay3 + ld a, TOSS_ANIM + ld [wAnimationID], a + predef MoveAnimation ; do animation + ld hl, ThrowBallAtTrainerMonText1 + call PrintText + ld hl, ThrowBallAtTrainerMonText2 + call PrintText + jr RemoveUsedItem + +NoCyclingAllowedHere: + ld hl, NoCyclingAllowedHereText + jr ItemUseFailed + +BoxFullCannotThrowBall: + ld hl, BoxFullCannotThrowBallText + jr ItemUseFailed + +SurfingAttemptFailed: + ld hl, NoSurfingHereText + +ItemUseFailed: + xor a + ld [wActionResultOrTookBattleTurn], a ; item use failed + jp PrintText + +ItemUseNotTimeText: + TX_FAR _ItemUseNotTimeText + db "@" + +ItemUseNotYoursToUseText: + TX_FAR _ItemUseNotYoursToUseText + db "@" + +ItemUseNoEffectText: + TX_FAR _ItemUseNoEffectText + db "@" + +ThrowBallAtTrainerMonText1: + TX_FAR _ThrowBallAtTrainerMonText1 + db "@" + +ThrowBallAtTrainerMonText2: + TX_FAR _ThrowBallAtTrainerMonText2 + db "@" + +NoCyclingAllowedHereText: + TX_FAR _NoCyclingAllowedHereText + db "@" + +NoSurfingHereText: + TX_FAR _NoSurfingHereText + db "@" + +BoxFullCannotThrowBallText: + TX_FAR _BoxFullCannotThrowBallText + db "@" + +ItemUseText00: + TX_FAR _ItemUseText001 + TX_LINE + TX_FAR _ItemUseText002 + db "@" + +GotOnBicycleText: + TX_FAR _GotOnBicycleText1 + TX_LINE + TX_FAR _GotOnBicycleText2 + db "@" + +GotOffBicycleText: + TX_FAR _GotOffBicycleText1 + TX_LINE + TX_FAR _GotOffBicycleText2 + db "@" + +; restores bonus PP (from PP Ups) when healing at a pokemon center +; also, when a PP Up is used, it increases the current PP by one PP Up bonus +; INPUT: +; [wWhichPokemon] = index of pokemon in party +; [wCurrentMenuItem] = index of move (when using a PP Up) +RestoreBonusPP: + ld hl, wPartyMon1Moves + ld bc, wPartyMon2 - wPartyMon1 + ld a, [wWhichPokemon] + call AddNTimes + push hl + ld de, wNormalMaxPPList - 1 + predef LoadMovePPs ; loads the normal max PP of each of the pokemon's moves to wNormalMaxPPList + pop hl + ld c, wPartyMon1PP - wPartyMon1Moves + ld b, 0 + add hl, bc ; hl now points to move 1 PP + ld de, wNormalMaxPPList + ld b, 0 ; initialize move counter to zero +; loop through the pokemon's moves +.loop + inc b + ld a, b + cp 5 ; reached the end of the pokemon's moves? + ret z ; if so, return + ld a, [wUsingPPUp] + dec a ; using a PP Up? + jr nz, .skipMenuItemIDCheck +; if using a PP Up, check if this is the move it's being used on + ld a, [wCurrentMenuItem] + inc a + cp b + jr nz, .nextMove +.skipMenuItemIDCheck + ld a, [hl] + and %11000000 ; have any PP Ups been used? + call nz, AddBonusPP ; if so, add bonus PP +.nextMove + inc hl + inc de + jr .loop + +; adds bonus PP from PP Ups to current PP +; 1/5 of normal max PP (capped at 7) is added for each PP Up +; INPUT: +; [de] = normal max PP +; [hl] = move PP +AddBonusPP: + push bc + ld a, [de] ; normal max PP of move + ld [H_DIVIDEND + 3], a + xor a + ld [H_DIVIDEND], a + ld [H_DIVIDEND + 1], a + ld [H_DIVIDEND + 2], a + ld a, 5 + ld [H_DIVISOR], a + ld b, 4 + call Divide + ld a, [hl] ; move PP + ld b, a + swap a + and %00001111 + srl a + srl a + ld c, a ; c = number of PP Ups used +.loop + ld a, [H_QUOTIENT + 3] + cp 8 ; is the amount greater than or equal to 8? + jr c, .addAmount + ld a, 7 ; cap the amount at 7 +.addAmount + add b + ld b, a + ld a, [wUsingPPUp] + dec a ; is the player using a PP Up right now? + jr z, .done ; if so, only add the bonus once + dec c + jr nz, .loop +.done + ld [hl], b + pop bc + ret + +; gets max PP of a pokemon's move (including PP from PP Ups) +; INPUT: +; [wWhichPokemon] = index of pokemon within party/box +; [wMonDataLocation] = pokemon source +; 00: player's party +; 01: enemy's party +; 02: current box +; 03: daycare +; 04: player's in-battle pokemon +; [wCurrentMenuItem] = move index +; OUTPUT: +; [wMaxPP] = max PP +GetMaxPP: + ld a, [wMonDataLocation] + and a + ld hl, wPartyMon1Moves + ld bc, wPartyMon2 - wPartyMon1 + jr z, .sourceWithMultipleMon + ld hl, wEnemyMon1Moves + dec a + jr z, .sourceWithMultipleMon + ld hl, wBoxMon1Moves + ld bc, wBoxMon2 - wBoxMon1 + dec a + jr z, .sourceWithMultipleMon + ld hl, wDayCareMonMoves + dec a + jr z, .sourceWithOneMon + ld hl, wBattleMonMoves ; player's in-battle pokemon +.sourceWithOneMon + call GetSelectedMoveOffset2 + jr .next +.sourceWithMultipleMon + call GetSelectedMoveOffset +.next + ld a, [hl] + dec a + push hl + ld hl, Moves + ld bc, MoveEnd - Moves + call AddNTimes + ld de, wcd6d + ld a, BANK(Moves) + call FarCopyData + ld de, wcd6d + 5 ; PP is byte 5 of move data + ld a, [de] + ld b, a ; b = normal max PP + pop hl + push bc + ld bc, wPartyMon1PP - wPartyMon1Moves ; PP offset if not player's in-battle pokemon data + ld a, [wMonDataLocation] + cp 4 ; player's in-battle pokemon? + jr nz, .addPPOffset + ld bc, wBattleMonPP - wBattleMonMoves ; PP offset if player's in-battle pokemon data +.addPPOffset + add hl, bc + ld a, [hl] ; a = current PP + and %11000000 ; get PP Up count + pop bc + or b ; place normal max PP in 6 lower bits of a + ld h, d + ld l, e + inc hl ; hl = wcd73 + ld [hl], a + xor a ; add the bonus for the existing PP Up count + ld [wUsingPPUp], a + call AddBonusPP ; add bonus PP from PP Ups + ld a, [hl] + and %00111111 ; mask out the PP Up count + ld [wMaxPP], a ; store max PP + ret + +GetSelectedMoveOffset: + ld a, [wWhichPokemon] + call AddNTimes + +GetSelectedMoveOffset2: + ld a, [wCurrentMenuItem] + ld c, a + ld b, 0 + add hl, bc + ret + +; confirms the item toss and then tosses the item +; INPUT: +; hl = address of inventory (either wNumBagItems or wNumBoxItems) +; [wcf91] = item ID +; [wWhichPokemon] = index of item within inventory +; [wItemQuantity] = quantity to toss +; OUTPUT: +; clears carry flag if the item is tossed, sets carry flag if not +TossItem_:: + push hl + ld a, [wcf91] + call IsItemHM + pop hl + jr c, .tooImportantToToss + push hl + call IsKeyItem_ + ld a, [wIsKeyItem] + pop hl + and a + jr nz, .tooImportantToToss + push hl + ld a, [wcf91] + ld [wd11e], a + call GetItemName + call CopyStringToCF4B ; copy name to wcf4b + ld hl, IsItOKToTossItemText + call PrintText + coord hl, 14, 7 + lb bc, 8, 15 + ld a, TWO_OPTION_MENU + ld [wTextBoxID], a + call DisplayTextBoxID ; yes/no menu + ld a, [wMenuExitMethod] + cp CHOSE_SECOND_ITEM + pop hl + scf + ret z ; return if the player chose No +; if the player chose Yes + push hl + ld a, [wWhichPokemon] + call RemoveItemFromInventory + ld a, [wcf91] + ld [wd11e], a + call GetItemName + call CopyStringToCF4B ; copy name to wcf4b + ld hl, ThrewAwayItemText + call PrintText + pop hl + and a + ret +.tooImportantToToss + push hl + ld hl, TooImportantToTossText + call PrintText + pop hl + scf + ret + +ThrewAwayItemText: + TX_FAR _ThrewAwayItemText + db "@" + +IsItOKToTossItemText: + TX_FAR _IsItOKToTossItemText + db "@" + +TooImportantToTossText: + TX_FAR _TooImportantToTossText + db "@" + +; checks if an item is a key item +; INPUT: +; [wcf91] = item ID +; OUTPUT: +; [wIsKeyItem] = result +; 00: item is not key item +; 01: item is key item +IsKeyItem_:: + ld a, $01 + ld [wIsKeyItem], a + ld a, [wcf91] + cp HM_01 ; is the item an HM or TM? + jr nc, .checkIfItemIsHM +; if the item is not an HM or TM + push af + ld hl, KeyItemBitfield + ld de, wBuffer + ld bc, 15 ; only 11 bytes are actually used + call CopyData + pop af + dec a + ld c, a + ld hl, wBuffer + ld b, FLAG_TEST + predef FlagActionPredef + ld a, c + and a + ret nz +.checkIfItemIsHM + ld a, [wcf91] + call IsItemHM + ret c + xor a + ld [wIsKeyItem], a + ret + +INCLUDE "data/key_items.asm" + +SendNewMonToBox: + ld de, wNumInBox + ld a, [de] + inc a + ld [de], a + ld a, [wcf91] + ld [wd0b5], a + ld c, a +.asm_e7b1 + inc de + ld a, [de] + ld b, a + ld a, c + ld c, b + ld [de], a + cp $ff + jr nz, .asm_e7b1 + call GetMonHeader + ld hl, wBoxMonOT + ld bc, NAME_LENGTH + ld a, [wNumInBox] + dec a + jr z, .asm_e7ee + dec a + call AddNTimes + push hl + ld bc, NAME_LENGTH + add hl, bc + ld d, h + ld e, l + pop hl + ld a, [wNumInBox] + dec a + ld b, a +.asm_e7db + push bc + push hl + ld bc, NAME_LENGTH + call CopyData + pop hl + ld d, h + ld e, l + ld bc, -NAME_LENGTH + add hl, bc + pop bc + dec b + jr nz, .asm_e7db +.asm_e7ee + ld hl, wPlayerName + ld de, wBoxMonOT + ld bc, NAME_LENGTH + call CopyData + ld a, [wNumInBox] + dec a + jr z, .asm_e82a + ld hl, wBoxMonNicks + ld bc, NAME_LENGTH + dec a + call AddNTimes + push hl + ld bc, NAME_LENGTH + add hl, bc + ld d, h + ld e, l + pop hl + ld a, [wNumInBox] + dec a + ld b, a +.asm_e817 + push bc + push hl + ld bc, NAME_LENGTH + call CopyData + pop hl + ld d, h + ld e, l + ld bc, -NAME_LENGTH + add hl, bc + pop bc + dec b + jr nz, .asm_e817 +.asm_e82a + ld hl, wBoxMonNicks + ld a, NAME_MON_SCREEN + ld [wNamingScreenType], a + predef AskName + ld a, [wNumInBox] + dec a + jr z, .asm_e867 + ld hl, wBoxMons + ld bc, wBoxMon2 - wBoxMon1 + dec a + call AddNTimes + push hl + ld bc, wBoxMon2 - wBoxMon1 + add hl, bc + ld d, h + ld e, l + pop hl + ld a, [wNumInBox] + dec a + ld b, a +.asm_e854 + push bc + push hl + ld bc, wBoxMon2 - wBoxMon1 + call CopyData + pop hl + ld d, h + ld e, l + ld bc, wBoxMon1 - wBoxMon2 + add hl, bc + pop bc + dec b + jr nz, .asm_e854 +.asm_e867 + ld a, [wEnemyMonLevel] + ld [wEnemyMonBoxLevel], a + ld hl, wEnemyMon + ld de, wBoxMon1 + ld bc, wEnemyMonDVs - wEnemyMon + call CopyData + ld hl, wPlayerID + ld a, [hli] + ld [de], a + inc de + ld a, [hl] + ld [de], a + inc de + push de + ld a, [wCurEnemyLVL] + ld d, a + callab CalcExperience + pop de + ld a, [hExperience] + ld [de], a + inc de + ld a, [hExperience + 1] + ld [de], a + inc de + ld a, [hExperience + 2] + ld [de], a + inc de + xor a + ld b, NUM_STATS * 2 +.asm_e89f + ld [de], a + inc de + dec b + jr nz, .asm_e89f + ld hl, wEnemyMonDVs + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + ld hl, wEnemyMonPP + ld b, NUM_MOVES +.asm_e8b1 + ld a, [hli] + inc de + ld [de], a + dec b + jr nz, .asm_e8b1 + ret + +; checks if the tile in front of the player is a shore or water tile +; used for surfing and fishing +; unsets carry if it is, sets carry if not +IsNextTileShoreOrWater: + ld a, [wCurMapTileset] + ld hl, WaterTilesets + ld de, 1 + call IsInArray + jr nc, .notShoreOrWater + ld a, [wCurMapTileset] + cp SHIP_PORT ; Vermilion Dock tileset + ld a, [wTileInFrontOfPlayer] ; tile in front of player + jr z, .skipShoreTiles ; if it's the Vermilion Dock tileset + cp $48 ; eastern shore tile in Safari Zone + jr z, .shoreOrWater + cp $32 ; usual eastern shore tile + jr z, .shoreOrWater +.skipShoreTiles + cp $14 ; water tile + jr z, .shoreOrWater +.notShoreOrWater + scf + ret +.shoreOrWater + and a + ret + +INCLUDE "data/water_tilesets.asm" + +ReadSuperRodData: +; return e = 2 if no fish on this map +; return e = 1 if a bite, bc = level,species +; return e = 0 if no bite + ld a, [wCurMap] + ld de, 3 ; each fishing group is three bytes wide + ld hl, SuperRodData + call IsInArray + jr c, .ReadFishingGroup + ld e, $2 ; $2 if no fishing groups found + ret + +.ReadFishingGroup +; hl points to the fishing group entry in the index + inc hl ; skip map id + + ; read fishing group address + ld a, [hli] + ld h, [hl] + ld l, a + + ld b, [hl] ; how many mons in group + inc hl ; point to data + ld e, $0 ; no bite yet + +.RandomLoop + call Random + srl a + ret c ; 50% chance of no battle + + and %11 ; 2-bit random number + cp b + jr nc, .RandomLoop ; if a is greater than the number of mons, regenerate + + ; get the mon + add a + ld c, a + ld b, $0 + add hl, bc + ld b, [hl] ; level + inc hl + ld c, [hl] ; species + ld e, $1 ; $1 if there's a bite + ret + +INCLUDE "data/super_rod.asm" + +; reloads map view and processes sprite data +; for items that cause the overworld to be displayed +ItemUseReloadOverworldData: + call LoadCurrentMapView + jp UpdateSprites + +; creates a list at wBuffer of maps where the mon in [wd11e] can be found. +; this is used by the pokedex to display locations the mon can be found on the map. +FindWildLocationsOfMon: + ld hl, WildDataPointers + ld de, wBuffer + ld c, $0 +.loop + inc hl + ld a, [hld] + inc a + jr z, .done + push hl + ld a, [hli] + ld h, [hl] + ld l, a + ld a, [hli] + and a + call nz, CheckMapForMon ; land + ld a, [hli] + and a + call nz, CheckMapForMon ; water + pop hl + inc hl + inc hl + inc c + jr .loop +.done + ld a, $ff ; list terminator + ld [de], a + ret + +CheckMapForMon: + inc hl + ld b, $a +.loop + ld a, [wd11e] + cp [hl] + jr nz, .nextEntry + ld a, c + ld [de], a + inc de +.nextEntry + inc hl + inc hl + dec b + jr nz, .loop + dec hl + ret diff --git a/engine/items/items.asm b/engine/items/items.asm deleted file mode 100755 index 6e7bed1e..00000000 --- a/engine/items/items.asm +++ /dev/null @@ -1,2986 +0,0 @@ -UseItem_:: - ld a, 1 - ld [wActionResultOrTookBattleTurn], a ; initialise to success value - ld a, [wcf91] ;contains item_ID - cp HM_01 - jp nc, ItemUseTMHM - ld hl, ItemUsePtrTable - dec a - add a - ld c, a - ld b, 0 - add hl, bc - ld a, [hli] - ld h, [hl] - ld l, a - jp hl - -ItemUsePtrTable: - dw ItemUseBall ; MASTER_BALL - dw ItemUseBall ; ULTRA_BALL - dw ItemUseBall ; GREAT_BALL - dw ItemUseBall ; POKE_BALL - dw ItemUseTownMap ; TOWN_MAP - dw ItemUseBicycle ; BICYCLE - dw ItemUseSurfboard ; out-of-battle Surf effect - dw ItemUseBall ; SAFARI_BALL - dw ItemUsePokedex ; POKEDEX - dw ItemUseEvoStone ; MOON_STONE - dw ItemUseMedicine ; ANTIDOTE - dw ItemUseMedicine ; BURN_HEAL - dw ItemUseMedicine ; ICE_HEAL - dw ItemUseMedicine ; AWAKENING - dw ItemUseMedicine ; PARLYZ_HEAL - dw ItemUseMedicine ; FULL_RESTORE - dw ItemUseMedicine ; MAX_POTION - dw ItemUseMedicine ; HYPER_POTION - dw ItemUseMedicine ; SUPER_POTION - dw ItemUseMedicine ; POTION - dw ItemUseBait ; BOULDERBADGE - dw ItemUseRock ; CASCADEBADGE - dw UnusableItem ; THUNDERBADGE - dw UnusableItem ; RAINBOWBADGE - dw UnusableItem ; SOULBADGE - dw UnusableItem ; MARSHBADGE - dw UnusableItem ; VOLCANOBADGE - dw UnusableItem ; EARTHBADGE - dw ItemUseEscapeRope ; ESCAPE_ROPE - dw ItemUseRepel ; REPEL - dw UnusableItem ; OLD_AMBER - dw ItemUseEvoStone ; FIRE_STONE - dw ItemUseEvoStone ; THUNDER_STONE - dw ItemUseEvoStone ; WATER_STONE - dw ItemUseVitamin ; HP_UP - dw ItemUseVitamin ; PROTEIN - dw ItemUseVitamin ; IRON - dw ItemUseVitamin ; CARBOS - dw ItemUseVitamin ; CALCIUM - dw ItemUseVitamin ; RARE_CANDY - dw UnusableItem ; DOME_FOSSIL - dw UnusableItem ; HELIX_FOSSIL - dw UnusableItem ; SECRET_KEY - dw UnusableItem - dw UnusableItem ; BIKE_VOUCHER - dw ItemUseXAccuracy ; X_ACCURACY - dw ItemUseEvoStone ; LEAF_STONE - dw ItemUseCardKey ; CARD_KEY - dw UnusableItem ; NUGGET - dw UnusableItem ; ??? PP_UP - dw ItemUsePokedoll ; POKE_DOLL - dw ItemUseMedicine ; FULL_HEAL - dw ItemUseMedicine ; REVIVE - dw ItemUseMedicine ; MAX_REVIVE - dw ItemUseGuardSpec ; GUARD_SPEC - dw ItemUseSuperRepel ; SUPER_REPL - dw ItemUseMaxRepel ; MAX_REPEL - dw ItemUseDireHit ; DIRE_HIT - dw UnusableItem ; COIN - dw ItemUseMedicine ; FRESH_WATER - dw ItemUseMedicine ; SODA_POP - dw ItemUseMedicine ; LEMONADE - dw UnusableItem ; S_S_TICKET - dw UnusableItem ; GOLD_TEETH - dw ItemUseXStat ; X_ATTACK - dw ItemUseXStat ; X_DEFEND - dw ItemUseXStat ; X_SPEED - dw ItemUseXStat ; X_SPECIAL - dw ItemUseCoinCase ; COIN_CASE - dw ItemUseOaksParcel ; OAKS_PARCEL - dw ItemUseItemfinder ; ITEMFINDER - dw UnusableItem ; SILPH_SCOPE - dw ItemUsePokeflute ; POKE_FLUTE - dw UnusableItem ; LIFT_KEY - dw UnusableItem ; EXP_ALL - dw ItemUseOldRod ; OLD_ROD - dw ItemUseGoodRod ; GOOD_ROD - dw ItemUseSuperRod ; SUPER_ROD - dw ItemUsePPUp ; PP_UP (real one) - dw ItemUsePPRestore ; ETHER - dw ItemUsePPRestore ; MAX_ETHER - dw ItemUsePPRestore ; ELIXER - dw ItemUsePPRestore ; MAX_ELIXER - -ItemUseBall: - -; Balls can't be used out of battle. - ld a, [wIsInBattle] - and a - jp z, ItemUseNotTime - -; Balls can't catch trainers' Pokémon. - dec a - jp nz, ThrowBallAtTrainerMon - -; If this is for the old man battle, skip checking if the party & box are full. - ld a, [wBattleType] - dec a - jr z, .canUseBall - - ld a, [wPartyCount] ; is party full? - cp PARTY_LENGTH - jr nz, .canUseBall - ld a, [wNumInBox] ; is box full? - cp MONS_PER_BOX - jp z, BoxFullCannotThrowBall - -.canUseBall - xor a - ld [wCapturedMonSpecies], a - - ld a, [wBattleType] - cp BATTLE_TYPE_SAFARI - jr nz, .skipSafariZoneCode - -.safariZone - ld hl, wNumSafariBalls - dec [hl] ; remove a Safari Ball - -.skipSafariZoneCode - call RunDefaultPaletteCommand - - ld a, $43 ; successful capture value - ld [wPokeBallAnimData], a - - call LoadScreenTilesFromBuffer1 - ld hl, ItemUseText00 - call PrintText - -; If the player is fighting an unidentified ghost, set the value that indicates -; the Pokémon can't be caught and skip the capture calculations. - callab IsGhostBattle - ld b, $10 ; can't be caught value - jp z, .setAnimData - - ld a, [wBattleType] - dec a - jr nz, .notOldManBattle - -.oldManBattle - ld hl, wGrassRate - ld de, wPlayerName - ld bc, NAME_LENGTH - call CopyData ; save the player's name in the Wild Monster data (part of the Cinnabar Island Missingno. glitch) - jp .captured - -.notOldManBattle -; If the player is fighting the ghost Marowak, set the value that indicates the -; Pokémon can't be caught and skip the capture calculations. - ld a, [wCurMap] - cp POKEMON_TOWER_6F - jr nz, .loop - ld a, [wEnemyMonSpecies2] - cp MAROWAK - ld b, $10 ; can't be caught value - jp z, .setAnimData - -; Get the first random number. Let it be called Rand1. -; Rand1 must be within a certain range according the kind of ball being thrown. -; The ranges are as follows. -; Poké Ball: [0, 255] -; Great Ball: [0, 200] -; Ultra/Safari Ball: [0, 150] -; Loop until an acceptable number is found. - -.loop - call Random - ld b, a - -; Get the item ID. - ld hl, wcf91 - ld a, [hl] - -; The Master Ball always succeeds. - cp MASTER_BALL - jp z, .captured - -; Anything will do for the basic Poké Ball. - cp POKE_BALL - jr z, .checkForAilments - -; If it's a Great/Ultra/Safari Ball and Rand1 is greater than 200, try again. - ld a, 200 - cp b - jr c, .loop - -; Less than or equal to 200 is good enough for a Great Ball. - ld a, [hl] - cp GREAT_BALL - jr z, .checkForAilments - -; If it's an Ultra/Safari Ball and Rand1 is greater than 150, try again. - ld a, 150 - cp b - jr c, .loop - -.checkForAilments -; Pokémon can be caught more easily with a status ailment. -; Depending on the status ailment, a certain value will be subtracted from -; Rand1. Let this value be called Status. -; The larger Status is, the more easily the Pokémon can be caught. -; no status ailment: Status = 0 -; Burn/Paralysis/Poison: Status = 12 -; Freeze/Sleep: Status = 25 -; If Status is greater than Rand1, the Pokémon will be caught for sure. - ld a, [wEnemyMonStatus] - and a - jr z, .skipAilmentValueSubtraction ; no ailments - and 1 << FRZ | SLP - ld c, 12 - jr z, .notFrozenOrAsleep - ld c, 25 -.notFrozenOrAsleep - ld a, b - sub c - jp c, .captured - ld b, a - -.skipAilmentValueSubtraction - push bc ; save (Rand1 - Status) - -; Calculate MaxHP * 255. - xor a - ld [H_MULTIPLICAND], a - ld hl, wEnemyMonMaxHP - ld a, [hli] - ld [H_MULTIPLICAND + 1], a - ld a, [hl] - ld [H_MULTIPLICAND + 2], a - ld a, 255 - ld [H_MULTIPLIER], a - call Multiply - -; Determine BallFactor. It's 8 for Great Balls and 12 for the others. - ld a, [wcf91] - cp GREAT_BALL - ld a, 12 - jr nz, .skip1 - ld a, 8 - -.skip1 -; Note that the results of all division operations are floored. - -; Calculate (MaxHP * 255) / BallFactor. - ld [H_DIVISOR], a - ld b, 4 ; number of bytes in dividend - call Divide - -; Divide the enemy's current HP by 4. HP is not supposed to exceed 999 so -; the result should fit in a. If the division results in a quotient of 0, -; change it to 1. - ld hl, wEnemyMonHP - ld a, [hli] - ld b, a - ld a, [hl] - srl b - rr a - srl b - rr a - and a - jr nz, .skip2 - inc a - -.skip2 -; Let W = ((MaxHP * 255) / BallFactor) / max(HP / 4, 1). Calculate W. - ld [H_DIVISOR], a - ld b, 4 - call Divide - -; If W > 255, store 255 in [H_QUOTIENT + 3]. -; Let X = min(W, 255) = [H_QUOTIENT + 3]. - ld a, [H_QUOTIENT + 2] - and a - jr z, .skip3 - ld a, 255 - ld [H_QUOTIENT + 3], a - -.skip3 - pop bc ; b = Rand1 - Status - -; If Rand1 - Status > CatchRate, the ball fails to capture the Pokémon. - ld a, [wEnemyMonActualCatchRate] - cp b - jr c, .failedToCapture - -; If W > 255, the ball captures the Pokémon. - ld a, [H_QUOTIENT + 2] - and a - jr nz, .captured - - call Random ; Let this random number be called Rand2. - -; If Rand2 > X, the ball fails to capture the Pokémon. - ld b, a - ld a, [H_QUOTIENT + 3] - cp b - jr c, .failedToCapture - -.captured - jr .skipShakeCalculations - -.failedToCapture - ld a, [H_QUOTIENT + 3] - ld [wPokeBallCaptureCalcTemp], a ; Save X. - -; Calculate CatchRate * 100. - xor a - ld [H_MULTIPLICAND], a - ld [H_MULTIPLICAND + 1], a - ld a, [wEnemyMonActualCatchRate] - ld [H_MULTIPLICAND + 2], a - ld a, 100 - ld [H_MULTIPLIER], a - call Multiply - -; Determine BallFactor2. -; Poké Ball: BallFactor2 = 255 -; Great Ball: BallFactor2 = 200 -; Ultra/Safari Ball: BallFactor2 = 150 - ld a, [wcf91] - ld b, 255 - cp POKE_BALL - jr z, .skip4 - ld b, 200 - cp GREAT_BALL - jr z, .skip4 - ld b, 150 - cp ULTRA_BALL - jr z, .skip4 - -.skip4 -; Let Y = (CatchRate * 100) / BallFactor2. Calculate Y. - ld a, b - ld [H_DIVISOR], a - ld b, 4 - call Divide - -; If Y > 255, there are 3 shakes. -; Note that this shouldn't be possible. -; The maximum value of Y is (255 * 100) / 150 = 170. - ld a, [H_QUOTIENT + 2] - and a - ld b, $63 ; 3 shakes - jr nz, .setAnimData - -; Calculate X * Y. - ld a, [wPokeBallCaptureCalcTemp] - ld [H_MULTIPLIER], a - call Multiply - -; Calculate (X * Y) / 255. - ld a, 255 - ld [H_DIVISOR], a - ld b, 4 - call Divide - -; Determine Status2. -; no status ailment: Status2 = 0 -; Burn/Paralysis/Poison: Status2 = 5 -; Freeze/Sleep: Status2 = 10 - ld a, [wEnemyMonStatus] - and a - jr z, .skip5 - and 1 << FRZ | SLP - ld b, 5 - jr z, .addAilmentValue - ld b, 10 - -.addAilmentValue -; If the Pokémon has a status ailment, add Status2. - ld a, [H_QUOTIENT + 3] - add b - ld [H_QUOTIENT + 3], a - -.skip5 -; Finally determine the number of shakes. -; Let Z = ((X * Y) / 255) + Status2 = [H_QUOTIENT + 3]. -; The number of shakes depend on the range Z is in. -; 0 ≤ Z < 10: 0 shakes (the ball misses) -; 10 ≤ Z < 30: 1 shake -; 30 ≤ Z < 70: 2 shakes -; 70 ≤ Z: 3 shakes - ld a, [H_QUOTIENT + 3] - cp 10 - ld b, $20 - jr c, .setAnimData - cp 30 - ld b, $61 - jr c, .setAnimData - cp 70 - ld b, $62 - jr c, .setAnimData - ld b, $63 - -.setAnimData - ld a, b - ld [wPokeBallAnimData], a - -.skipShakeCalculations - ld c, 20 - call DelayFrames - -; Do the animation. - ld a, TOSS_ANIM - ld [wAnimationID], a - xor a - ld [H_WHOSETURN], a - ld [wAnimationType], a - ld [wDamageMultipliers], a - ld a, [wWhichPokemon] - push af - ld a, [wcf91] - push af - predef MoveAnimation - pop af - ld [wcf91], a - pop af - ld [wWhichPokemon], a - -; Determine the message to display from the animation. - ld a, [wPokeBallAnimData] - cp $10 - ld hl, ItemUseBallText00 - jp z, .printMessage - cp $20 - ld hl, ItemUseBallText01 - jp z, .printMessage - cp $61 - ld hl, ItemUseBallText02 - jp z, .printMessage - cp $62 - ld hl, ItemUseBallText03 - jp z, .printMessage - cp $63 - ld hl, ItemUseBallText04 - jp z, .printMessage - -; Save current HP. - ld hl, wEnemyMonHP - ld a, [hli] - push af - ld a, [hli] - push af - -; Save status ailment. - inc hl - ld a, [hl] - push af - - push hl - -; If the Pokémon is transformed, the Pokémon is assumed to be a Ditto. -; This is a bug because a wild Pokémon could have used Transform via -; Mirror Move even though the only wild Pokémon that knows Transform is Ditto. - ld hl, wEnemyBattleStatus3 - bit TRANSFORMED, [hl] - jr z, .notTransformed - ld a, DITTO - ld [wEnemyMonSpecies2], a - jr .skip6 - -.notTransformed -; If the Pokémon is not transformed, set the transformed bit and copy the -; DVs to wTransformedEnemyMonOriginalDVs so that LoadEnemyMonData won't generate -; new DVs. - set TRANSFORMED, [hl] - ld hl, wTransformedEnemyMonOriginalDVs - ld a, [wEnemyMonDVs] - ld [hli], a - ld a, [wEnemyMonDVs + 1] - ld [hl], a - -.skip6 - ld a, [wcf91] - push af - ld a, [wEnemyMonSpecies2] - ld [wcf91], a - ld a, [wEnemyMonLevel] - ld [wCurEnemyLVL], a - callab LoadEnemyMonData - pop af - ld [wcf91], a - pop hl - pop af - ld [hld], a - dec hl - pop af - ld [hld], a - pop af - ld [hl], a - ld a, [wEnemyMonSpecies] - ld [wCapturedMonSpecies], a - ld [wcf91], a - ld [wd11e], a - ld a, [wBattleType] - dec a ; is this the old man battle? - jr z, .oldManCaughtMon ; if so, don't give the player the caught Pokémon - - ld hl, ItemUseBallText05 - call PrintText - -; Add the caught Pokémon to the Pokédex. - predef IndexToPokedex - ld a, [wd11e] - dec a - ld c, a - ld b, FLAG_TEST - ld hl, wPokedexOwned - predef FlagActionPredef - ld a, c - push af - ld a, [wd11e] - dec a - ld c, a - ld b, FLAG_SET - predef FlagActionPredef - pop af - - and a ; was the Pokémon already in the Pokédex? - jr nz, .skipShowingPokedexData ; if so, don't show the Pokédex data - - ld hl, ItemUseBallText06 - call PrintText - call ClearSprites - ld a, [wEnemyMonSpecies] - ld [wd11e], a - predef ShowPokedexData - -.skipShowingPokedexData - ld a, [wPartyCount] - cp PARTY_LENGTH ; is party full? - jr z, .sendToBox - xor a ; PLAYER_PARTY_DATA - ld [wMonDataLocation], a - call ClearSprites - call AddPartyMon - jr .done - -.sendToBox - call ClearSprites - call SendNewMonToBox - ld hl, ItemUseBallText07 - CheckEvent EVENT_MET_BILL - jr nz, .printTransferredToPCText - ld hl, ItemUseBallText08 -.printTransferredToPCText - call PrintText - jr .done - -.oldManCaughtMon - ld hl, ItemUseBallText05 - -.printMessage - call PrintText - call ClearSprites - -.done - ld a, [wBattleType] - and a ; is this the old man battle? - ret nz ; if so, don't remove a ball from the bag - -; Remove a ball from the bag. - ld hl, wNumBagItems - inc a - ld [wItemQuantity], a - jp RemoveItemFromInventory - -ItemUseBallText00: -;"It dodged the thrown ball!" -;"This pokemon can't be caught" - TX_FAR _ItemUseBallText00 - db "@" -ItemUseBallText01: -;"You missed the pokemon!" - TX_FAR _ItemUseBallText01 - db "@" -ItemUseBallText02: -;"Darn! The pokemon broke free!" - TX_FAR _ItemUseBallText02 - db "@" -ItemUseBallText03: -;"Aww! It appeared to be caught!" - TX_FAR _ItemUseBallText03 - db "@" -ItemUseBallText04: -;"Shoot! It was so close too!" - TX_FAR _ItemUseBallText04 - db "@" -ItemUseBallText05: -;"All right! {MonName} was caught!" -;play sound - TX_FAR _ItemUseBallText05 - TX_SFX_CAUGHT_MON - TX_BLINK - db "@" -ItemUseBallText07: -;"X was transferred to Bill's PC" - TX_FAR _ItemUseBallText07 - db "@" -ItemUseBallText08: -;"X was transferred to someone's PC" - TX_FAR _ItemUseBallText08 - db "@" - -ItemUseBallText06: -;"New DEX data will be added..." -;play sound - TX_FAR _ItemUseBallText06 - TX_SFX_DEX_PAGE_ADDED - TX_BLINK - db "@" - -ItemUseTownMap: - ld a, [wIsInBattle] - and a - jp nz, ItemUseNotTime - jpba DisplayTownMap - -ItemUseBicycle: - ld a, [wIsInBattle] - and a - jp nz, ItemUseNotTime - ld a, [wWalkBikeSurfState] - ld [wWalkBikeSurfStateCopy], a - cp 2 ; is the player surfing? - jp z, ItemUseNotTime - dec a ; is player already bicycling? - jr nz, .tryToGetOnBike -.getOffBike - call ItemUseReloadOverworldData - xor a - ld [wWalkBikeSurfState], a ; change player state to walking - call PlayDefaultMusic ; play walking music - ld hl, GotOffBicycleText - jr .printText -.tryToGetOnBike - call IsBikeRidingAllowed - jp nc, NoCyclingAllowedHere - call ItemUseReloadOverworldData - xor a ; no keys pressed - ld [hJoyHeld], a ; current joypad state - inc a - ld [wWalkBikeSurfState], a ; change player state to bicycling - ld hl, GotOnBicycleText - call PlayDefaultMusic ; play bike riding music -.printText - jp PrintText - -; used for Surf out-of-battle effect -ItemUseSurfboard: - ld a, [wWalkBikeSurfState] - ld [wWalkBikeSurfStateCopy], a - cp 2 ; is the player already surfing? - jr z, .tryToStopSurfing -.tryToSurf - call IsNextTileShoreOrWater - jp c, SurfingAttemptFailed - ld hl, TilePairCollisionsWater - call CheckForTilePairCollisions - jp c, SurfingAttemptFailed -.surf - call .makePlayerMoveForward - ld hl, wd730 - set 7, [hl] - ld a, 2 - ld [wWalkBikeSurfState], a ; change player state to surfing - call PlayDefaultMusic ; play surfing music - ld hl, SurfingGotOnText - jp PrintText -.tryToStopSurfing - xor a - ld [hSpriteIndexOrTextID], a - ld d, 16 ; talking range in pixels (normal range) - call IsSpriteInFrontOfPlayer2 - res 7, [hl] - ld a, [hSpriteIndexOrTextID] - and a ; is there a sprite in the way? - jr nz, .cannotStopSurfing - ld hl, TilePairCollisionsWater - call CheckForTilePairCollisions - jr c, .cannotStopSurfing - ld hl, wTilesetCollisionPtr ; pointer to list of passable tiles - ld a, [hli] - ld h, [hl] - ld l, a ; hl now points to passable tiles - ld a, [wTileInFrontOfPlayer] ; tile in front of the player - ld b, a -.passableTileLoop - ld a, [hli] - cp b - jr z, .stopSurfing - cp $ff - jr nz, .passableTileLoop -.cannotStopSurfing - ld hl, SurfingNoPlaceToGetOffText - jp PrintText -.stopSurfing - call .makePlayerMoveForward - ld hl, wd730 - set 7, [hl] - xor a - ld [wWalkBikeSurfState], a ; change player state to walking - dec a - ld [wJoyIgnore], a - call PlayDefaultMusic ; play walking music - jp LoadWalkingPlayerSpriteGraphics -; uses a simulated button press to make the player move forward -.makePlayerMoveForward - ld a, [wPlayerDirection] ; direction the player is going - bit PLAYER_DIR_BIT_UP, a - ld b, D_UP - jr nz, .storeSimulatedButtonPress - bit PLAYER_DIR_BIT_DOWN, a - ld b, D_DOWN - jr nz, .storeSimulatedButtonPress - bit PLAYER_DIR_BIT_LEFT, a - ld b, D_LEFT - jr nz, .storeSimulatedButtonPress - ld b, D_RIGHT -.storeSimulatedButtonPress - ld a, b - ld [wSimulatedJoypadStatesEnd], a - xor a - ld [wWastedByteCD39], a - inc a - ld [wSimulatedJoypadStatesIndex], a - ret - -SurfingGotOnText: - TX_FAR _SurfingGotOnText - db "@" - -SurfingNoPlaceToGetOffText: - TX_FAR _SurfingNoPlaceToGetOffText - db "@" - -ItemUsePokedex: - predef_jump ShowPokedexMenu - -ItemUseEvoStone: - ld a, [wIsInBattle] - and a - jp nz, ItemUseNotTime - ld a, [wWhichPokemon] - push af - ld a, [wcf91] - ld [wEvoStoneItemID], a - push af - ld a, EVO_STONE_PARTY_MENU - ld [wPartyMenuTypeOrMessageID], a - ld a, $ff - ld [wUpdateSpritesEnabled], a - call DisplayPartyMenu - pop bc - jr c, .canceledItemUse - ld a, b - ld [wcf91], a - ld a, $01 - ld [wForceEvolution], a - ld a, SFX_HEAL_AILMENT - call PlaySoundWaitForCurrent - call WaitForSoundToFinish - callab TryEvolvingMon ; try to evolve pokemon - ld a, [wEvolutionOccurred] - and a - jr z, .noEffect - pop af - ld [wWhichPokemon], a - ld hl, wNumBagItems - ld a, 1 ; remove 1 stone - ld [wItemQuantity], a - jp RemoveItemFromInventory -.noEffect - call ItemUseNoEffect -.canceledItemUse - xor a - ld [wActionResultOrTookBattleTurn], a ; item not used - pop af - ret - -ItemUseVitamin: - ld a, [wIsInBattle] - and a - jp nz, ItemUseNotTime - -ItemUseMedicine: - ld a, [wPartyCount] - and a - jp z, .emptyParty - ld a, [wWhichPokemon] - push af - ld a, [wcf91] - push af - ld a, USE_ITEM_PARTY_MENU - ld [wPartyMenuTypeOrMessageID], a - ld a, $ff - ld [wUpdateSpritesEnabled], a - ld a, [wPseudoItemID] - and a ; using Softboiled? - jr z, .notUsingSoftboiled -; if using softboiled - call GoBackToPartyMenu - jr .getPartyMonDataAddress -.emptyParty - ld hl, .emptyPartyText - xor a - ld [wActionResultOrTookBattleTurn], a ; item use failed - jp PrintText -.emptyPartyText - text "You don't have" - line "any #MON!" - prompt -.notUsingSoftboiled - call DisplayPartyMenu -.getPartyMonDataAddress - jp c, .canceledItemUse - ld hl, wPartyMons - ld bc, wPartyMon2 - wPartyMon1 - ld a, [wWhichPokemon] - call AddNTimes - ld a, [wWhichPokemon] - ld [wUsedItemOnWhichPokemon], a - ld d, a - ld a, [wcf91] - ld e, a - ld [wd0b5], a - pop af - ld [wcf91], a - pop af - ld [wWhichPokemon], a - ld a, [wPseudoItemID] - and a ; using Softboiled? - jr z, .checkItemType -; if using softboiled - ld a, [wWhichPokemon] - cp d ; is the pokemon trying to use softboiled on itself? - jr z, ItemUseMedicine ; if so, force another choice -.checkItemType - ld a, [wcf91] - cp REVIVE - jr nc, .healHP ; if it's a Revive or Max Revive - cp FULL_HEAL - jr z, .cureStatusAilment ; if it's a Full Heal - cp HP_UP - jp nc, .useVitamin ; if it's a vitamin or Rare Candy - cp FULL_RESTORE - jr nc, .healHP ; if it's a Full Restore or one of the potions -; fall through if it's one of the status-specific healing items -.cureStatusAilment - ld bc, wPartyMon1Status - wPartyMon1 - add hl, bc ; hl now points to status - ld a, [wcf91] - lb bc, ANTIDOTE_MSG, 1 << PSN - cp ANTIDOTE - jr z, .checkMonStatus - lb bc, BURN_HEAL_MSG, 1 << BRN - cp BURN_HEAL - jr z, .checkMonStatus - lb bc, ICE_HEAL_MSG, 1 << FRZ - cp ICE_HEAL - jr z, .checkMonStatus - lb bc, AWAKENING_MSG, SLP - cp AWAKENING - jr z, .checkMonStatus - lb bc, PARALYZ_HEAL_MSG, 1 << PAR - cp PARLYZ_HEAL - jr z, .checkMonStatus - lb bc, FULL_HEAL_MSG, $ff ; Full Heal -.checkMonStatus - ld a, [hl] ; pokemon's status - and c ; does the pokemon have a status ailment the item can cure? - jp z, .healingItemNoEffect -; if the pokemon has a status the item can heal - xor a - ld [hl], a ; remove the status ailment in the party data - ld a, b - ld [wPartyMenuTypeOrMessageID], a ; the message to display for the item used - ld a, [wPlayerMonNumber] - cp d ; is pokemon the item was used on active in battle? - jp nz, .doneHealing -; if it is active in battle - xor a - ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data - push hl - ld hl, wPlayerBattleStatus3 - res BADLY_POISONED, [hl] ; heal Toxic status - pop hl - ld bc, wPartyMon1Stats - wPartyMon1Status - add hl, bc ; hl now points to party stats - ld de, wBattleMonStats - ld bc, NUM_STATS * 2 - call CopyData ; copy party stats to in-battle stat data - predef DoubleOrHalveSelectedStats - jp .doneHealing -.healHP - inc hl ; hl = address of current HP - ld a, [hli] - ld b, a - ld [wHPBarOldHP+1], a - ld a, [hl] - ld c, a - ld [wHPBarOldHP], a ; current HP stored at wHPBarOldHP (2 bytes, big-endian) - or b - jr nz, .notFainted -.fainted - ld a, [wcf91] - cp REVIVE - jr z, .updateInBattleFaintedData - cp MAX_REVIVE - jr z, .updateInBattleFaintedData - jp .healingItemNoEffect -.updateInBattleFaintedData - ld a, [wIsInBattle] - and a - jr z, .compareCurrentHPToMaxHP - push hl - push de - push bc - ld a, [wUsedItemOnWhichPokemon] - ld c, a - ld hl, wPartyFoughtCurrentEnemyFlags - ld b, FLAG_TEST - predef FlagActionPredef - ld a, c - and a - jr z, .next - ld a, [wUsedItemOnWhichPokemon] - ld c, a - ld hl, wPartyGainExpFlags - ld b, FLAG_SET - predef FlagActionPredef -.next - pop bc - pop de - pop hl - jr .compareCurrentHPToMaxHP -.notFainted - ld a, [wcf91] - cp REVIVE - jp z, .healingItemNoEffect - cp MAX_REVIVE - jp z, .healingItemNoEffect -.compareCurrentHPToMaxHP - push hl - push bc - ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1) - add hl, bc ; hl now points to max HP - pop bc - ld a, [hli] - cp b - jr nz, .skipComparingLSB ; no need to compare the LSB's if the MSB's don't match - ld a, [hl] - cp c -.skipComparingLSB - pop hl - jr nz, .notFullHP -.fullHP ; if the pokemon's current HP equals its max HP - ld a, [wcf91] - cp FULL_RESTORE - jp nz, .healingItemNoEffect - inc hl - inc hl - ld a, [hld] ; status ailment - and a ; does the pokemon have a status ailment? - jp z, .healingItemNoEffect - ld a, FULL_HEAL - ld [wcf91], a - dec hl - dec hl - dec hl - jp .cureStatusAilment -.notFullHP ; if the pokemon's current HP doesn't equal its max HP - xor a - ld [wLowHealthAlarm], a ;disable low health alarm - ld [wChannelSoundIDs + Ch5], a - push hl - push de - ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1) - add hl, bc ; hl now points to max HP - ld a, [hli] - ld [wHPBarMaxHP+1], a - ld a, [hl] - ld [wHPBarMaxHP], a ; max HP stored at wHPBarMaxHP (2 bytes, big-endian) - ld a, [wPseudoItemID] - and a ; using Softboiled? - jp z, .notUsingSoftboiled2 -; if using softboiled - ld hl, wHPBarMaxHP - ld a, [hli] - push af - ld a, [hli] - push af - ld a, [hli] - push af - ld a, [hl] - push af - ld hl, wPartyMon1MaxHP - ld a, [wWhichPokemon] - ld bc, wPartyMon2 - wPartyMon1 - call AddNTimes - ld a, [hli] - ld [wHPBarMaxHP + 1], a - ld [H_DIVIDEND], a - ld a, [hl] - ld [wHPBarMaxHP], a - ld [H_DIVIDEND + 1], a - ld a, 5 - ld [H_DIVISOR], a - ld b, 2 ; number of bytes - call Divide ; get 1/5 of max HP of pokemon that used Softboiled - ld bc, (wPartyMon1HP + 1) - (wPartyMon1MaxHP + 1) - add hl, bc ; hl now points to LSB of current HP of pokemon that used Softboiled -; subtract 1/5 of max HP from current HP of pokemon that used Softboiled - ld a, [H_QUOTIENT + 3] - push af - ld b, a - ld a, [hl] - ld [wHPBarOldHP], a - sub b - ld [hld], a - ld [wHPBarNewHP], a - ld a, [H_QUOTIENT + 2] - ld b, a - ld a, [hl] - ld [wHPBarOldHP+1], a - sbc b - ld [hl], a - ld [wHPBarNewHP+1], a - coord hl, 4, 1 - ld a, [wWhichPokemon] - ld bc, 2 * SCREEN_WIDTH - call AddNTimes ; calculate coordinates of HP bar of pokemon that used Softboiled - ld a, SFX_HEAL_HP - call PlaySoundWaitForCurrent - ld a, [hFlags_0xFFF6] - set 0, a - ld [hFlags_0xFFF6], a - ld a, $02 - ld [wHPBarType], a - predef UpdateHPBar2 ; animate HP bar decrease of pokemon that used Softboiled - ld a, [hFlags_0xFFF6] - res 0, a - ld [hFlags_0xFFF6], a - pop af - ld b, a ; store heal amount (1/5 of max HP) - ld hl, wHPBarOldHP + 1 - pop af - ld [hld], a - pop af - ld [hld], a - pop af - ld [hld], a - pop af - ld [hl], a - jr .addHealAmount -.notUsingSoftboiled2 - ld a, [wcf91] - cp SODA_POP - ld b, 60 ; Soda Pop heal amount - jr z, .addHealAmount - ld b, 80 ; Lemonade heal amount - jr nc, .addHealAmount - cp FRESH_WATER - ld b, 50 ; Fresh Water heal amount - jr z, .addHealAmount - cp SUPER_POTION - ld b, 200 ; Hyper Potion heal amount - jr c, .addHealAmount - ld b, 50 ; Super Potion heal amount - jr z, .addHealAmount - ld b, 20 ; Potion heal amount -.addHealAmount - pop de - pop hl - ld a, [hl] - add b - ld [hld], a - ld [wHPBarNewHP], a - ld a, [hl] - ld [wHPBarNewHP+1], a - jr nc, .noCarry - inc [hl] - ld a, [hl] - ld [wHPBarNewHP + 1], a -.noCarry - push de - inc hl - ld d, h - ld e, l ; de now points to current HP - ld hl, (wPartyMon1MaxHP + 1) - (wPartyMon1HP + 1) - add hl, de ; hl now points to max HP - ld a, [wcf91] - cp REVIVE - jr z, .setCurrentHPToHalfMaxHP - ld a, [hld] - ld b, a - ld a, [de] - sub b - dec de - ld b, [hl] - ld a, [de] - sbc b - jr nc, .setCurrentHPToMaxHp ; if current HP exceeds max HP after healing - ld a, [wcf91] - cp HYPER_POTION - jr c, .setCurrentHPToMaxHp ; if using a Full Restore or Max Potion - cp MAX_REVIVE - jr z, .setCurrentHPToMaxHp ; if using a Max Revive - jr .updateInBattleData -.setCurrentHPToHalfMaxHP - dec hl - dec de - ld a, [hli] - srl a - ld [de], a - ld [wHPBarNewHP+1], a - ld a, [hl] - rr a - inc de - ld [de], a - ld [wHPBarNewHP], a - dec de - jr .doneHealingPartyHP -.setCurrentHPToMaxHp - ld a, [hli] - ld [de], a - ld [wHPBarNewHP+1], a - inc de - ld a, [hl] - ld [de], a - ld [wHPBarNewHP], a - dec de -.doneHealingPartyHP ; done updating the pokemon's current HP in the party data structure - ld a, [wcf91] - cp FULL_RESTORE - jr nz, .updateInBattleData - ld bc, wPartyMon1Status - (wPartyMon1MaxHP + 1) - add hl, bc - xor a - ld [hl], a ; remove the status ailment in the party data -.updateInBattleData - ld h, d - ld l, e - pop de - ld a, [wPlayerMonNumber] - cp d ; is pokemon the item was used on active in battle? - jr nz, .calculateHPBarCoords -; copy party HP to in-battle HP - ld a, [hli] - ld [wBattleMonHP], a - ld a, [hld] - ld [wBattleMonHP + 1], a - ld a, [wcf91] - cp FULL_RESTORE - jr nz, .calculateHPBarCoords - xor a - ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data -.calculateHPBarCoords - ld hl, wOAMBuffer + $90 - ld bc, 2 * SCREEN_WIDTH - inc d -.calculateHPBarCoordsLoop - add hl, bc - dec d - jr nz, .calculateHPBarCoordsLoop - jr .doneHealing -.healingItemNoEffect - call ItemUseNoEffect - jp .done -.doneHealing - ld a, [wPseudoItemID] - and a ; using Softboiled? - jr nz, .skipRemovingItem ; no item to remove if using Softboiled - push hl - call RemoveUsedItem - pop hl -.skipRemovingItem - ld a, [wcf91] - cp FULL_RESTORE - jr c, .playStatusAilmentCuringSound - cp FULL_HEAL - jr z, .playStatusAilmentCuringSound - ld a, SFX_HEAL_HP - call PlaySoundWaitForCurrent - ld a, [hFlags_0xFFF6] - set 0, a - ld [hFlags_0xFFF6], a - ld a, $02 - ld [wHPBarType], a - predef UpdateHPBar2 ; animate the HP bar lengthening - ld a, [hFlags_0xFFF6] - res 0, a - ld [hFlags_0xFFF6], a - ld a, REVIVE_MSG - ld [wPartyMenuTypeOrMessageID], a - ld a, [wcf91] - cp REVIVE - jr z, .showHealingItemMessage - cp MAX_REVIVE - jr z, .showHealingItemMessage - ld a, POTION_MSG - ld [wPartyMenuTypeOrMessageID], a - jr .showHealingItemMessage -.playStatusAilmentCuringSound - ld a, SFX_HEAL_AILMENT - call PlaySoundWaitForCurrent -.showHealingItemMessage - xor a - ld [H_AUTOBGTRANSFERENABLED], a - call ClearScreen - dec a - ld [wUpdateSpritesEnabled], a - call RedrawPartyMenu ; redraws the party menu and displays the message - ld a, 1 - ld [H_AUTOBGTRANSFERENABLED], a - ld c, 50 - call DelayFrames - call WaitForTextScrollButtonPress - jr .done -.canceledItemUse - xor a - ld [wActionResultOrTookBattleTurn], a ; item use failed - pop af - pop af -.done - ld a, [wPseudoItemID] - and a ; using Softboiled? - ret nz ; if so, return - call GBPalWhiteOut - call z, RunDefaultPaletteCommand - ld a, [wIsInBattle] - and a - ret nz - jp ReloadMapData -.useVitamin - push hl - ld a, [hl] - ld [wd0b5], a - ld [wd11e], a - ld bc, wPartyMon1Level - wPartyMon1 - add hl, bc ; hl now points to level - ld a, [hl] ; a = level - ld [wCurEnemyLVL], a ; store level - call GetMonHeader - push de - ld a, d - ld hl, wPartyMonNicks - call GetPartyMonName - pop de - pop hl - ld a, [wcf91] - cp RARE_CANDY - jp z, .useRareCandy - push hl - sub HP_UP - add a - ld bc, wPartyMon1HPExp - wPartyMon1 - add hl, bc - add l - ld l, a - jr nc, .noCarry2 - inc h -.noCarry2 - ld a, 10 - ld b, a - ld a, [hl] ; a = MSB of stat experience of the appropriate stat - cp 100 ; is there already at least 25600 (256 * 100) stat experience? - jr nc, .vitaminNoEffect ; if so, vitamins can't add any more - add b ; add 2560 (256 * 10) stat experience - jr nc, .noCarry3 ; a carry should be impossible here, so this will always jump - ld a, 255 -.noCarry3 - ld [hl], a - pop hl - call .recalculateStats - ld hl, VitaminText - ld a, [wcf91] - sub HP_UP - 1 - ld c, a -.statNameLoop ; loop to get the address of the name of the stat the vitamin increases - dec c - jr z, .gotStatName -.statNameInnerLoop - ld a, [hli] - ld b, a - ld a, $50 - cp b - jr nz, .statNameInnerLoop - jr .statNameLoop -.gotStatName - ld de, wcf4b - ld bc, 10 - call CopyData ; copy the stat's name to wcf4b - ld a, SFX_HEAL_AILMENT - call PlaySound - ld hl, VitaminStatRoseText - call PrintText - jp RemoveUsedItem -.vitaminNoEffect - pop hl - ld hl, VitaminNoEffectText - call PrintText - jp GBPalWhiteOut -.recalculateStats - ld bc, wPartyMon1Stats - wPartyMon1 - add hl, bc - ld d, h - ld e, l ; de now points to stats - ld bc, (wPartyMon1Exp + 2) - wPartyMon1Stats - add hl, bc ; hl now points to LSB of experience - ld b, 1 - jp CalcStats ; recalculate stats -.useRareCandy - push hl - ld bc, wPartyMon1Level - wPartyMon1 - add hl, bc ; hl now points to level - ld a, [hl] ; a = level - cp MAX_LEVEL - jr z, .vitaminNoEffect ; can't raise level above 100 - inc a - ld [hl], a ; store incremented level - ld [wCurEnemyLVL], a - push hl - push de - ld d, a - callab CalcExperience ; calculate experience for next level and store it at $ff96 - pop de - pop hl - ld bc, wPartyMon1Exp - wPartyMon1Level - add hl, bc ; hl now points to MSB of experience -; update experience to minimum for new level - ld a, [hExperience] - ld [hli], a - ld a, [hExperience + 1] - ld [hli], a - ld a, [hExperience + 2] - ld [hl], a - pop hl - ld a, [wWhichPokemon] - push af - ld a, [wcf91] - push af - push de - push hl - ld bc, wPartyMon1MaxHP - wPartyMon1 - add hl, bc ; hl now points to MSB of max HP - ld a, [hli] - ld b, a - ld c, [hl] - pop hl - push bc - push hl - call .recalculateStats - pop hl - ld bc, (wPartyMon1MaxHP + 1) - wPartyMon1 - add hl, bc ; hl now points to LSB of max HP - pop bc - ld a, [hld] - sub c - ld c, a - ld a, [hl] - sbc b - ld b, a ; bc = the amount of max HP gained from leveling up -; add the amount gained to the current HP - ld de, (wPartyMon1HP + 1) - wPartyMon1MaxHP - add hl, de ; hl now points to LSB of current HP - ld a, [hl] - add c - ld [hld], a - ld a, [hl] - adc b - ld [hl], a - ld a, RARE_CANDY_MSG - ld [wPartyMenuTypeOrMessageID], a - call RedrawPartyMenu - pop de - ld a, d - ld [wWhichPokemon], a - ld a, e - ld [wd11e], a - xor a ; PLAYER_PARTY_DATA - ld [wMonDataLocation], a - call LoadMonData - ld d, $01 - callab PrintStatsBox ; display new stats text box - call WaitForTextScrollButtonPress ; wait for button press - xor a ; PLAYER_PARTY_DATA - ld [wMonDataLocation], a - predef LearnMoveFromLevelUp ; learn level up move, if any - xor a - ld [wForceEvolution], a - callab TryEvolvingMon ; evolve pokemon, if appropriate - ld a, $01 - ld [wUpdateSpritesEnabled], a - pop af - ld [wcf91], a - pop af - ld [wWhichPokemon], a - jp RemoveUsedItem - -VitaminStatRoseText: - TX_FAR _VitaminStatRoseText - db "@" - -VitaminNoEffectText: - TX_FAR _VitaminNoEffectText - db "@" - -VitaminText: - db "HEALTH@" - db "ATTACK@" - db "DEFENSE@" - db "SPEED@" - db "SPECIAL@" - -ItemUseBait: - ld hl, ThrewBaitText - call PrintText - ld hl, wEnemyMonActualCatchRate ; catch rate - srl [hl] ; halve catch rate - ld a, BAIT_ANIM - ld hl, wSafariBaitFactor ; bait factor - ld de, wSafariEscapeFactor ; escape factor - jr BaitRockCommon - -ItemUseRock: - ld hl, ThrewRockText - call PrintText - ld hl, wEnemyMonActualCatchRate ; catch rate - ld a, [hl] - add a ; double catch rate - jr nc, .noCarry - ld a, $ff -.noCarry - ld [hl], a - ld a, ROCK_ANIM - ld hl, wSafariEscapeFactor ; escape factor - ld de, wSafariBaitFactor ; bait factor - -BaitRockCommon: - ld [wAnimationID], a - xor a - ld [wAnimationType], a - ld [H_WHOSETURN], a - ld [de], a ; zero escape factor (for bait), zero bait factor (for rock) -.randomLoop ; loop until a random number less than 5 is generated - call Random - and 7 - cp 5 - jr nc, .randomLoop - inc a ; increment the random number, giving a range from 1 to 5 inclusive - ld b, a - ld a, [hl] - add b ; increase bait factor (for bait), increase escape factor (for rock) - jr nc, .noCarry - ld a, $ff -.noCarry - ld [hl], a - predef MoveAnimation ; do animation - ld c, 70 - jp DelayFrames - -ThrewBaitText: - TX_FAR _ThrewBaitText - db "@" - -ThrewRockText: - TX_FAR _ThrewRockText - db "@" - -; also used for Dig out-of-battle effect -ItemUseEscapeRope: - ld a, [wIsInBattle] - and a - jr nz, .notUsable - ld a, [wCurMap] - cp AGATHAS_ROOM - jr z, .notUsable - ld a, [wCurMapTileset] - ld b, a - ld hl, EscapeRopeTilesets -.loop - ld a, [hli] - cp $ff - jr z, .notUsable - cp b - jr nz, .loop - ld hl, wd732 - set 3, [hl] - set 6, [hl] - ld hl, wd72e - res 4, [hl] - ResetEvent EVENT_IN_SAFARI_ZONE - xor a - ld [wNumSafariBalls], a - ld [wSafariZoneGateCurScript], a - inc a - ld [wEscapedFromBattle], a - ld [wActionResultOrTookBattleTurn], a ; item used - ld a, [wPseudoItemID] - and a ; using Dig? - ret nz ; if so, return - call ItemUseReloadOverworldData - ld c, 30 - call DelayFrames - jp RemoveUsedItem -.notUsable - jp ItemUseNotTime - -EscapeRopeTilesets: - db FOREST, CEMETERY, CAVERN, FACILITY, INTERIOR - db $ff ; terminator - -ItemUseRepel: - ld b, 100 - -ItemUseRepelCommon: - ld a, [wIsInBattle] - and a - jp nz, ItemUseNotTime - ld a, b - ld [wRepelRemainingSteps], a - jp PrintItemUseTextAndRemoveItem - -; handles X Accuracy item -ItemUseXAccuracy: - ld a, [wIsInBattle] - and a - jp z, ItemUseNotTime - ld hl, wPlayerBattleStatus2 - set USING_X_ACCURACY, [hl] ; X Accuracy bit - jp PrintItemUseTextAndRemoveItem - -; This function is bugged and never works. It always jumps to ItemUseNotTime. -; The Card Key is handled in a different way. -ItemUseCardKey: - xor a - ld [wUnusedD71F], a - call GetTileAndCoordsInFrontOfPlayer - ld a, [GetTileAndCoordsInFrontOfPlayer] - cp $18 - jr nz, .next0 - ld hl, CardKeyTable1 - jr .next1 -.next0 - cp $24 - jr nz, .next2 - ld hl, CardKeyTable2 - jr .next1 -.next2 - cp $5e - jp nz, ItemUseNotTime - ld hl, CardKeyTable3 -.next1 - ld a, [wCurMap] - ld b, a -.loop - ld a, [hli] - cp $ff - jp z, ItemUseNotTime - cp b - jr nz, .nextEntry1 - ld a, [hli] - cp d - jr nz, .nextEntry2 - ld a, [hli] - cp e - jr nz, .nextEntry3 - ld a, [hl] - ld [wUnusedD71F], a - jr .done -.nextEntry1 - inc hl -.nextEntry2 - inc hl -.nextEntry3 - inc hl - jr .loop -.done - ld hl, ItemUseText00 - call PrintText - ld hl, wd728 - set 7, [hl] - ret - -; These tables are probably supposed to be door locations in Silph Co., -; but they are unused. -; The reason there are 3 tables is unknown. - -; Format: -; 00: Map ID -; 01: Y -; 02: X -; 03: ID? - -CardKeyTable1: - db SILPH_CO_2F,$04,$04,$00 - db SILPH_CO_2F,$04,$05,$01 - db SILPH_CO_4F,$0C,$04,$02 - db SILPH_CO_4F,$0C,$05,$03 - db SILPH_CO_7F,$06,$0A,$04 - db SILPH_CO_7F,$06,$0B,$05 - db SILPH_CO_9F,$04,$12,$06 - db SILPH_CO_9F,$04,$13,$07 - db SILPH_CO_10F,$08,$0A,$08 - db SILPH_CO_10F,$08,$0B,$09 - db $ff - -CardKeyTable2: - db SILPH_CO_3F,$08,$09,$0A - db SILPH_CO_3F,$09,$09,$0B - db SILPH_CO_5F,$04,$07,$0C - db SILPH_CO_5F,$05,$07,$0D - db SILPH_CO_6F,$0C,$05,$0E - db SILPH_CO_6F,$0D,$05,$0F - db SILPH_CO_8F,$08,$07,$10 - db SILPH_CO_8F,$09,$07,$11 - db SILPH_CO_9F,$08,$03,$12 - db SILPH_CO_9F,$09,$03,$13 - db $ff - -CardKeyTable3: - db SILPH_CO_11F,$08,$09,$14 - db SILPH_CO_11F,$09,$09,$15 - db $ff - -ItemUsePokedoll: - ld a, [wIsInBattle] - dec a - jp nz, ItemUseNotTime - ld a, $01 - ld [wEscapedFromBattle], a - jp PrintItemUseTextAndRemoveItem - -ItemUseGuardSpec: - ld a, [wIsInBattle] - and a - jp z, ItemUseNotTime - ld hl, wPlayerBattleStatus2 - set PROTECTED_BY_MIST, [hl] ; Mist bit - jp PrintItemUseTextAndRemoveItem - -ItemUseSuperRepel: - ld b, 200 - jp ItemUseRepelCommon - -ItemUseMaxRepel: - ld b, 250 - jp ItemUseRepelCommon - -ItemUseDireHit: - ld a, [wIsInBattle] - and a - jp z, ItemUseNotTime - ld hl, wPlayerBattleStatus2 - set GETTING_PUMPED, [hl] ; Focus Energy bit - jp PrintItemUseTextAndRemoveItem - -ItemUseXStat: - ld a, [wIsInBattle] - and a - jr nz, .inBattle - call ItemUseNotTime - ld a, 2 - ld [wActionResultOrTookBattleTurn], a ; item not used - ret -.inBattle - ld hl, wPlayerMoveNum - ld a, [hli] - push af ; save [wPlayerMoveNum] - ld a, [hl] - push af ; save [wPlayerMoveEffect] - push hl - ld a, [wcf91] - sub X_ATTACK - ATTACK_UP1_EFFECT - ld [hl], a ; store player move effect - call PrintItemUseTextAndRemoveItem - ld a, XSTATITEM_ANIM ; X stat item animation ID - ld [wPlayerMoveNum], a - call LoadScreenTilesFromBuffer1 ; restore saved screen - call Delay3 - xor a - ld [H_WHOSETURN], a ; set turn to player's turn - callba StatModifierUpEffect ; do stat increase move - pop hl - pop af - ld [hld], a ; restore [wPlayerMoveEffect] - pop af - ld [hl], a ; restore [wPlayerMoveNum] - ret - -ItemUsePokeflute: - ld a, [wIsInBattle] - and a - jr nz, .inBattle -; if not in battle - call ItemUseReloadOverworldData - ld a, [wCurMap] - cp ROUTE_12 - jr nz, .notRoute12 - CheckEvent EVENT_BEAT_ROUTE12_SNORLAX - jr nz, .noSnorlaxToWakeUp -; if the player hasn't beaten Route 12 Snorlax - ld hl, Route12SnorlaxFluteCoords - call ArePlayerCoordsInArray - jr nc, .noSnorlaxToWakeUp - ld hl, PlayedFluteHadEffectText - call PrintText - SetEvent EVENT_FIGHT_ROUTE12_SNORLAX - ret -.notRoute12 - cp ROUTE_16 - jr nz, .noSnorlaxToWakeUp - CheckEvent EVENT_BEAT_ROUTE16_SNORLAX - jr nz, .noSnorlaxToWakeUp -; if the player hasn't beaten Route 16 Snorlax - ld hl, Route16SnorlaxFluteCoords - call ArePlayerCoordsInArray - jr nc, .noSnorlaxToWakeUp - ld hl, PlayedFluteHadEffectText - call PrintText - SetEvent EVENT_FIGHT_ROUTE16_SNORLAX - ret -.noSnorlaxToWakeUp - ld hl, PlayedFluteNoEffectText - jp PrintText -.inBattle - xor a - ld [wWereAnyMonsAsleep], a - ld b, ~SLP & $ff - ld hl, wPartyMon1Status - call WakeUpEntireParty - ld a, [wIsInBattle] - dec a ; is it a trainer battle? - jr z, .skipWakingUpEnemyParty -; if it's a trainer battle - ld hl, wEnemyMon1Status - call WakeUpEntireParty -.skipWakingUpEnemyParty - ld hl, wBattleMonStatus - ld a, [hl] - and b ; remove Sleep status - ld [hl], a - ld hl, wEnemyMonStatus - ld a, [hl] - and b ; remove Sleep status - ld [hl], a - call LoadScreenTilesFromBuffer2 ; restore saved screen - ld a, [wWereAnyMonsAsleep] - and a ; were any pokemon asleep before playing the flute? - ld hl, PlayedFluteNoEffectText - jp z, PrintText ; if no pokemon were asleep -; if some pokemon were asleep - ld hl, PlayedFluteHadEffectText - call PrintText - ld a, [wLowHealthAlarm] - and $80 - jr nz, .skipMusic - call WaitForSoundToFinish ; wait for sound to end - callba Music_PokeFluteInBattle ; play in-battle pokeflute music -.musicWaitLoop ; wait for music to finish playing - ld a, [wChannelSoundIDs + Ch7] - and a ; music off? - jr nz, .musicWaitLoop -.skipMusic - ld hl, FluteWokeUpText - jp PrintText - -; wakes up all party pokemon -; INPUT: -; hl must point to status of first pokemon in party (player's or enemy's) -; b must equal ~SLP -; [wWereAnyMonsAsleep] should be initialized to 0 -; OUTPUT: -; [wWereAnyMonsAsleep]: set to 1 if any pokemon were asleep -WakeUpEntireParty: - ld de, 44 - ld c, 6 -.loop - ld a, [hl] - push af - and SLP ; is pokemon asleep? - jr z, .notAsleep - ld a, 1 - ld [wWereAnyMonsAsleep], a ; indicate that a pokemon had to be woken up -.notAsleep - pop af - and b ; remove Sleep status - ld [hl], a - add hl, de - dec c - jr nz, .loop - ret - -; Format: -; 00: Y -; 01: X -Route12SnorlaxFluteCoords: - db 62,9 ; one space West of Snorlax - db 61,10 ; one space North of Snorlax - db 63,10 ; one space South of Snorlax - db 62,11 ; one space East of Snorlax - db $ff ; terminator - -; Format: -; 00: Y -; 01: X -Route16SnorlaxFluteCoords: - db 10,27 ; one space East of Snorlax - db 10,25 ; one space West of Snorlax - db $ff ; terminator - -PlayedFluteNoEffectText: - TX_FAR _PlayedFluteNoEffectText - db "@" - -FluteWokeUpText: - TX_FAR _FluteWokeUpText - db "@" - -PlayedFluteHadEffectText: - TX_FAR _PlayedFluteHadEffectText - TX_BLINK - TX_ASM - ld a, [wIsInBattle] - and a - jr nz, .done -; play out-of-battle pokeflute music - ld a, $ff - call PlaySound ; turn off music - ld a, SFX_POKEFLUTE - ld c, BANK(SFX_Pokeflute) - call PlayMusic -.musicWaitLoop ; wait for music to finish playing - ld a, [wChannelSoundIDs + Ch3] - cp SFX_POKEFLUTE - jr z, .musicWaitLoop - call PlayDefaultMusic ; start playing normal music again -.done - jp TextScriptEnd ; end text - -ItemUseCoinCase: - ld a, [wIsInBattle] - and a - jp nz, ItemUseNotTime - ld hl, CoinCaseNumCoinsText - jp PrintText - -CoinCaseNumCoinsText: - TX_FAR _CoinCaseNumCoinsText - db "@" - -ItemUseOldRod: - call FishingInit - jp c, ItemUseNotTime - lb bc, 5, MAGIKARP - ld a, $1 ; set bite - jr RodResponse - -ItemUseGoodRod: - call FishingInit - jp c, ItemUseNotTime -.RandomLoop - call Random - srl a - jr c, .SetBite - and %11 - cp 2 - jr nc, .RandomLoop - ; choose which monster appears - ld hl, GoodRodMons - add a - ld c, a - ld b, 0 - add hl, bc - ld b, [hl] - inc hl - ld c, [hl] - and a -.SetBite - ld a, 0 - rla - xor 1 - jr RodResponse - -INCLUDE "data/good_rod.asm" - -ItemUseSuperRod: - call FishingInit - jp c, ItemUseNotTime - call ReadSuperRodData - ld a, e -RodResponse: - ld [wRodResponse], a - - dec a ; is there a bite? - jr nz, .next - ; if yes, store level and species data - ld a, 1 - ld [wMoveMissed], a - ld a, b ; level - ld [wCurEnemyLVL], a - ld a, c ; species - ld [wCurOpponent], a - -.next - ld hl, wWalkBikeSurfState - ld a, [hl] ; store the value in a - push af - push hl - ld [hl], 0 - callba FishingAnim - pop hl - pop af - ld [hl], a - ret - -; checks if fishing is possible and if so, runs initialization code common to all rods -; unsets carry if fishing is possible, sets carry if not -FishingInit: - ld a, [wIsInBattle] - and a - jr z, .notInBattle - scf ; can't fish during battle - ret -.notInBattle - call IsNextTileShoreOrWater - ret c - ld a, [wWalkBikeSurfState] - cp 2 ; Surfing? - jr z, .surfing - call ItemUseReloadOverworldData - ld hl, ItemUseText00 - call PrintText - ld a, SFX_HEAL_AILMENT - call PlaySound - ld c, 80 - call DelayFrames - and a - ret -.surfing - scf ; can't fish when surfing - ret - -ItemUseOaksParcel: - jp ItemUseNotYoursToUse - -ItemUseItemfinder: - ld a, [wIsInBattle] - and a - jp nz, ItemUseNotTime - call ItemUseReloadOverworldData - callba HiddenItemNear ; check for hidden items - ld hl, ItemfinderFoundNothingText - jr nc, .printText ; if no hidden items - ld c, 4 -.loop - ld a, SFX_HEALING_MACHINE - call PlaySoundWaitForCurrent - ld a, SFX_PURCHASE - call PlaySoundWaitForCurrent - dec c - jr nz, .loop - ld hl, ItemfinderFoundItemText -.printText - jp PrintText - -ItemfinderFoundItemText: - TX_FAR _ItemfinderFoundItemText - db "@" - -ItemfinderFoundNothingText: - TX_FAR _ItemfinderFoundNothingText - db "@" - -ItemUsePPUp: - ld a, [wIsInBattle] - and a - jp nz, ItemUseNotTime - -ItemUsePPRestore: - ld a, [wWhichPokemon] - push af - ld a, [wcf91] - ld [wPPRestoreItem], a -.chooseMon - xor a - ld [wUpdateSpritesEnabled], a - ld a, USE_ITEM_PARTY_MENU - ld [wPartyMenuTypeOrMessageID], a - call DisplayPartyMenu - jr nc, .chooseMove - jp .itemNotUsed -.chooseMove - ld a, [wPPRestoreItem] - cp ELIXER - jp nc, .useElixir ; if Elixir or Max Elixir - ld a, $02 - ld [wMoveMenuType], a - ld hl, RaisePPWhichTechniqueText - ld a, [wPPRestoreItem] - cp ETHER ; is it a PP Up? - jr c, .printWhichTechniqueMessage ; if so, print the raise PP message - ld hl, RestorePPWhichTechniqueText ; otherwise, print the restore PP message -.printWhichTechniqueMessage - call PrintText - xor a - ld [wPlayerMoveListIndex], a - callab MoveSelectionMenu ; move selection menu - ld a, 0 - ld [wPlayerMoveListIndex], a - jr nz, .chooseMon - ld hl, wPartyMon1Moves - ld bc, wPartyMon2 - wPartyMon1 - call GetSelectedMoveOffset - push hl - ld a, [hl] - ld [wd11e], a - call GetMoveName - call CopyStringToCF4B ; copy name to wcf4b - pop hl - ld a, [wPPRestoreItem] - cp ETHER - jr nc, .useEther ; if Ether or Max Ether -.usePPUp - ld bc, wPartyMon1PP - wPartyMon1Moves - add hl, bc - ld a, [hl] ; move PP - cp 3 << 6 ; have 3 PP Ups already been used? - jr c, .PPNotMaxedOut - ld hl, PPMaxedOutText - call PrintText - jr .chooseMove -.PPNotMaxedOut - ld a, [hl] - add 1 << 6 ; increase PP Up count by 1 - ld [hl], a - ld a, 1 ; 1 PP Up used - ld [wd11e], a - call RestoreBonusPP ; add the bonus PP to current PP - ld hl, PPIncreasedText - call PrintText -.done - pop af - ld [wWhichPokemon], a - call GBPalWhiteOut - call RunDefaultPaletteCommand - jp RemoveUsedItem -.afterRestoringPP ; after using a (Max) Ether/Elixir - ld a, [wWhichPokemon] - ld b, a - ld a, [wPlayerMonNumber] - cp b ; is the pokemon whose PP was restored active in battle? - jr nz, .skipUpdatingInBattleData - ld hl, wPartyMon1PP - ld bc, wPartyMon2 - wPartyMon1 - call AddNTimes - ld de, wBattleMonPP - ld bc, 4 - call CopyData ; copy party data to in-battle data -.skipUpdatingInBattleData - ld a, SFX_HEAL_AILMENT - call PlaySound - ld hl, PPRestoredText - call PrintText - jr .done -.useEther - call .restorePP - jr nz, .afterRestoringPP - jp .noEffect -; unsets zero flag if PP was restored, sets zero flag if not -; however, this is bugged for Max Ethers and Max Elixirs (see below) -.restorePP - xor a ; PLAYER_PARTY_DATA - ld [wMonDataLocation], a - call GetMaxPP - ld hl, wPartyMon1Moves - ld bc, wPartyMon2 - wPartyMon1 - call GetSelectedMoveOffset - ld bc, wPartyMon1PP - wPartyMon1Moves - add hl, bc ; hl now points to move's PP - ld a, [wMaxPP] - ld b, a - ld a, [wPPRestoreItem] - cp MAX_ETHER - jr z, .fullyRestorePP - ld a, [hl] ; move PP - and %00111111 ; lower 6 bit bits store current PP - cp b ; does current PP equal max PP? - ret z ; if so, return - add 10 ; increase current PP by 10 -; b holds the max PP amount and b will hold the new PP amount. -; So, if the new amount meets or exceeds the max amount, -; cap the amount to the max amount by leaving b unchanged. -; Otherwise, store the new amount in b. - cp b ; does the new amount meet or exceed the maximum? - jr nc, .storeNewAmount - ld b, a -.storeNewAmount - ld a, [hl] ; move PP - and %11000000 ; PP Up counter bits - add b - ld [hl], a - ret -.fullyRestorePP - ld a, [hl] ; move PP -; Note that this code has a bug. It doesn't mask out the upper two bits, which -; are used to count how many PP Ups have been used on the move. So, Max Ethers -; and Max Elixirs will not be detected as having no effect on a move with full -; PP if the move has had any PP Ups used on it. - cp b ; does current PP equal max PP? - ret z - jr .storeNewAmount -.useElixir -; decrement the item ID so that ELIXER becomes ETHER and MAX_ELIXER becomes MAX_ETHER - ld hl, wPPRestoreItem - dec [hl] - dec [hl] - xor a - ld hl, wCurrentMenuItem - ld [hli], a - ld [hl], a ; zero the counter for number of moves that had their PP restored - ld b, 4 -; loop through each move and restore PP -.elixirLoop - push bc - ld hl, wPartyMon1Moves - ld bc, wPartyMon2 - wPartyMon1 - call GetSelectedMoveOffset - ld a, [hl] - and a ; does the current slot have a move? - jr z, .nextMove - call .restorePP - jr z, .nextMove -; if some PP was restored - ld hl, wTileBehindCursor ; counter for number of moves that had their PP restored - inc [hl] -.nextMove - ld hl, wCurrentMenuItem - inc [hl] - pop bc - dec b - jr nz, .elixirLoop - ld a, [wTileBehindCursor] - and a ; did any moves have their PP restored? - jp nz, .afterRestoringPP -.noEffect - call ItemUseNoEffect -.itemNotUsed - call GBPalWhiteOut - call RunDefaultPaletteCommand - pop af - xor a - ld [wActionResultOrTookBattleTurn], a ; item use failed - ret - -RaisePPWhichTechniqueText: - TX_FAR _RaisePPWhichTechniqueText - db "@" - -RestorePPWhichTechniqueText: - TX_FAR _RestorePPWhichTechniqueText - db "@" - -PPMaxedOutText: - TX_FAR _PPMaxedOutText - db "@" - -PPIncreasedText: - TX_FAR _PPIncreasedText - db "@" - -PPRestoredText: - TX_FAR _PPRestoredText - db "@" - -; for items that can't be used from the Item menu -UnusableItem: - jp ItemUseNotTime - -ItemUseTMHM: - ld a, [wIsInBattle] - and a - jp nz, ItemUseNotTime - ld a, [wcf91] - sub TM_01 - push af - jr nc, .skipAdding - add 55 ; if item is an HM, add 55 -.skipAdding - inc a - ld [wd11e], a - predef TMToMove ; get move ID from TM/HM ID - ld a, [wd11e] - ld [wMoveNum], a - call GetMoveName - call CopyStringToCF4B ; copy name to wcf4b - pop af - ld hl, BootedUpTMText - jr nc, .printBootedUpMachineText - ld hl, BootedUpHMText -.printBootedUpMachineText - call PrintText - ld hl, TeachMachineMoveText - call PrintText - coord hl, 14, 7 - lb bc, 8, 15 - ld a, TWO_OPTION_MENU - ld [wTextBoxID], a - call DisplayTextBoxID ; yes/no menu - ld a, [wCurrentMenuItem] - and a - jr z, .useMachine - ld a, 2 - ld [wActionResultOrTookBattleTurn], a ; item not used - ret -.useMachine - ld a, [wWhichPokemon] - push af - ld a, [wcf91] - push af -.chooseMon - ld hl, wcf4b - ld de, wTempMoveNameBuffer - ld bc, 14 - call CopyData ; save the move name because DisplayPartyMenu will overwrite it - ld a, $ff - ld [wUpdateSpritesEnabled], a - ld a, TMHM_PARTY_MENU - ld [wPartyMenuTypeOrMessageID], a - call DisplayPartyMenu - push af - ld hl, wTempMoveNameBuffer - ld de, wcf4b - ld bc, 14 - call CopyData - pop af - jr nc, .checkIfAbleToLearnMove -; if the player canceled teaching the move - pop af - pop af - call GBPalWhiteOutWithDelay3 - call ClearSprites - call RunDefaultPaletteCommand - jp LoadScreenTilesFromBuffer1 ; restore saved screen -.checkIfAbleToLearnMove - predef CanLearnTM ; check if the pokemon can learn the move - push bc - ld a, [wWhichPokemon] - ld hl, wPartyMonNicks - call GetPartyMonName - pop bc - ld a, c - and a ; can the pokemon learn the move? - jr nz, .checkIfAlreadyLearnedMove -; if the pokemon can't learn the move - ld a, SFX_DENIED - call PlaySoundWaitForCurrent - ld hl, MonCannotLearnMachineMoveText - call PrintText - jr .chooseMon -.checkIfAlreadyLearnedMove - callab CheckIfMoveIsKnown ; check if the pokemon already knows the move - jr c, .chooseMon - predef LearnMove ; teach move - pop af - ld [wcf91], a - pop af - ld [wWhichPokemon], a - ld a, b - and a - ret z - ld a, [wcf91] - call IsItemHM - ret c - jp RemoveUsedItem - -BootedUpTMText: - TX_FAR _BootedUpTMText - db "@" - -BootedUpHMText: - TX_FAR _BootedUpHMText - db "@" - -TeachMachineMoveText: - TX_FAR _TeachMachineMoveText - db "@" - -MonCannotLearnMachineMoveText: - TX_FAR _MonCannotLearnMachineMoveText - db "@" - -PrintItemUseTextAndRemoveItem: - ld hl, ItemUseText00 - call PrintText - ld a, SFX_HEAL_AILMENT - call PlaySound - call WaitForTextScrollButtonPress ; wait for button press - -RemoveUsedItem: - ld hl, wNumBagItems - ld a, 1 ; one item - ld [wItemQuantity], a - jp RemoveItemFromInventory - -ItemUseNoEffect: - ld hl, ItemUseNoEffectText - jr ItemUseFailed - -ItemUseNotTime: - ld hl, ItemUseNotTimeText - jr ItemUseFailed - -ItemUseNotYoursToUse: - ld hl, ItemUseNotYoursToUseText - jr ItemUseFailed - -ThrowBallAtTrainerMon: - call RunDefaultPaletteCommand - call LoadScreenTilesFromBuffer1 ; restore saved screen - call Delay3 - ld a, TOSS_ANIM - ld [wAnimationID], a - predef MoveAnimation ; do animation - ld hl, ThrowBallAtTrainerMonText1 - call PrintText - ld hl, ThrowBallAtTrainerMonText2 - call PrintText - jr RemoveUsedItem - -NoCyclingAllowedHere: - ld hl, NoCyclingAllowedHereText - jr ItemUseFailed - -BoxFullCannotThrowBall: - ld hl, BoxFullCannotThrowBallText - jr ItemUseFailed - -SurfingAttemptFailed: - ld hl, NoSurfingHereText - -ItemUseFailed: - xor a - ld [wActionResultOrTookBattleTurn], a ; item use failed - jp PrintText - -ItemUseNotTimeText: - TX_FAR _ItemUseNotTimeText - db "@" - -ItemUseNotYoursToUseText: - TX_FAR _ItemUseNotYoursToUseText - db "@" - -ItemUseNoEffectText: - TX_FAR _ItemUseNoEffectText - db "@" - -ThrowBallAtTrainerMonText1: - TX_FAR _ThrowBallAtTrainerMonText1 - db "@" - -ThrowBallAtTrainerMonText2: - TX_FAR _ThrowBallAtTrainerMonText2 - db "@" - -NoCyclingAllowedHereText: - TX_FAR _NoCyclingAllowedHereText - db "@" - -NoSurfingHereText: - TX_FAR _NoSurfingHereText - db "@" - -BoxFullCannotThrowBallText: - TX_FAR _BoxFullCannotThrowBallText - db "@" - -ItemUseText00: - TX_FAR _ItemUseText001 - TX_LINE - TX_FAR _ItemUseText002 - db "@" - -GotOnBicycleText: - TX_FAR _GotOnBicycleText1 - TX_LINE - TX_FAR _GotOnBicycleText2 - db "@" - -GotOffBicycleText: - TX_FAR _GotOffBicycleText1 - TX_LINE - TX_FAR _GotOffBicycleText2 - db "@" - -; restores bonus PP (from PP Ups) when healing at a pokemon center -; also, when a PP Up is used, it increases the current PP by one PP Up bonus -; INPUT: -; [wWhichPokemon] = index of pokemon in party -; [wCurrentMenuItem] = index of move (when using a PP Up) -RestoreBonusPP: - ld hl, wPartyMon1Moves - ld bc, wPartyMon2 - wPartyMon1 - ld a, [wWhichPokemon] - call AddNTimes - push hl - ld de, wNormalMaxPPList - 1 - predef LoadMovePPs ; loads the normal max PP of each of the pokemon's moves to wNormalMaxPPList - pop hl - ld c, wPartyMon1PP - wPartyMon1Moves - ld b, 0 - add hl, bc ; hl now points to move 1 PP - ld de, wNormalMaxPPList - ld b, 0 ; initialize move counter to zero -; loop through the pokemon's moves -.loop - inc b - ld a, b - cp 5 ; reached the end of the pokemon's moves? - ret z ; if so, return - ld a, [wUsingPPUp] - dec a ; using a PP Up? - jr nz, .skipMenuItemIDCheck -; if using a PP Up, check if this is the move it's being used on - ld a, [wCurrentMenuItem] - inc a - cp b - jr nz, .nextMove -.skipMenuItemIDCheck - ld a, [hl] - and %11000000 ; have any PP Ups been used? - call nz, AddBonusPP ; if so, add bonus PP -.nextMove - inc hl - inc de - jr .loop - -; adds bonus PP from PP Ups to current PP -; 1/5 of normal max PP (capped at 7) is added for each PP Up -; INPUT: -; [de] = normal max PP -; [hl] = move PP -AddBonusPP: - push bc - ld a, [de] ; normal max PP of move - ld [H_DIVIDEND + 3], a - xor a - ld [H_DIVIDEND], a - ld [H_DIVIDEND + 1], a - ld [H_DIVIDEND + 2], a - ld a, 5 - ld [H_DIVISOR], a - ld b, 4 - call Divide - ld a, [hl] ; move PP - ld b, a - swap a - and %00001111 - srl a - srl a - ld c, a ; c = number of PP Ups used -.loop - ld a, [H_QUOTIENT + 3] - cp 8 ; is the amount greater than or equal to 8? - jr c, .addAmount - ld a, 7 ; cap the amount at 7 -.addAmount - add b - ld b, a - ld a, [wUsingPPUp] - dec a ; is the player using a PP Up right now? - jr z, .done ; if so, only add the bonus once - dec c - jr nz, .loop -.done - ld [hl], b - pop bc - ret - -; gets max PP of a pokemon's move (including PP from PP Ups) -; INPUT: -; [wWhichPokemon] = index of pokemon within party/box -; [wMonDataLocation] = pokemon source -; 00: player's party -; 01: enemy's party -; 02: current box -; 03: daycare -; 04: player's in-battle pokemon -; [wCurrentMenuItem] = move index -; OUTPUT: -; [wMaxPP] = max PP -GetMaxPP: - ld a, [wMonDataLocation] - and a - ld hl, wPartyMon1Moves - ld bc, wPartyMon2 - wPartyMon1 - jr z, .sourceWithMultipleMon - ld hl, wEnemyMon1Moves - dec a - jr z, .sourceWithMultipleMon - ld hl, wBoxMon1Moves - ld bc, wBoxMon2 - wBoxMon1 - dec a - jr z, .sourceWithMultipleMon - ld hl, wDayCareMonMoves - dec a - jr z, .sourceWithOneMon - ld hl, wBattleMonMoves ; player's in-battle pokemon -.sourceWithOneMon - call GetSelectedMoveOffset2 - jr .next -.sourceWithMultipleMon - call GetSelectedMoveOffset -.next - ld a, [hl] - dec a - push hl - ld hl, Moves - ld bc, MoveEnd - Moves - call AddNTimes - ld de, wcd6d - ld a, BANK(Moves) - call FarCopyData - ld de, wcd6d + 5 ; PP is byte 5 of move data - ld a, [de] - ld b, a ; b = normal max PP - pop hl - push bc - ld bc, wPartyMon1PP - wPartyMon1Moves ; PP offset if not player's in-battle pokemon data - ld a, [wMonDataLocation] - cp 4 ; player's in-battle pokemon? - jr nz, .addPPOffset - ld bc, wBattleMonPP - wBattleMonMoves ; PP offset if player's in-battle pokemon data -.addPPOffset - add hl, bc - ld a, [hl] ; a = current PP - and %11000000 ; get PP Up count - pop bc - or b ; place normal max PP in 6 lower bits of a - ld h, d - ld l, e - inc hl ; hl = wcd73 - ld [hl], a - xor a ; add the bonus for the existing PP Up count - ld [wUsingPPUp], a - call AddBonusPP ; add bonus PP from PP Ups - ld a, [hl] - and %00111111 ; mask out the PP Up count - ld [wMaxPP], a ; store max PP - ret - -GetSelectedMoveOffset: - ld a, [wWhichPokemon] - call AddNTimes - -GetSelectedMoveOffset2: - ld a, [wCurrentMenuItem] - ld c, a - ld b, 0 - add hl, bc - ret - -; confirms the item toss and then tosses the item -; INPUT: -; hl = address of inventory (either wNumBagItems or wNumBoxItems) -; [wcf91] = item ID -; [wWhichPokemon] = index of item within inventory -; [wItemQuantity] = quantity to toss -; OUTPUT: -; clears carry flag if the item is tossed, sets carry flag if not -TossItem_:: - push hl - ld a, [wcf91] - call IsItemHM - pop hl - jr c, .tooImportantToToss - push hl - call IsKeyItem_ - ld a, [wIsKeyItem] - pop hl - and a - jr nz, .tooImportantToToss - push hl - ld a, [wcf91] - ld [wd11e], a - call GetItemName - call CopyStringToCF4B ; copy name to wcf4b - ld hl, IsItOKToTossItemText - call PrintText - coord hl, 14, 7 - lb bc, 8, 15 - ld a, TWO_OPTION_MENU - ld [wTextBoxID], a - call DisplayTextBoxID ; yes/no menu - ld a, [wMenuExitMethod] - cp CHOSE_SECOND_ITEM - pop hl - scf - ret z ; return if the player chose No -; if the player chose Yes - push hl - ld a, [wWhichPokemon] - call RemoveItemFromInventory - ld a, [wcf91] - ld [wd11e], a - call GetItemName - call CopyStringToCF4B ; copy name to wcf4b - ld hl, ThrewAwayItemText - call PrintText - pop hl - and a - ret -.tooImportantToToss - push hl - ld hl, TooImportantToTossText - call PrintText - pop hl - scf - ret - -ThrewAwayItemText: - TX_FAR _ThrewAwayItemText - db "@" - -IsItOKToTossItemText: - TX_FAR _IsItOKToTossItemText - db "@" - -TooImportantToTossText: - TX_FAR _TooImportantToTossText - db "@" - -; checks if an item is a key item -; INPUT: -; [wcf91] = item ID -; OUTPUT: -; [wIsKeyItem] = result -; 00: item is not key item -; 01: item is key item -IsKeyItem_:: - ld a, $01 - ld [wIsKeyItem], a - ld a, [wcf91] - cp HM_01 ; is the item an HM or TM? - jr nc, .checkIfItemIsHM -; if the item is not an HM or TM - push af - ld hl, KeyItemBitfield - ld de, wBuffer - ld bc, 15 ; only 11 bytes are actually used - call CopyData - pop af - dec a - ld c, a - ld hl, wBuffer - ld b, FLAG_TEST - predef FlagActionPredef - ld a, c - and a - ret nz -.checkIfItemIsHM - ld a, [wcf91] - call IsItemHM - ret c - xor a - ld [wIsKeyItem], a - ret - -INCLUDE "data/key_items.asm" - -SendNewMonToBox: - ld de, wNumInBox - ld a, [de] - inc a - ld [de], a - ld a, [wcf91] - ld [wd0b5], a - ld c, a -.asm_e7b1 - inc de - ld a, [de] - ld b, a - ld a, c - ld c, b - ld [de], a - cp $ff - jr nz, .asm_e7b1 - call GetMonHeader - ld hl, wBoxMonOT - ld bc, NAME_LENGTH - ld a, [wNumInBox] - dec a - jr z, .asm_e7ee - dec a - call AddNTimes - push hl - ld bc, NAME_LENGTH - add hl, bc - ld d, h - ld e, l - pop hl - ld a, [wNumInBox] - dec a - ld b, a -.asm_e7db - push bc - push hl - ld bc, NAME_LENGTH - call CopyData - pop hl - ld d, h - ld e, l - ld bc, -NAME_LENGTH - add hl, bc - pop bc - dec b - jr nz, .asm_e7db -.asm_e7ee - ld hl, wPlayerName - ld de, wBoxMonOT - ld bc, NAME_LENGTH - call CopyData - ld a, [wNumInBox] - dec a - jr z, .asm_e82a - ld hl, wBoxMonNicks - ld bc, NAME_LENGTH - dec a - call AddNTimes - push hl - ld bc, NAME_LENGTH - add hl, bc - ld d, h - ld e, l - pop hl - ld a, [wNumInBox] - dec a - ld b, a -.asm_e817 - push bc - push hl - ld bc, NAME_LENGTH - call CopyData - pop hl - ld d, h - ld e, l - ld bc, -NAME_LENGTH - add hl, bc - pop bc - dec b - jr nz, .asm_e817 -.asm_e82a - ld hl, wBoxMonNicks - ld a, NAME_MON_SCREEN - ld [wNamingScreenType], a - predef AskName - ld a, [wNumInBox] - dec a - jr z, .asm_e867 - ld hl, wBoxMons - ld bc, wBoxMon2 - wBoxMon1 - dec a - call AddNTimes - push hl - ld bc, wBoxMon2 - wBoxMon1 - add hl, bc - ld d, h - ld e, l - pop hl - ld a, [wNumInBox] - dec a - ld b, a -.asm_e854 - push bc - push hl - ld bc, wBoxMon2 - wBoxMon1 - call CopyData - pop hl - ld d, h - ld e, l - ld bc, wBoxMon1 - wBoxMon2 - add hl, bc - pop bc - dec b - jr nz, .asm_e854 -.asm_e867 - ld a, [wEnemyMonLevel] - ld [wEnemyMonBoxLevel], a - ld hl, wEnemyMon - ld de, wBoxMon1 - ld bc, wEnemyMonDVs - wEnemyMon - call CopyData - ld hl, wPlayerID - ld a, [hli] - ld [de], a - inc de - ld a, [hl] - ld [de], a - inc de - push de - ld a, [wCurEnemyLVL] - ld d, a - callab CalcExperience - pop de - ld a, [hExperience] - ld [de], a - inc de - ld a, [hExperience + 1] - ld [de], a - inc de - ld a, [hExperience + 2] - ld [de], a - inc de - xor a - ld b, NUM_STATS * 2 -.asm_e89f - ld [de], a - inc de - dec b - jr nz, .asm_e89f - ld hl, wEnemyMonDVs - ld a, [hli] - ld [de], a - inc de - ld a, [hli] - ld [de], a - ld hl, wEnemyMonPP - ld b, NUM_MOVES -.asm_e8b1 - ld a, [hli] - inc de - ld [de], a - dec b - jr nz, .asm_e8b1 - ret - -; checks if the tile in front of the player is a shore or water tile -; used for surfing and fishing -; unsets carry if it is, sets carry if not -IsNextTileShoreOrWater: - ld a, [wCurMapTileset] - ld hl, WaterTilesets - ld de, 1 - call IsInArray - jr nc, .notShoreOrWater - ld a, [wCurMapTileset] - cp SHIP_PORT ; Vermilion Dock tileset - ld a, [wTileInFrontOfPlayer] ; tile in front of player - jr z, .skipShoreTiles ; if it's the Vermilion Dock tileset - cp $48 ; eastern shore tile in Safari Zone - jr z, .shoreOrWater - cp $32 ; usual eastern shore tile - jr z, .shoreOrWater -.skipShoreTiles - cp $14 ; water tile - jr z, .shoreOrWater -.notShoreOrWater - scf - ret -.shoreOrWater - and a - ret - -INCLUDE "data/water_tilesets.asm" - -ReadSuperRodData: -; return e = 2 if no fish on this map -; return e = 1 if a bite, bc = level,species -; return e = 0 if no bite - ld a, [wCurMap] - ld de, 3 ; each fishing group is three bytes wide - ld hl, SuperRodData - call IsInArray - jr c, .ReadFishingGroup - ld e, $2 ; $2 if no fishing groups found - ret - -.ReadFishingGroup -; hl points to the fishing group entry in the index - inc hl ; skip map id - - ; read fishing group address - ld a, [hli] - ld h, [hl] - ld l, a - - ld b, [hl] ; how many mons in group - inc hl ; point to data - ld e, $0 ; no bite yet - -.RandomLoop - call Random - srl a - ret c ; 50% chance of no battle - - and %11 ; 2-bit random number - cp b - jr nc, .RandomLoop ; if a is greater than the number of mons, regenerate - - ; get the mon - add a - ld c, a - ld b, $0 - add hl, bc - ld b, [hl] ; level - inc hl - ld c, [hl] ; species - ld e, $1 ; $1 if there's a bite - ret - -INCLUDE "data/super_rod.asm" - -; reloads map view and processes sprite data -; for items that cause the overworld to be displayed -ItemUseReloadOverworldData: - call LoadCurrentMapView - jp UpdateSprites - -; creates a list at wBuffer of maps where the mon in [wd11e] can be found. -; this is used by the pokedex to display locations the mon can be found on the map. -FindWildLocationsOfMon: - ld hl, WildDataPointers - ld de, wBuffer - ld c, $0 -.loop - inc hl - ld a, [hld] - inc a - jr z, .done - push hl - ld a, [hli] - ld h, [hl] - ld l, a - ld a, [hli] - and a - call nz, CheckMapForMon ; land - ld a, [hli] - and a - call nz, CheckMapForMon ; water - pop hl - inc hl - inc hl - inc c - jr .loop -.done - ld a, $ff ; list terminator - ld [de], a - ret - -CheckMapForMon: - inc hl - ld b, $a -.loop - ld a, [wd11e] - cp [hl] - jr nz, .nextEntry - ld a, c - ld [de], a - inc de -.nextEntry - inc hl - inc hl - dec b - jr nz, .loop - dec hl - ret diff --git a/engine/items/subtract_paid_money.asm b/engine/items/subtract_paid_money.asm new file mode 100644 index 00000000..fdefe3d6 --- /dev/null +++ b/engine/items/subtract_paid_money.asm @@ -0,0 +1,17 @@ +; subtracts the amount the player paid from their money +; OUTPUT: carry = 0(success) or 1(fail because there is not enough money) +SubtractAmountPaidFromMoney_:: + ld de, wPlayerMoney + ld hl, hMoney ; total price of items + ld c, 3 ; length of money in bytes + call StringCmp + ret c + ld de, wPlayerMoney + 2 + ld hl, hMoney + 2 ; total price of items + ld c, 3 ; length of money in bytes + predef SubBCDPredef ; subtract total price from money + ld a, MONEY_BOX + ld [wTextBoxID], a + call DisplayTextBoxID ; redraw money text box + and a + ret diff --git a/engine/items/town_map.asm b/engine/items/town_map.asm new file mode 100755 index 00000000..84a92994 --- /dev/null +++ b/engine/items/town_map.asm @@ -0,0 +1,618 @@ +DisplayTownMap: + call LoadTownMap + ld hl, wUpdateSpritesEnabled + ld a, [hl] + push af + ld [hl], $ff + push hl + ld a, $1 + ld [hJoy7], a + ld a, [wCurMap] + push af + ld b, $0 + call DrawPlayerOrBirdSprite ; player sprite + coord hl, 1, 0 + ld de, wcd6d + call PlaceString + ld hl, wOAMBuffer + ld de, wTileMapBackup + ld bc, $10 + call CopyData + ld hl, vSprites + $40 + ld de, TownMapCursor + lb bc, BANK(TownMapCursor), (TownMapCursorEnd - TownMapCursor) / $8 + call CopyVideoDataDouble + xor a + ld [wWhichTownMapLocation], a + pop af + jr .enterLoop + +.townMapLoop + coord hl, 0, 0 + lb bc, 1, 20 + call ClearScreenArea + ld hl, TownMapOrder + ld a, [wWhichTownMapLocation] + ld c, a + ld b, 0 + add hl, bc + ld a, [hl] +.enterLoop + ld de, wTownMapCoords + call LoadTownMapEntry + ld a, [de] + push hl + call TownMapCoordsToOAMCoords + ld a, $4 + ld [wOAMBaseTile], a + ld hl, wOAMBuffer + $10 + call WriteTownMapSpriteOAM ; town map cursor sprite + pop hl + ld de, wcd6d +.copyMapName + ld a, [hli] + ld [de], a + inc de + cp $50 + jr nz, .copyMapName + coord hl, 1, 0 + ld de, wcd6d + call PlaceString + ld hl, wOAMBuffer + $10 + ld de, wTileMapBackup + 16 + ld bc, $10 + call CopyData +.inputLoop + call TownMapSpriteBlinkingAnimation + call JoypadLowSensitivity + ld a, [hJoy5] + ld b, a + and A_BUTTON | B_BUTTON | D_UP | D_DOWN + jr z, .inputLoop + ld a, SFX_TINK + call PlaySound + bit 6, b + jr nz, .pressedUp + bit 7, b + jr nz, .pressedDown + xor a + ld [wTownMapSpriteBlinkingEnabled], a + ld [hJoy7], a + ld [wAnimCounter], a + call ExitTownMap + pop hl + pop af + ld [hl], a + ret +.pressedUp + ld a, [wWhichTownMapLocation] + inc a + cp TownMapOrderEnd - TownMapOrder ; number of list items + 1 + jr nz, .noOverflow + xor a +.noOverflow + ld [wWhichTownMapLocation], a + jp .townMapLoop +.pressedDown + ld a, [wWhichTownMapLocation] + dec a + cp -1 + jr nz, .noUnderflow + ld a, TownMapOrderEnd - TownMapOrder - 1 ; number of list items +.noUnderflow + ld [wWhichTownMapLocation], a + jp .townMapLoop + +INCLUDE "data/town_map_order.asm" + +TownMapCursor: + INCBIN "gfx/town_map/town_map_cursor.1bpp" +TownMapCursorEnd: + +LoadTownMap_Nest: + call LoadTownMap + ld hl, wUpdateSpritesEnabled + ld a, [hl] + push af + ld [hl], $ff + push hl + call DisplayWildLocations + call GetMonName + coord hl, 1, 0 + call PlaceString + ld h, b + ld l, c + ld de, MonsNestText + call PlaceString + call WaitForTextScrollButtonPress + call ExitTownMap + pop hl + pop af + ld [hl], a + ret + +MonsNestText: + db "'s NEST@" + +LoadTownMap_Fly:: + call ClearSprites + call LoadTownMap + call LoadPlayerSpriteGraphics + call LoadFontTilePatterns + ld de, BirdSprite + ld hl, vSprites + $40 + lb bc, BANK(BirdSprite), $c + call CopyVideoData + ld de, TownMapUpArrow + ld hl, vChars1 + $6d0 + lb bc, BANK(TownMapUpArrow), (TownMapUpArrowEnd - TownMapUpArrow) / $8 + call CopyVideoDataDouble + call BuildFlyLocationsList + ld hl, wUpdateSpritesEnabled + ld a, [hl] + push af + ld [hl], $ff + push hl + coord hl, 0, 0 + ld de, ToText + call PlaceString + ld a, [wCurMap] + ld b, $0 + call DrawPlayerOrBirdSprite + ld hl, wFlyLocationsList + coord de, 18, 0 +.townMapFlyLoop + ld a, " " + ld [de], a + push hl + push hl + coord hl, 3, 0 + lb bc, 1, 15 + call ClearScreenArea + pop hl + ld a, [hl] + ld b, $4 + call DrawPlayerOrBirdSprite ; draw bird sprite + coord hl, 3, 0 + ld de, wcd6d + call PlaceString + ld c, 15 + call DelayFrames + coord hl, 18, 0 + ld [hl], "▲" + coord hl, 19, 0 + ld [hl], "▼" + pop hl +.inputLoop + push hl + call DelayFrame + call JoypadLowSensitivity + ld a, [hJoy5] + ld b, a + pop hl + and A_BUTTON | B_BUTTON | D_UP | D_DOWN + jr z, .inputLoop + bit 0, b + jr nz, .pressedA + ld a, SFX_TINK + call PlaySound + bit 6, b + jr nz, .pressedUp + bit 7, b + jr nz, .pressedDown + jr .pressedB +.pressedA + ld a, SFX_HEAL_AILMENT + call PlaySound + ld a, [hl] + ld [wDestinationMap], a + ld hl, wd732 + set 3, [hl] + inc hl + set 7, [hl] +.pressedB + xor a + ld [wTownMapSpriteBlinkingEnabled], a + call GBPalWhiteOutWithDelay3 + pop hl + pop af + ld [hl], a + ret +.pressedUp + coord de, 18, 0 + inc hl + ld a, [hl] + cp $ff + jr z, .wrapToStartOfList + cp $fe + jr z, .pressedUp ; skip past unvisited towns + jp .townMapFlyLoop +.wrapToStartOfList + ld hl, wFlyLocationsList + jp .townMapFlyLoop +.pressedDown + coord de, 19, 0 + dec hl + ld a, [hl] + cp $ff + jr z, .wrapToEndOfList + cp $fe + jr z, .pressedDown ; skip past unvisited towns + jp .townMapFlyLoop +.wrapToEndOfList + ld hl, wFlyLocationsList + 11 + jr .pressedDown + +ToText: + db "To@" + +BuildFlyLocationsList: + ld hl, wFlyLocationsList - 1 + ld [hl], $ff + inc hl + ld a, [wTownVisitedFlag] + ld e, a + ld a, [wTownVisitedFlag + 1] + ld d, a + ld bc, SAFFRON_CITY + 1 +.loop + srl d + rr e + ld a, $fe ; store $fe if the town hasn't been visited + jr nc, .notVisited + ld a, b ; store the map number of the town if it has been visited +.notVisited + ld [hl], a + inc hl + inc b + dec c + jr nz, .loop + ld [hl], $ff + ret + +TownMapUpArrow: + INCBIN "gfx/town_map/up_arrow.1bpp" +TownMapUpArrowEnd: + +LoadTownMap: + call GBPalWhiteOutWithDelay3 + call ClearScreen + call UpdateSprites + coord hl, 0, 0 + ld b, $12 + ld c, $12 + call TextBoxBorder + call DisableLCD + ld hl, WorldMapTileGraphics + ld de, vChars2 + $600 + ld bc, WorldMapTileGraphicsEnd - WorldMapTileGraphics + ld a, BANK(WorldMapTileGraphics) + call FarCopyData2 + ld hl, MonNestIcon + ld de, vSprites + $40 + ld bc, MonNestIconEnd - MonNestIcon + ld a, BANK(MonNestIcon) + call FarCopyDataDouble + coord hl, 0, 0 + ld de, CompressedMap +.nextTile + ld a, [de] + and a + jr z, .done + ld b, a + and $f + ld c, a + ld a, b + swap a + and $f + add $60 +.writeRunLoop + ld [hli], a + dec c + jr nz, .writeRunLoop + inc de + jr .nextTile +.done + call EnableLCD + ld b, SET_PAL_TOWN_MAP + call RunPaletteCommand + call Delay3 + call GBPalNormal + xor a + ld [wAnimCounter], a + inc a + ld [wTownMapSpriteBlinkingEnabled], a + ret + +CompressedMap: + INCBIN "gfx/town_map/town_map.rle" + +ExitTownMap: +; clear town map graphics data and load usual graphics data + xor a + ld [wTownMapSpriteBlinkingEnabled], a + call GBPalWhiteOut + call ClearScreen + call ClearSprites + call LoadPlayerSpriteGraphics + call LoadFontTilePatterns + call UpdateSprites + jp RunDefaultPaletteCommand + +DrawPlayerOrBirdSprite: +; a = map number +; b = OAM base tile + push af + ld a, b + ld [wOAMBaseTile], a + pop af + ld de, wTownMapCoords + call LoadTownMapEntry + ld a, [de] + push hl + call TownMapCoordsToOAMCoords + call WritePlayerOrBirdSpriteOAM + pop hl + ld de, wcd6d +.loop + ld a, [hli] + ld [de], a + inc de + cp "@" + jr nz, .loop + ld hl, wOAMBuffer + ld de, wTileMapBackup + ld bc, $a0 + jp CopyData + +DisplayWildLocations: + callba FindWildLocationsOfMon + call ZeroOutDuplicatesInList + ld hl, wOAMBuffer + ld de, wTownMapCoords +.loop + ld a, [de] + cp $ff + jr z, .exitLoop + and a + jr z, .nextEntry + push hl + call LoadTownMapEntry + pop hl + ld a, [de] + cp $19 ; Cerulean Cave's coordinates + jr z, .nextEntry ; skip Cerulean Cave + call TownMapCoordsToOAMCoords + ld a, $4 ; nest icon tile no. + ld [hli], a + xor a + ld [hli], a +.nextEntry + inc de + jr .loop +.exitLoop + ld a, l + and a ; were any OAM entries written? + jr nz, .drawPlayerSprite +; if no OAM entries were written, print area unknown text + coord hl, 1, 7 + ld b, 2 + ld c, 15 + call TextBoxBorder + coord hl, 2, 9 + ld de, AreaUnknownText + call PlaceString + jr .done +.drawPlayerSprite + ld a, [wCurMap] + ld b, $0 + call DrawPlayerOrBirdSprite +.done + ld hl, wOAMBuffer + ld de, wTileMapBackup + ld bc, $a0 + jp CopyData + +AreaUnknownText: + db " AREA UNKNOWN@" + +TownMapCoordsToOAMCoords: +; in: lower nybble of a = x, upper nybble of a = y +; out: b and [hl] = (y * 8) + 24, c and [hl+1] = (x * 8) + 24 + push af + and $f0 + srl a + add 24 + ld b, a + ld [hli], a + pop af + and $f + swap a + srl a + add 24 + ld c, a + ld [hli], a + ret + +WritePlayerOrBirdSpriteOAM: + ld a, [wOAMBaseTile] + and a + ld hl, wOAMBuffer + $90 ; for player sprite + jr z, WriteTownMapSpriteOAM + ld hl, wOAMBuffer + $80 ; for bird sprite + +WriteTownMapSpriteOAM: + push hl + +; Subtract 4 from c (X coord) and 4 from b (Y coord). However, the carry from c +; is added to b, so the net result is that only 3 is subtracted from b. + lb hl, -4, -4 + add hl, bc + + ld b, h + ld c, l + pop hl + +WriteAsymmetricMonPartySpriteOAM: +; Writes 4 OAM blocks for a helix mon party sprite, since it does not have +; a vertical line of symmetry. + lb de, 2, 2 +.loop + push de + push bc +.innerLoop + ld a, b + ld [hli], a + ld a, c + ld [hli], a + ld a, [wOAMBaseTile] + ld [hli], a + inc a + ld [wOAMBaseTile], a + xor a + ld [hli], a + inc d + ld a, 8 + add c + ld c, a + dec e + jr nz, .innerLoop + pop bc + pop de + ld a, 8 + add b + ld b, a + dec d + jr nz, .loop + ret + +WriteSymmetricMonPartySpriteOAM: +; Writes 4 OAM blocks for a mon party sprite other than a helix. All the +; sprites other than the helix one have a vertical line of symmetry which allows +; the X-flip OAM bit to be used so that only 2 rather than 4 tile patterns are +; needed. + xor a + ld [wSymmetricSpriteOAMAttributes], a + lb de, 2, 2 +.loop + push de + push bc +.innerLoop + ld a, b + ld [hli], a ; Y + ld a, c + ld [hli], a ; X + ld a, [wOAMBaseTile] + ld [hli], a ; tile + ld a, [wSymmetricSpriteOAMAttributes] + ld [hli], a ; attributes + xor (1 << OAM_X_FLIP) + ld [wSymmetricSpriteOAMAttributes], a + inc d + ld a, 8 + add c + ld c, a + dec e + jr nz, .innerLoop + pop bc + pop de + push hl + ld hl, wOAMBaseTile + inc [hl] + inc [hl] + pop hl + ld a, 8 + add b + ld b, a + dec d + jr nz, .loop + ret + +ZeroOutDuplicatesInList: +; replace duplicate bytes in the list of wild pokemon locations with 0 + ld de, wBuffer +.loop + ld a, [de] + inc de + cp $ff + ret z + ld c, a + ld l, e + ld h, d +.zeroDuplicatesLoop + ld a, [hl] + cp $ff + jr z, .loop + cp c + jr nz, .skipZeroing + xor a + ld [hl], a +.skipZeroing + inc hl + jr .zeroDuplicatesLoop + +LoadTownMapEntry: +; in: a = map number +; out: lower nybble of [de] = x, upper nybble of [de] = y, hl = address of name + cp REDS_HOUSE_1F + jr c, .external + ld bc, 4 + ld hl, InternalMapEntries +.loop + cp [hl] + jr c, .foundEntry + add hl, bc + jr .loop +.foundEntry + inc hl + jr .readEntry +.external + ld hl, ExternalMapEntries + ld c, a + ld b, 0 + add hl, bc + add hl, bc + add hl, bc +.readEntry + ld a, [hli] + ld [de], a + ld a, [hli] + ld h, [hl] + ld l, a + ret + +INCLUDE "data/town_map_entries.asm" + +INCLUDE "text/map_names.asm" + +MonNestIcon: + INCBIN "gfx/town_map/mon_nest_icon.1bpp" +MonNestIconEnd: + +TownMapSpriteBlinkingAnimation:: + ld a, [wAnimCounter] + inc a + cp 25 + jr z, .hideSprites + cp 50 + jr nz, .done +; show sprites when the counter reaches 50 + ld hl, wTileMapBackup + ld de, wOAMBuffer + ld bc, $90 + call CopyData + xor a + jr .done +.hideSprites + ld hl, wOAMBuffer + ld b, $24 + ld de, $4 +.hideSpritesLoop + ld [hl], $a0 + add hl, de + dec b + jr nz, .hideSpritesLoop + ld a, 25 +.done + ld [wAnimCounter], a + jp DelayFrame -- cgit v1.2.3