Togglable Infinite Repel - pret/pokecrystal GitHub Wiki
Inspired by Radical Red's Infinite Repel system, I decided to try and implement it in Crystal.
Contents
- Change Repel to a Key Item
- Modify Repel Functions
- Addendum: Make Repel an option in the options menu
1. Change Repel to a Key Item
First make the proper edits to the base Repel item in data/items/attributes.asm, as well as data/items/descriptions.asm.
ItemAttributes:
; entries correspond to item ids (see constants/item_constants.asm)
table_width ITEMATTR_STRUCT_LENGTH, ItemAttributes
...
; REPEL
- item_attribute 350, HELD_NONE, 0, CANT_SELECT, ITEM, ITEMMENU_CURRENT, ITEMMENU_NOUSE
+ item_attribute 0, HELD_NONE, 0, CANT_SELECT | CANT_TOSS, KEY_ITEM, ITEMMENU_CURRENT, ITEMMENU_NOUSE
...
assert_table_length $100
RepelDesc:
db "Repels weak #-"
- next "MON for 100 steps.@"
+ next "MON.@"
2. Modify Repel Functions
Now that the Repel is properly a Key Item, we'll need to modify its behavior. First let's open engine/items/item_effects.asm and remove Super Repel and Max Repel effects.
ItemEffects:
; entries correspond to item ids (see constants/item_constants.asm)
table_width 2, ItemEffects
dw GuardSpecEffect ; GUARD_SPEC
- dw SuperRepelEffect ; SUPER_REPEL
- dw MaxRepelEffect ; MAX_REPEL
+ dw NoEffect ; SUPER_REPEL
+ dw NoEffect ; MAX_REPEL
dw DireHitEffect ; DIRE_HIT
dw NoEffect ; ITEM_2D
...
assert_table_length ITEM_B3
-SuperRepelEffect:
- ld b, 200
- jr UseRepel
- MaxRepelEffect:
- ld b, 250
- jr UseRepel
Now we'll change up RepelEffect
function a bit.
RepelEffect:
- ld b, 100
-UseRepel:
+ ld b, 1
ld a, [wRepelEffect]
and a
- ld hl, RepelUsedEarlierIsStillInEffectText
- jp nz, PrintText
-
+ jr nz, .RepelisOn
ld a, b
ld [wRepelEffect], a
- jp UseItemText
+ ld hl, RepelTurnOnText
+ jp PrintText
-RepelUsedEarlierIsStillInEffectText:
- text_far _RepelUsedEarlierIsStillInEffectText
+.RepelisOn
+ xor a
+ ld [wRepelEffect], a
+ ld hl, RepelTurnOffText
+ jp PrintText
+
+RepelTurnOffText:
+ text_far _RepelTurnOffText
+ text_end
+
+RepelTurnOnText:
+ text_far _RepelTurnOnText
text_end
The repel items used to load the number of steps in register b
and check if there's a repel active by checking wRepelEffect
; if it was zero then it would load the value of b
into it, if not then it would print a text indicating a repel is still in effect. What we did here is rework it so that we only need to load either 0
or 1
into wRepelEffect
to indicate whether it's active or not and print the appropriate message.
Finally, go to data/text/common_3.asm edit a proper message:
-_RepelUsedEarlierIsStillInEffectText::
- text "The REPEL used"
- line "earlier is still"
- cont "in effect."
+_RepelTurnOffText::
+ text "The REPEL is off."
prompt
+
+_RepelTurnOnText::
+ text "The REPEL is on."
+ prompt
Now the issue is that the function for subtracting steps from Repel still exists, so next we'll head over to engine/overworld/events.asm and make these edits:
CountStep:
; Don't count steps in link communication rooms.
ld a, [wLinkMode]
and a
jr nz, .done
farcall CheckSpecialPhoneCall
jr c, .doscript
- ; If Repel wore off, don't count the step.
- call DoRepelStep
- jr c, .doscript
-
; Count the step for poison and total steps
ld hl, wPoisonStepCount
inc [hl]
...
-DoRepelStep:
- ld a, [wRepelEffect]
- and a
- ret z
-
- dec a
- ld [wRepelEffect], a
- ret nz
-
- ld a, BANK(RepelWoreOffScript)
- ld hl, RepelWoreOffScript
- call CallScript
- scf
- ret
-
Finally we can delete engine/events/repel.asm and remove it from main.asm:
SECTION "bank4", ROMX
...
INCLUDE "engine/events/elevator.asm"
INCLUDE "engine/events/bug_contest/contest.asm"
-INCLUDE "engine/events/repel.asm"
INCLUDE "engine/events/hidden_item.asm"
INCLUDE "engine/events/std_collision.asm"
...
That's it! Its now togglable from the Items menu.
3. Addendum: Make Repel an Option in the Options Menu
As a fun extra, I also figured out how to simply add an infinite repel to the options menu. This will remove the often unused Printer function from the options menu.
First we'll add a new constant in constants/wram_constants.asm:
; wOptions2::
const_def
const MENU_ACCOUNT ; 0
+ const REPEL_OPTION ; 1
Also we'll add a comment in ram/wram.asm indicating what bit our new constant represents:
wOptions2::
; bit 1: menu account off/on
+; bit 2: repel off/on
db
ds 2
wOptionsEnd::
wOptions2
is a byte that holds the on/off bits for options. Notice its named 2, this is because there are 2 bytes for options, and wOptions
is full. wOptions2
, however, only holds the bit for Menu Account, so we can purpose the next bit for Repel.
Next we'll modify engine/menus/options_menu.asm.
; GetOptionPointer.Pointers indexes
const_def
const OPT_BATTLE_SCENE ; 1
const OPT_BATTLE_STYLE ; 2
const OPT_SOUND ; 3
- const OPT_PRINT ; 4
+ const OPT_REPEL ; 4
const OPT_MENU_ACCOUNT ; 5
const OPT_FRAME ; 6
const OPT_CANCEL ; 7
DEF NUM_OPTIONS EQU const_value ; 8
StringOptions:
...
db "SOUND<LF>"
db " :<LF>"
- db "PRINT<LF>"
+ db "REPEL<LF>"
db " :<LF>"
db "MENU ACCOUNT<LF>"
db " :<LF>"
...
GetOptionPointer:
jumptable .Pointers, wJumptableIndex
.Pointers:
; entries correspond to OPT_* constants
dw Options_TextSpeed
dw Options_BattleScene
dw Options_BattleStyle
dw Options_Sound
- dw Options_Print
+ dw Options_Repel
dw Options_MenuAccount
dw Options_Frame
dw Options_Cancel
Next copy the below function underneath Options_MenuAccount
.
Options_Repel:
ld hl, wOptions2
ldh a, [hJoyPressed]
bit D_LEFT_F, a
jr nz, .LeftPressed
bit D_RIGHT_F, a
jr z, .NonePressed
bit REPEL_OPTION, [hl]
jr nz, .ToggleOff
jr .ToggleOn
.LeftPressed:
bit REPEL_OPTION, [hl]
jr z, .ToggleOn
jr .ToggleOff
.NonePressed:
bit REPEL_OPTION, [hl]
jr nz, .ToggleOn
.ToggleOff:
res REPEL_OPTION, [hl]
ld de, .Off
jr .Display
.ToggleOn:
set REPEL_OPTION, [hl]
ld de, .On
.Display:
hlcoord 11, 11
call PlaceString
and a
ret
.Off: db "OFF@"
.On: db "ON @"
This is a copy of Options_MenuAccount
but modified to toggle the Repel bit we added earlier.
Next we need to have the Repel function that is called when we actually do the Repel effect check for this bit, instead of wRepelEffect
. Let's edit engine/overworld/wildmons.asm
CheckRepelEffect::
-; If there is no active Repel, there's no need to be here.
- ld a, [wRepelEffect]
- and a
+; Repel option is set to off, there's no need to be here.
+ ld a, [wOptions2]
+ and 1 << REPEL_OPTION
jr z, .encounter
; Get the first Pokemon in your party that isn't fainted.
ld hl, wPartyMon1HP
And that's it! Now we'll have the option in the Menu to toggle Repel's effect on or off. I would recommend doing one or the other, simply because of redundancy. Also as a bit of clean up, if you go with the Repel in Options Menu, you can now safely remove the Options_Print
function from engine/menus/options_menu.asm, as well as repurpose wRepelEffect
for something else as it'll be unused.