Skip to content

Commit

Permalink
lufia2ac: ability to swap party members mid-run and option to gain EX…
Browse files Browse the repository at this point in the history
…P while inactive (ArchipelagoMW#2800)
  • Loading branch information
el-u authored and qwint committed Jun 24, 2024
1 parent c2502d5 commit 9d93c26
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 7 deletions.
17 changes: 16 additions & 1 deletion worlds/lufia2ac/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,20 @@ class HealingFloorChance(Range):
default = 16


class InactiveExpGain(Choice):
"""The rate at which characters not currently in the active party gain EXP.
Supported values: disabled, half, full
Default value: disabled (same as in an unmodified game)
"""

display_name = "Inactive character EXP gain"
option_disabled = 0
option_half = 50
option_full = 100
default = option_disabled


class InitialFloor(Range):
"""The initial floor, where you begin your journey.
Expand Down Expand Up @@ -805,7 +819,7 @@ class ShufflePartyMembers(Toggle):
false — all 6 optional party members are present in the cafe and can be recruited right away
true — only Maxim is available from the start; 6 new "items" are added to your pool and shuffled into the
multiworld; when one of these items is found, the corresponding party member is unlocked for you to use.
While cave diving, you can add newly unlocked ones to your party by using the character items from the inventory
While cave diving, you can add or remove unlocked party members by using the character items from the inventory
Default value: false (same as in an unmodified game)
"""

Expand Down Expand Up @@ -838,6 +852,7 @@ class L2ACOptions(PerGameCommonOptions):
goal: Goal
gold_modifier: GoldModifier
healing_floor_chance: HealingFloorChance
inactive_exp_gain: InactiveExpGain
initial_floor: InitialFloor
iris_floor_chance: IrisFloorChance
iris_treasures_required: IrisTreasuresRequired
Expand Down
1 change: 1 addition & 0 deletions worlds/lufia2ac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def generate_output(self, output_directory: str) -> None:
rom_bytearray[0x280018:0x280018 + 1] = self.o.shuffle_party_members.unlock.to_bytes(1, "little")
rom_bytearray[0x280019:0x280019 + 1] = self.o.shuffle_capsule_monsters.unlock.to_bytes(1, "little")
rom_bytearray[0x28001A:0x28001A + 1] = self.o.shop_interval.value.to_bytes(1, "little")
rom_bytearray[0x28001B:0x28001B + 1] = self.o.inactive_exp_gain.value.to_bytes(1, "little")
rom_bytearray[0x280030:0x280030 + 1] = self.o.goal.value.to_bytes(1, "little")
rom_bytearray[0x28003D:0x28003D + 1] = self.o.death_link.value.to_bytes(1, "little")
rom_bytearray[0x281200:0x281200 + 470] = self.get_capsule_cravings_table()
Expand Down
85 changes: 81 additions & 4 deletions worlds/lufia2ac/basepatch/basepatch.asm
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@ org $8EFD2E ; unused region at the end of bank $8E
DB $1E,$0B,$01,$2B,$05,$1A,$05,$00 ; add dekar
DB $1E,$0B,$01,$2B,$04,$1A,$06,$00 ; add tia
DB $1E,$0B,$01,$2B,$06,$1A,$07,$00 ; add lexis
DB $1F,$0B,$01,$2C,$01,$1B,$02,$00 ; remove selan
DB $1F,$0B,$01,$2C,$02,$1B,$03,$00 ; remove guy
DB $1F,$0B,$01,$2C,$03,$1B,$04,$00 ; remove arty
DB $1F,$0B,$01,$2C,$05,$1B,$05,$00 ; remove dekar
DB $1F,$0B,$01,$2C,$04,$1B,$06,$00 ; remove tia
DB $1F,$0B,$01,$2C,$06,$1B,$07,$00 ; remove lexis
pullpc

SpecialItemUse:
Expand All @@ -328,19 +334,22 @@ SpecialItemUse:
SEP #$20
LDA $8ED8C7,X ; load predefined bitmask with a single bit set
BIT $077E ; check against EV flags $02 to $07 (party member flags)
BNE + ; abort if character already present
LDA $07A9 ; load EV register $11 (party counter)
BEQ ++
LDA.b #$30 ; character already present; modify pointer to point to L2SASM leave script
ADC $09B7
STA $09B7
BRA +++
++: LDA $07A9 ; character not present; load EV register $0B (party counter)
CMP.b #$03
BPL + ; abort if party full
LDA.b #$8E
+++ LDA.b #$8E
STA $09B9
PHK
PEA ++
PEA $8DD8
JML $83BB76 ; initialize parser variables
++: NOP
JSL $809CB8 ; call L2SASM parser
JSL $81F034 ; consume the item
TSX
INX #13
TXS
Expand Down Expand Up @@ -490,6 +499,73 @@ pullpc



; allow inactive characters to gain exp
pushpc
org $81DADD
; DB=$81, x=0, m=1
NOP ; overwrites BNE $81DAE2 : JMP $DBED
JML HandleActiveExp
AwardExp:
; isolate exp distribution into a subroutine, to be reused for both active party members and inactive characters
org $81DAE9
NOP #2 ; overwrites JMP $DBBD
RTL
org $81DB42
NOP #2 ; overwrites JMP $DBBD
RTL
org $81DD11
; DB=$81, x=0, m=1
JSL HandleInactiveExp ; overwrites LDA $0A8A : CLC
pullpc

HandleActiveExp:
BNE + ; (overwritten instruction; modified) check if statblock not empty
JML $81DBED ; (overwritten instruction; modified) abort
+: JSL AwardExp ; award exp (X=statblock pointer, Y=position in battle order, $00=position in menu order)
JML $81DBBD ; (overwritten instruction; modified) continue to next level text

HandleInactiveExp:
LDA $F0201B ; load inactive exp gain rate
BEQ + ; zero gain; skip everything
CMP.b #$64
BCS ++ ; full gain
LSR $1607
ROR $1606 ; half gain
ROR $1605
++: LDY.w #$0000 ; start looping through all characters
-: TDC
TYA
LDX.w #$0003 ; start looping through active party
--: CMP $0A7B,X
BEQ ++ ; skip if character in active party
DEX
BPL -- ; continue looping through active party
STA $153D ; inactive character detected; overwrite character index of 1st slot in party battle order
ASL
TAX
REP #$20
LDA $859EBA,X ; convert character index to statblock pointer
SEP #$20
TAX
PHY ; stash character loop index
LDY $0A80
PHY ; stash 1st (in menu order) party member statblock pointer
STX $0A80 ; overwrite 1st (in menu order) party member statblock pointer
LDY.w #$0000 ; set to use 1st position (in battle order)
STY $00 ; set to use 1st position (in menu order)
JSL AwardExp ; award exp (X=statblock pointer, Y=position in battle order, $00=position in menu order)
PLY ; restore 1st (in menu order) party member statblock pointer
STY $0A80
PLY ; restore character loop index
++: INY
CPY.w #$0007
BCC - ; continue looping through all characters
+: LDA $0A8A ; (overwritten instruction) load current gold
CLC ; (overwritten instruction)
RTL



; receive death link
pushpc
org $83BC91
Expand Down Expand Up @@ -1226,6 +1302,7 @@ pullpc
; $F02018 1 party members available
; $F02019 1 capsule monsters available
; $F0201A 1 shop interval
; $F0201B 1 inactive exp gain rate
; $F02030 1 selected goal
; $F02031 1 goal completion: boss
; $F02032 1 goal completion: iris_treasure_hunt
Expand Down
Binary file modified worlds/lufia2ac/basepatch/basepatch.bsdiff4
Binary file not shown.
5 changes: 3 additions & 2 deletions worlds/lufia2ac/docs/en_Lufia II Ancient Cave.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ Your Party Leader will hold up the item they received when not in a fight or in
- Randomize enemy movement patterns, enemy sprites, and which enemy types can appear at which floor numbers
- Option to make shops appear in the cave so that you have a way to spend your hard-earned gold
- Option to shuffle your party members and/or capsule monsters into the multiworld, meaning that someone will have to
find them in order to unlock them for you to use. While cave diving, you can add newly unlocked members to your party
by using the character items from your inventory
find them in order to unlock them for you to use. While cave diving, you can add or remove unlocked party members by
using the character items from your inventory. There's also an option to allow inactive characters to gain some EXP,
so that new party members added during a run don't have to start off at a low level

###### Quality of life:

Expand Down

0 comments on commit 9d93c26

Please sign in to comment.