kep-hack/engine/pokemon/evos_moves.asm
Llinos Evans d22bb2994b (mostly) fix the tradeback tutor
This makes it so everything except Rhydon works properly. At this point I'm willing to have Rhydon not work, they can just evolve into Rhyperior instead.
2023-05-01 19:39:30 +01:00

811 lines
16 KiB
NASM

; try to evolve the mon in [wWhichPokemon]
TryEvolvingMon:
ld hl, wCanEvolveFlags
xor a
ld [hl], a
ld a, [wWhichPokemon]
ld c, a
ld b, FLAG_SET
call Evolution_FlagAction
; this is only called after battle
; it is supposed to do level up evolutions, though there is a bug that allows item evolutions to occur
EvolutionAfterBattle:
ldh a, [hTileAnimations]
push af
xor a
ld [wEvolutionOccurred], a
dec a
ld [wWhichPokemon], a
push hl
push bc
push de
ld hl, wPartyCount
push hl
Evolution_PartyMonLoop: ; loop over party mons
ld hl, wWhichPokemon
inc [hl]
pop hl
inc hl
ld a, [hl]
cp $ff ; have we reached the end of the party?
jp z, .done
ld [wEvoOldSpecies], a
push hl
ld a, [wWhichPokemon]
ld c, a
ld hl, wCanEvolveFlags
ld b, FLAG_TEST
call Evolution_FlagAction
ld a, c
and a ; is the mon's bit set?
jp z, Evolution_PartyMonLoop ; if not, go to the next mon
ld a, [wEvoOldSpecies]
dec a
ld b, 0
ld hl, EvosMovesPointerTable
add a
rl b
ld c, a
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
push hl
ld a, [wcf91]
push af
xor a ; PLAYER_PARTY_DATA
ld [wMonDataLocation], a
call LoadMonData
pop af
ld [wcf91], a
pop hl
.evoEntryLoop ; loop over evolution entries
ld a, [hli]
and a ; have we reached the end of the evolution data?
jr z, Evolution_PartyMonLoop
ld b, a ; evolution type
cp EV_TRADE
jr z, .checkTradeEvo
; not trade evolution
ld a, [wLinkState]
cp LINK_STATE_TRADING
jr z, Evolution_PartyMonLoop ; if trading, go the next mon
ld a, b
cp EV_ITEM
jr z, .checkItemEvo
ld a, [wForceEvolution]
and a
jr nz, Evolution_PartyMonLoop
ld a, b
cp EV_LEVEL
jr z, .checkLevel
.checkTradeEvo
ld a, [wLinkState]
cp LINK_STATE_TRADING
jp nz, .nextEvoEntry1 ; if not trading, go to the next evolution entry
ld a, [hli] ; level requirement
ld b, a
ld a, [wLoadedMonLevel]
cp b ; is the mon's level greater than the evolution requirement?
jp c, Evolution_PartyMonLoop ; if so, go the next mon
jr .doEvolution
.checkItemEvo
ld a, [hli]
ld b, a ; evolution item
ld a, [wcf91] ; this is supposed to be the last item used, but it is also used to hold species numbers
cp b ; was the evolution item in this entry used?
jp nz, .nextEvoEntry1 ; if not, go to the next evolution entry
.checkLevel
ld a, [hli] ; level requirement
ld b, a
ld a, [wLoadedMonLevel]
cp b ; is the mon's level greater than the evolution requirement?
jp c, .nextEvoEntry2 ; if so, go the next evolution entry
.doEvolution
ld [wCurEnemyLVL], a
ld a, 1
ld [wEvolutionOccurred], a
push hl
ld a, [hl]
ld [wEvoNewSpecies], a
ld a, [wWhichPokemon]
ld hl, wPartyMonNicks
call GetPartyMonName
call CopyToStringBuffer
ld hl, IsEvolvingText
call PrintText
ld c, 50
call DelayFrames
xor a
ldh [hAutoBGTransferEnabled], a
hlcoord 0, 0
lb bc, 12, 20
call ClearScreenArea
ld a, $1
ldh [hAutoBGTransferEnabled], a
ld a, $ff
ld [wUpdateSpritesEnabled], a
call ClearSprites
callfar EvolveMon
jp c, CancelledEvolution
ld hl, EvolvedText
call PrintText
pop hl
ld a, [hl]
ld [wd0b5], a
ld [wLoadedMonSpecies], a
ld [wEvoNewSpecies], a
ld a, MONSTER_NAME
ld [wNameListType], a
ld a, BANK(TrainerNames) ; bank is not used for monster names
ld [wPredefBank], a
call GetName
push hl
ld hl, IntoText
call PrintText_NoCreatingTextBox
ld a, SFX_GET_ITEM_2
call PlaySoundWaitForCurrent
call WaitForSoundToFinish
ld c, 40
call DelayFrames
call ClearScreen
call RenameEvolvedMon
ld a, [wd11e]
push af
ld a, [wd0b5]
ld [wd11e], a
predef IndexToPokedex
ld a, [wd11e]
dec a
ld hl, BaseStats
ld bc, BASE_DATA_SIZE
call AddNTimes
ld de, wMonHeader
call CopyData
ld a, [wd0b5]
ld [wMonHIndex], a
pop af
ld [wd11e], a
ld hl, wLoadedMonHPExp - 1
ld de, wLoadedMonStats
ld b, $1
call CalcStats
ld a, [wWhichPokemon]
ld hl, wPartyMon1
ld bc, wPartyMon2 - wPartyMon1
call AddNTimes
ld e, l
ld d, h
push hl
push bc
ld bc, wPartyMon1MaxHP - wPartyMon1
add hl, bc
ld a, [hli]
ld b, a
ld c, [hl]
ld hl, wLoadedMonMaxHP + 1
ld a, [hld]
sub c
ld c, a
ld a, [hl]
sbc b
ld b, a
ld hl, wLoadedMonHP + 1
ld a, [hl]
add c
ld [hld], a
ld a, [hl]
adc b
ld [hl], a
dec hl
pop bc
call CopyData
ld a, [wd0b5]
ld [wd11e], a
xor a
ld [wMonDataLocation], a
call LearnMoveFromLevelUp
pop hl
predef SetPartyMonTypes
ld a, [wIsInBattle]
and a
call z, Evolution_ReloadTilesetTilePatterns
predef IndexToPokedex
ld a, [wd11e]
dec a
ld c, a
ld b, FLAG_SET
ld hl, wPokedexOwned
push bc
call Evolution_FlagAction
pop bc
ld hl, wPokedexSeen
call Evolution_FlagAction
pop de
pop hl
ld a, [wLoadedMonSpecies]
ld [hl], a
push hl
ld l, e
ld h, d
jr .nextEvoEntry2
.nextEvoEntry1
inc hl
.nextEvoEntry2
inc hl
jp .evoEntryLoop
.done
pop de
pop bc
pop hl
pop af
ldh [hTileAnimations], a
ld a, [wLinkState]
cp LINK_STATE_TRADING
ret z
ld a, [wIsInBattle]
and a
ret nz
ld a, [wEvolutionOccurred]
and a
call nz, PlayDefaultMusic
ret
RenameEvolvedMon:
; Renames the mon to its new, evolved form's standard name unless it had a
; nickname, in which case the nickname is kept.
ld a, [wd0b5]
push af
ld a, [wMonHIndex]
ld [wd0b5], a
call GetName
pop af
ld [wd0b5], a
ld hl, wcd6d
ld de, wStringBuffer
.compareNamesLoop
ld a, [de]
inc de
cp [hl]
inc hl
ret nz
cp "@"
jr nz, .compareNamesLoop
ld a, [wWhichPokemon]
ld bc, NAME_LENGTH
ld hl, wPartyMonNicks
call AddNTimes
push hl
call GetName
ld hl, wcd6d
pop de
jp CopyData
CancelledEvolution:
ld hl, StoppedEvolvingText
call PrintText
call ClearScreen
pop hl
call Evolution_ReloadTilesetTilePatterns
jp Evolution_PartyMonLoop
EvolvedText:
text_far _EvolvedText
text_end
IntoText:
text_far _IntoText
text_end
StoppedEvolvingText:
text_far _StoppedEvolvingText
text_end
IsEvolvingText:
text_far _IsEvolvingText
text_end
Evolution_ReloadTilesetTilePatterns:
ld a, [wLinkState]
cp LINK_STATE_TRADING
ret z
jp ReloadTilesetTilePatterns
LearnMoveFromLevelUp:
ld hl, EvosMovesPointerTable
ld a, [wd11e] ; species
ld [wcf91], a
dec a
ld bc, 0
ld hl, EvosMovesPointerTable
add a
rl b
ld c, a
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
.skipEvolutionDataLoop ; loop to skip past the evolution data, which comes before the move data
ld a, [hli]
and a ; have we reached the end of the evolution data?
jr nz, .skipEvolutionDataLoop ; if not, jump back up
.learnSetLoop ; loop over the learn set until we reach a move that is learnt at the current level or the end of the list
ld a, [hli]
and a ; have we reached the end of the learn set?
jr z, .done ; if we've reached the end of the learn set, jump
ld b, a ; level the move is learnt at
ld a, [wCurEnemyLVL]
cp b ; is the move learnt at the mon's current level?
ld a, [hli] ; move ID
jr nz, .learnSetLoop
ld d, a ; ID of move to learn
ld a, [wMonDataLocation]
and a
jr nz, .next
; If [wMonDataLocation] is 0 (PLAYER_PARTY_DATA), get the address of the mon's
; current moves in party data. Every call to this function sets
; [wMonDataLocation] to 0 because other data locations are not supported.
; If it is not 0, this function will not work properly.
ld hl, wPartyMon1Moves
ld a, [wWhichPokemon]
ld bc, wPartyMon2 - wPartyMon1
call AddNTimes
.next
ld b, NUM_MOVES
.checkCurrentMovesLoop ; check if the move to learn is already known
ld a, [hli]
cp d
jr z, .done ; if already known, jump
dec b
jr nz, .checkCurrentMovesLoop
ld a, d
ld [wMoveNum], a
ld [wd11e], a
call GetMoveName
call CopyToStringBuffer
predef LearnMove
.done
ld a, [wcf91]
ld [wd11e], a
ret
; writes the moves a mon has at level [wCurEnemyLVL] to [de]
; move slots are being filled up sequentially and shifted if all slots are full
WriteMonMoves:
call GetPredefRegisters
push hl
push de
push bc
ld hl, EvosMovesPointerTable
ld b, 0
ld a, [wcf91] ; cur mon ID
dec a
add a
rl b
ld c, a
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
.skipEvoEntriesLoop
ld a, [hli]
and a
jr nz, .skipEvoEntriesLoop
jr .firstMove
.nextMove
pop de
.nextMove2
inc hl
.firstMove
ld a, [hli] ; read level of next move in learnset
and a
jp z, .done ; end of list
ld b, a
ld a, [wCurEnemyLVL]
cp b
jp c, .done ; mon level < move level (assumption: learnset is sorted by level)
ld a, [wLearningMovesFromDayCare]
and a
jr z, .skipMinLevelCheck
ld a, [wDayCareStartLevel]
cp b
jr nc, .nextMove2 ; min level >= move level
.skipMinLevelCheck
; check if the move is already known
push de
ld c, NUM_MOVES
.alreadyKnowsCheckLoop
ld a, [de]
inc de
cp [hl]
jr z, .nextMove
dec c
jr nz, .alreadyKnowsCheckLoop
; try to find an empty move slot
pop de
push de
ld c, NUM_MOVES
.findEmptySlotLoop
ld a, [de]
and a
jr z, .writeMoveToSlot2
inc de
dec c
jr nz, .findEmptySlotLoop
; no empty move slots found
pop de
push de
push hl
ld h, d
ld l, e
call WriteMonMoves_ShiftMoveData ; shift all moves one up (deleting move 1)
ld a, [wLearningMovesFromDayCare]
and a
jr z, .writeMoveToSlot
; shift PP as well if learning moves from day care
push de
ld bc, wPartyMon1PP - (wPartyMon1Moves + 3)
add hl, bc
ld d, h
ld e, l
call WriteMonMoves_ShiftMoveData ; shift all move PP data one up
pop de
.writeMoveToSlot
pop hl
.writeMoveToSlot2
ld a, [hl]
ld [de], a
ld a, [wLearningMovesFromDayCare]
and a
jr z, .nextMove
; write move PP value if learning moves from day care
push hl
ld a, [hl]
ld hl, wPartyMon1PP - wPartyMon1Moves
add hl, de
push hl
dec a
ld hl, Moves
ld bc, MOVE_LENGTH
call AddNTimes
ld de, wBuffer
ld a, BANK(Moves)
call FarCopyData
ld a, [wBuffer + 5]
pop hl
ld [hl], a
pop hl
jr .nextMove
.done
pop bc
pop de
pop hl
ret
; shifts all move data one up (freeing 4th move slot)
WriteMonMoves_ShiftMoveData:
ld c, NUM_MOVES - 1
.loop
inc de
ld a, [de]
ld [hli], a
dec c
jr nz, .loop
ret
Evolution_FlagAction:
predef_jump FlagActionPredef
; From here, Move Relearner-related code -PvK
;joenote - custom function by Mateo for move relearner
PrepareRelearnableMoveList:: ; I don't know how the fuck you're a single colon in shin pokered but it sure as shit doesn't work here - PvK
; Loads relearnable move list to wRelearnableMoves.
; Input: party mon index = [wWhichPokemon]
; Get mon id.
ld a, [wWhichPokemon]
ld c, a
ld b, 0
ld hl, wPartySpecies
add hl, bc
ld a, [hl] ; a = mon id
ld [wd0b5], a ;joenote - put mon id into wram for potential later usage of GetMonHeader
; Get pointer to evos moves data.
dec a
ld c, a
ld b, 0
ld hl, EvosMovesPointerTable
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a ; hl = pointer to evos moves data for our mon
push hl
; Get pointer to mon's currently-known moves.
ld a, [wWhichPokemon]
ld hl, wPartyMon1Level
ld bc, wPartyMon2 - wPartyMon1
call AddNTimes
ld a, [hl]
ld b, a
push bc
ld a, [wWhichPokemon]
ld hl, wPartyMon1Moves
ld bc, wPartyMon2 - wPartyMon1
call AddNTimes
pop bc
ld d, h
ld e, l
pop hl
; Skip over evolution data.
.skipEvoEntriesLoop
ld a, [hli]
and a
jr nz, .skipEvoEntriesLoop
; Write list of relearnable moves, while keeping count along the way.
; de = pointer to mon's currently-known moves
; hl = pointer to moves data for our mon
; b = mon's level
ld c, 0 ; c = count of relearnable moves
.loop
ld a, [hli]
and a
jr z, .done
cp b
jr c, .addMove
jr nz, .done
.addMove
push bc
ld a, [hli] ; move id
ld b, a
; Check if move is already known by our mon.
push de
ld a, [de]
cp b
jr z, .knowsMove
inc de
ld a, [de]
cp b
jr z, .knowsMove
inc de
ld a, [de]
cp b
jr z, .knowsMove
inc de
ld a, [de]
cp b
jr z, .knowsMove
.relearnableMove
pop de
push hl
; Add move to the list, and update the running count.
ld a, b
ld b, 0
ld hl, wMoveBuffer + 1
add hl, bc
ld [hl], a
pop hl
pop bc
inc c
jr .loop
.knowsMove
pop de
pop bc
jr .loop
.done
;joenote - start checking for level-0 moves
xor a
ld b, a ;b will act as a counter, as there can only be up to 4 level-0 moves
call GetMonHeader ;mon id already stored earlier in wd0b5
ld hl, wMonHMoves
.loop2
ld a, b ;get the current loop counter into a
cp $4
jr nc, .done2 ;if gone through 4 moves already, reached the end of the list. move to done2.
ld a, [hl] ;load move
and a
jr z, .done2 ;if move has id 0, list has reached the end early. move to done2.
;check if the move is already in the learnable move list
push bc
push hl
;c = buffer length
.buffer_loop
ld hl, wMoveBuffer
ld b, 0
add hl, bc ;move to buffer at current c value
ld b, a ;b = move id
ld a, [hl] ; move id at buffer point
cp b
ld a, b ;a = move id
jr z, .move_in_buffer
inc c
dec c
jr z, .end_buffer_loop ;jump out if start of buffer is reached
dec c ;else decrement c and loop again
jr .buffer_loop
.move_in_buffer
pop hl
pop bc
inc hl ;increment to the next level-0 move
inc b ;increment the loop counter
jr .loop2
.end_buffer_loop
pop hl
pop bc
;Check if move is already known by our mon.
push bc
ld a, [hl] ; move id
ld b, a
push de
ld a, [de]
cp b
jr z, .knowsMove2
inc de
ld a, [de]
cp b
jr z, .knowsMove2
inc de
ld a, [de]
cp b
jr z, .knowsMove2
inc de
ld a, [de]
cp b
jr z, .knowsMove2
;if the move is not already known, add it to the learnable move list
pop de
push hl
; Add move to the list, and update the running count.
ld a, b
ld b, 0
ld hl, wMoveBuffer + 1
add hl, bc
ld [hl], a
pop hl
pop bc
inc c
inc hl ;increment to the next level-0 move
inc b ;increment the loop counter
jr .loop2
.knowsMove2
pop de
pop bc
inc hl ;increment to the next level-0 move
inc b ;increment the loop counter
jr .loop2
.done2
ld b, 0
ld hl, wMoveBuffer + 1
add hl, bc
ld a, $ff
ld [hl], a
ld hl, wMoveBuffer
ld [hl], c
ret
; Modified Mateo/jojobear13 code for a Tradeback Move Tutor
PrepareTradebackMoveList:: ; I still don't know how the fuck you're a single colon in shin pokered but it sure as shit doesn't work here - PvK
; Loads Tradeback move list to wRelearnableMoves - we can reuse this variable.
; Input: party mon index = [wWhichPokemon]
; Get mon id.
ld a, [wWhichPokemon]
ld c, a
ld b, 0
ld hl, wPartySpecies
add hl, bc
ld a, [hl] ; a = mon id
dec a
ld [wd0b5], a ;joenote - put mon id into wram for potential later usage of GetMonHeader
; Get pointer to evos moves data.
dec a
ld c, a
ld b, 0
ld hl, TradebackMovesPointerTable
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a ; hl = pointer to evos moves data for our mon
push hl
; Get pointer to mon's currently-known moves.
ld a, [wWhichPokemon]
ld hl, wPartyMon1Level
ld bc, wPartyMon2 - wPartyMon1
call AddNTimes
ld a, [hl]
ld b, a
push bc
ld a, [wWhichPokemon]
ld hl, wPartyMon1Moves
ld bc, wPartyMon2 - wPartyMon1
call AddNTimes
pop bc
ld d, h
ld e, l
pop hl
; Skip over evolution data.
.skipEvoEntriesLoop
ld a, [hli]
and a
jr nz, .skipEvoEntriesLoop
; Write list of relearnable moves, while keeping count along the way.
; de = pointer to mon's currently-known moves
; hl = pointer to moves data for our mon
; b = mon's level
ld c, 0 ; c = count of relearnable moves
.loop
ld a, [hli]
and a
jr z, .done
cp b
jr c, .addMove
jr nz, .done
.addMove
push bc
ld a, [hli] ; move id
ld b, a
; Check if move is already known by our mon.
push de
ld a, [de]
cp b
jr z, .knowsMove
inc de
ld a, [de]
cp b
jr z, .knowsMove
inc de
ld a, [de]
cp b
jr z, .knowsMove
inc de
ld a, [de]
cp b
jr z, .knowsMove
.tradebackMove
pop de
push hl
; Add move to the list, and update the running count.
ld a, b
ld b, 0
ld hl, wMoveBuffer + 1
add hl, bc
ld [hl], a
pop hl
pop bc
inc c
jr .loop
.knowsMove
pop de
pop bc
jr .loop
.done
ld b, 0
ld hl, wMoveBuffer + 1
add hl, bc
ld a, $ff
ld [hl], a
ld hl, wMoveBuffer
ld [hl], c
ret
INCLUDE "data/pokemon/evos_moves.asm"
INCLUDE "data/pokemon/tradeback_moves.asm"