kep-hack/scripts/VermilionCity.asm
Llinos Evans 72a372dcde Groundwork for new Vermilion Pass system
Alright so this is the start of producing a new and improved pass system - one that actually works.

Long story short: Now there are three dock entrances. One for the S.S. Anne, one for Citrine City, and one for Faraway Island. I don't like it, but the old system literally just would not work, and with the time span in mind, I just want to use this instead.

Here's how it works;
- Usual S.S. Anne trigger is used, but with a new coordinate range.
- When triggered, check the coordinates the player is in; if it's not the usual S.S. Anne one, they're getting kicked to a new script.
- Use that to flag the player as usual.

Glitches:
- Triggering these multiple times causes strange jank, including crashes. I have the player forced into the new maps for now, but even going back can cause issues. I don't understand it.
- Citrine City's warp just...doesn't work, presumably due to a warp limit. However, after chopping off the warps so there's one each, it still happens.

These glitches need fixing before KEP can launch.
2023-08-19 22:11:12 +01:00

830 lines
19 KiB
NASM

VermilionCity_Script:
call EnableAutoTextBoxDrawing
ld hl, wCurrentMapScriptFlags
bit 6, [hl]
res 6, [hl]
push hl
call nz, .initCityScript
pop hl
bit 5, [hl]
res 5, [hl]
call nz, .setFirstLockTrashCanIndex
ld hl, VermilionCity_ScriptPointers
ld a, [wVermilionCityCurScript]
jp CallFunctionInTable
.setFirstLockTrashCanIndex
call Random
ldh a, [hRandomSub]
and $e
ld [wFirstLockTrashCanIndex], a
ret
.initCityScript
CheckEventHL EVENT_SS_ANNE_LEFT
ret z
CheckEventReuseHL EVENT_WALKED_PAST_GUARD_AFTER_SS_ANNE_LEFT
SetEventReuseHL EVENT_WALKED_PAST_GUARD_AFTER_SS_ANNE_LEFT
ret nz
ld a, $2
ld [wVermilionCityCurScript], a
ret
VermilionCity_ScriptPointers:
dw VermilionCityScript0
dw VermilionCityScript1
dw VermilionCityScript2
dw VermilionCityScript3
dw VermilionCityScript4
VermilionCityScript0:
ld a, [wSpritePlayerStateData1FacingDirection]
and a ; cp SPRITE_FACING_DOWN
ret nz
ld hl, SSAnneTicketCheckCoords
call ArePlayerCoordsInArray
ret nc
xor a
ldh [hJoyHeld], a
ld [wcf0d], a
ld a, $3
ldh [hSpriteIndexOrTextID], a
call DisplayTextID
ld a, [wObtainedBadges] ; ship returns after obtaining the marsh badge
bit 5, a
jr nz, .default
CheckEvent EVENT_SS_ANNE_LEFT
jr nz, .shipHasDeparted
.default
ld hl, OldSeaMapCheckCoords
call ArePlayerCoordsInArray
jp c, MovePlayerVermilion
ld hl, CitrineCityCheckCoords
call ArePlayerCoordsInArray
jp c, MovePlayerVermilion
ld b, S_S_TICKET
predef GetQuantityOfItemInBag
ld a, b
and a
ret nz
.shipHasDeparted
ld a, D_UP
ld [wSimulatedJoypadStatesEnd], a
ld a, $1
ld [wSimulatedJoypadStatesIndex], a
call StartSimulatingJoypadStates
ld a, $1
ld [wVermilionCityCurScript], a
ret
SSAnneTicketCheckCoords: ; Mother coords - use to universally trigger the same event
dbmapcoord 18, 30 ; S. S. Anne
dbmapcoord 10, 30 ; Faraway Island
dbmapcoord 26, 30 ; Citrine City
db -1 ; end
; I know it looks awful but I use the individual ones to launch off the new ticket script.
; We don't need an S.S. Anne one, as I'm using a process of elimination check system.
OldSeaMapCheckCoords:
dbmapcoord 10, 30
db -1
CitrineCityCheckCoords:
dbmapcoord 26, 30
db -1
VermilionCityScript4:
ld hl, SSAnneTicketCheckCoords
call ArePlayerCoordsInArray
ret c
ld a, $0
ld [wVermilionCityCurScript], a
ret
VermilionCityScript2:
ld a, $ff
ld [wJoyIgnore], a
ld a, D_UP
ld [wSimulatedJoypadStatesEnd], a
ld [wSimulatedJoypadStatesEnd + 1], a
ld a, 2
ld [wSimulatedJoypadStatesIndex], a
call StartSimulatingJoypadStates
ld a, $3
ld [wVermilionCityCurScript], a
ret
VermilionCityScript3:
ld a, [wSimulatedJoypadStatesIndex]
and a
ret nz
xor a
ld [wJoyIgnore], a
ldh [hJoyHeld], a
ld a, $0
ld [wVermilionCityCurScript], a
ret
VermilionCityScript1:
ld a, [wSimulatedJoypadStatesIndex]
and a
ret nz
ld c, 10
call DelayFrames
ld a, $0
ld [wVermilionCityCurScript], a
ret
; We call these four commands to stop text from autoskipping 4 times, may as well just make this.
TheAutoskipStopinator:
ld a, [wSimulatedJoypadStatesEnd] ; ensuring that the text doesn't autoskip.
and a ; yep, here too.
call z, WaitForTextScrollButtonPress ; and here.
call EnableAutoTextBoxDrawing ; and here.
ret
; So get this: If you check multiple tickets, there's a really janky crash.
; I can't be fucked fixing it, so let's force the player through the warp instead.
; Yes, I'm serious.
MovePlayerVermilion:
ld a, PLAYER_DIR_DOWN
ld [wPlayerMovingDirection], a
call UpdateSprites
ld a, $ff ; Firstly...
ld [wJoyIgnore], a ; No joypad inputs. No funny business. RLE is weird as fuck without it.
ld hl, wSimulatedJoypadStatesEnd
ld de, WalkIntoWarp_RLEMovement
call DecodeRLEList
dec a
ld [wSimulatedJoypadStatesIndex], a
call StartSimulatingJoypadStates ; By this point, we're auto-moving.
ld a, $0
ld [wVermilionCityCurScript], a ; safety
ret
WalkIntoWarp_RLEMovement:
db D_DOWN, 2
db -1 ; end
; ================================================================
; THIS IS NO LONGER USED, KEPT IN CASE A BETTER WARP SYSTEM COMES UP.
; Alright, so I'll explain the issue.
; For some reason, all the warp methods that exist do not seem to like this system.
; Faraway Island would load but have botched collision and break when you leave.
; Citrine City would outright crash.
; Bank movements and stuff were tried but nothing seemed to work as desired.
; Instead, we use a triple-harbour method, which makes the city look more lively anyway.
;VermillionCityScript_GetPassesInBag:
; Gets a list of Passes in the player's bag.
; Ripped and modified from the fossil guy
; xor a
; ld [wFilteredBagItemsCount], a
; ld de, wFilteredBagItems
; ld hl, PassList
;.loop
; ld a, [hli]
; and a
; jr z, .done
; push hl
; push de
; ld [wd11e], a
; ld b, a
; predef GetQuantityOfItemInBag
; pop de
; pop hl
; ld a, b
; and a
; jr z, .loop
; ; A Pass is in the bag
; ld a, [wd11e]
; ld [de], a
; inc de
; push hl
; ld hl, wFilteredBagItemsCount
; inc [hl]
; pop hl
; jr .loop
;.done
; ld a, $ff
; ld [de], a
; ret
;PassList:
; db S_S_TICKET
; db OLD_SEA_MAP
; db CITRINE_PASS
; ;db RAINBOW_PASS I planned this for a while, but the space wasn't there, and the scope was strange.
; db 0 ; end
;PrintPassesInBag:
; Prints each pass in the player's bag on a separate line in the menu.
; ld hl, wFilteredBagItems
; xor a
; ldh [hItemCounter], a
;.loop
; ld a, [hli]
; cp $ff
; ret z
; push hl
; ld [wd11e], a
; call GetItemName
; hlcoord 2, 2
; ldh a, [hItemCounter]
; ld bc, SCREEN_WIDTH * 2
; call AddNTimes
; ld de, wcd6d
; call PlaceString
; ld hl, hItemCounter
; inc [hl]
; pop hl
; jr .loop
;PassListings:
; ld hl, VermillionCityPassSelectionText
; call PrintText
; call VermillionCityScript_GetPassesInBag
; ld hl, wd730
; set 6, [hl]
; xor a
; ld [wCurrentMenuItem], a
; ld a, A_BUTTON | B_BUTTON
; ld [wMenuWatchedKeys], a
; ld a, [wFilteredBagItemsCount]
; dec a
; ld [wMaxMenuItem], a
; ld a, 2
; ld [wTopMenuItemY], a
; ld a, 1
; ld [wTopMenuItemX], a
; ld a, [wFilteredBagItemsCount]
; dec a
; ld bc, 2
; ld hl, 3
; call AddNTimes
; dec l
; ld b, l
; ld c, $d
; hlcoord 0, 0
; call TextBoxBorder
; call UpdateSprites
; call PrintPassesInBag
; ld hl, wd730
; res 6, [hl]
; call HandleMenuInput
; bit BIT_B_BUTTON, a
; jr nz, .cancelledPass
; ld hl, wFilteredBagItems
; ld a, [wCurrentMenuItem]
; ld d, 0
; ld e, a
; add hl, de
; ld a, [hl]
; ldh [hItemToRemoveID], a
; cp S_S_TICKET
; jp z, VermilionCityText3.playerHasTicket ; Saves time and less risk of bugs
; cp OLD_SEA_MAP
; jr z, .choseFaraway
; cp CITRINE_PASS
; jr z, .choseCitrine
; god bless the safari game and pokemon tower 7f for being the few times a forcewarp exists
; For some reason, these aren't working properly...
;.choseFaraway
; ld hl, EventVermillionCityOldSeaMap
; call PrintText
; ld a, FARAWAY_ISLAND_OUTSIDE
; ld [wDestinationMap], a
; ld hl, wd732
; set 2, [hl] ; fly warp or dungeon warp
; call SpecialWarpIn
; jp SpecialEnterMap
;.choseCitrine
; ld hl, EventVermillionCityCitrinePass
; call PrintText
; ld a, CITRINE_CITY
; ldh [hWarpDestinationMap], a
; ld a, $6
; ld [wDestinationWarpID], a
; call WarpFound2
; jr .done
;.cancelledPass
; ld hl, PassRefuse
; call PrintText
;.done
; ret
;
; ================================================================
VermilionCity_TextPointers:
dw VermilionCityText1
dw VermilionCityText2
dw VermilionCityText3
dw VermilionCityText4
dw VermilionCityText5
dw VermilionCityText6
dw OfficerJennySquirtle
dw VermilionBeauty
dw EventVermillionCityOldSeaMapGreeting
dw EventVermillionCityCitrinePassGreeting
dw VermilionCityText7
dw VermilionCityText8
dw MartSignText
dw PokeCenterSignText
dw VermilionCityText11
dw VermilionCityText12
dw VermilionCityText13
VermilionCityText1:
text_far _VermilionCityText1
text_end
VermilionCityText2:
text_asm
CheckEvent EVENT_SS_ANNE_LEFT
jr nz, .shipHasDeparted
ld hl, VermilionCityTextDidYouSee
call PrintText
jr .end
.shipHasDeparted
ld hl, VermilionCityTextSSAnneDeparted
call PrintText
.end
jp TextScriptEnd
VermilionCityTextDidYouSee:
text_far _VermilionCityTextDidYouSee
text_end
VermilionCityTextSSAnneDeparted:
text_far _VermilionCityTextSSAnneDeparted
text_end
VermilionCityText3:
text_asm
; Checking the location that the script flagged earlier.
; Instead of using an item checker, this is more effective and easy on memory.
; Also, it doesn't get cross-threaded if you have multiples of the same thing. I had issues working around that one - I ain't a programmer by trade, unfortunately, I'm a TEFL student.
; Anyway, the code kicks you to different versions of the same script; I've had them isolated to call text in a way that's easier architecturally. Otherwise, we're checking the item found every time, and I'm 90% sure that nicks a WRAM entry.
; All in all: Yes, this is suboptimal, but I've been doing this for months. Sue me.
ld hl, OldSeaMapCheckCoords
call ArePlayerCoordsInArray
jr c, HasOldSeaMap
ld hl, CitrineCityCheckCoords
call ArePlayerCoordsInArray
jp c, HasCitrinePass
; If you have neither, this will continue as normal, see?
; This is why we needed the child coords.
; Before the coord system, we used this with the unused pass listing code.
; See where the issues arose? Possibly a neat exercise!
;ld b, OLD_SEA_MAP
;predef GetQuantityOfItemInBag
;ld a, b
;and a
;jp nz, PassListings
;ld b, CITRINE_PASS
;predef GetQuantityOfItemInBag
;ld a, b
;and a
;jp nz, PassListings
CheckEvent EVENT_SS_ANNE_LEFT
jr nz, .shipHasDeparted
ld a, [wSpritePlayerStateData1FacingDirection]
cp SPRITE_FACING_RIGHT
jr z, .greetPlayer
ld hl, .inFrontOfOrBehindGuardCoords
call ArePlayerCoordsInArray
jr nc, .greetPlayerAndCheckTicket
.greetPlayer
ld hl, SSAnneWelcomeText4
call PrintText
jr .end
.greetPlayerAndCheckTicket
ld hl, SSAnneWelcomeText9
call PrintText
call TheAutoskipStopinator ; this actually fixes a bug from vanilla...
ld b, S_S_TICKET
predef GetQuantityOfItemInBag
ld a, b
and a
jr nz, .playerHasTicket
ld hl, SSAnneNoTicketText
call PrintText
jr .end
.playerHasTicket
ld hl, SSAnneFlashedTicketText
call PrintText
ld a, $4
ld [wVermilionCityCurScript], a
jr .end
.shipHasDeparted
ld hl, SSAnneNotHereText
call PrintText
.end
jp TextScriptEnd
.inFrontOfOrBehindGuardCoords ; This can be all at once don't worry.
dbmapcoord 19, 29 ; in front of guard - S. S. Anne
dbmapcoord 19, 31 ; behind guard - S. S. Anne
dbmapcoord 11, 29 ; Faraway Island
dbmapcoord 11, 31 ; Faraway Island
dbmapcoord 27, 29 ; Citrine City
dbmapcoord 27, 31 ; Citrine City
db -1 ; end
SSAnneWelcomeText4:
text_far _SSAnneWelcomeText4
text_end
SSAnneWelcomeText9:
text_far _SSAnneWelcomeText9
text_end
SSAnneFlashedTicketText:
text_far _SSAnneFlashedTicketText
text_end
SSAnneNoTicketText:
text_far _SSAnneNoTicketText
text_end
SSAnneNotHereText:
text_far _SSAnneNotHereText
text_end
HasOldSeaMap:
text_asm
ld a, [wSpritePlayerStateData1FacingDirection]
cp SPRITE_FACING_RIGHT
jr z, .greetPlayer
ld hl, VermilionCityText3.inFrontOfOrBehindGuardCoords
call ArePlayerCoordsInArray
jr nc, .greetPlayerAndCheckTicket
.greetPlayer
ld hl, EventVermillionCityOldSeaMapGreeting
call PrintText
jr .end
.greetPlayerAndCheckTicket
ld hl, EventVermillionCityOldSeaMapGreeting
call PrintText
call TheAutoskipStopinator
ld b, OLD_SEA_MAP
predef GetQuantityOfItemInBag
ld a, b
and a
jr nz, .playerHasTicket
ld hl, EventVermillionCityOldSeaMapGreetCheck
call PrintText
jr .end
.playerHasTicket
ld hl, EventVermillionCityOldSeaMap
call PrintText
ld a, $4
ld [wVermilionCityCurScript], a
.end
jp TextScriptEnd
EventVermillionCityOldSeaMapGreeting:
text "I am looking for"
line "a certain map..."
done
text_end
EventVermillionCityOldSeaMapGreetCheck:
text "Do you have it?"
done
text_end
HasCitrinePass:
text_asm
ld a, [wSpritePlayerStateData1FacingDirection]
cp SPRITE_FACING_RIGHT
jr z, .greetPlayer
ld hl, VermilionCityText3.inFrontOfOrBehindGuardCoords
call ArePlayerCoordsInArray
jr nc, .greetPlayerAndCheckTicket
.greetPlayer
ld hl, EventVermillionCityCitrinePassGreeting
call PrintText
jr .end
.greetPlayerAndCheckTicket
ld hl, EventVermillionCityCitrinePassGreeting
call PrintText
call TheAutoskipStopinator
ld b, CITRINE_PASS
predef GetQuantityOfItemInBag
ld a, b
and a
jr nz, .playerHasTicket
ld hl, EventVermillionCityCitrinePassGreetCheck
call PrintText
jr .end
.playerHasTicket
ld hl, EventVermillionCityCitrinePass
call PrintText
ld a, $4
ld [wVermilionCityCurScript], a
.end
jp TextScriptEnd
EventVermillionCityCitrinePassGreeting:
text "Hah! I only serve"
line "strong TRAINERs!"
done
text_end
EventVermillionCityCitrinePassGreetCheck:
text "Gonna need to see"
line "proof, shrimp!"
done
text_end
VermilionCityText4:
text_far _VermilionCityText4
text_end
VermilionCityText5:
text_far _VermilionCityText5
text_asm
ld a, MACHOP
call PlayCry
call WaitForSoundToFinish
ld hl, VermilionCityText14
ret
VermilionCityText14:
text_far _VermilionCityText14
text_end
VermilionCityText6:
text_asm
ld a, [wObtainedBadges]
bit 5, a ; after obtaining the marsh badge the ship returns
jr z, .default
ld hl, VermilionCityText15
ret
.default
ld hl, VermilionCityText6get
ret
VermilionCityText6get:
text_far _VermilionCityText6
text_end
VermilionCityText15:
text_far _VermilionCityText15
text_end
VermilionCityText7:
text_far _VermilionCityText7
text_end
VermilionCityText8:
text_far _VermilionCityText8
text_end
VermilionCityText11:
text_far _VermilionCityText11
text_end
VermilionCityText12:
text_far _VermilionCityText12
text_end
VermilionCityText13:
text_far _VermilionCityText13
text_end
OfficerJennySquirtle:
text_asm
CheckEvent EVENT_GOT_SQUIRTLE
jr nz, .howDoing
CheckEvent EVENT_BEAT_LT_SURGE
jr z, .noBadge
ld hl, OfficerJennyHasBadge
call PrintText
call YesNoChoice
ld a, [wCurrentMenuItem]
and a
jr nz, .refuse
call SaveScreenTilesToBuffer1 ; prevents nickname screen corruption
ld a, SQUIRTLE
ld [wd11e], a
ld [wcf91], a
call GetMonName
ld a, $1
ld [wDoNotWaitForButtonPressAfterDisplayingText], a
lb bc, SQUIRTLE, 16
call GivePokemon
ld a, [wAddedToParty]
and a
call z, WaitForTextScrollButtonPress
ld a, $1
ld [wDoNotWaitForButtonPressAfterDisplayingText], a
ld hl, OfficerJennyGive
call PrintText
SetEvent EVENT_GOT_SQUIRTLE
jp TextScriptEnd
.howDoing
ld hl, OfficerJennyHowDoing
jr .done
.noBadge
ld hl, OfficerJennyNoBadge
jr .done
.refuse
ld hl, OfficerJennyRefuse
; fallthrough
.done
call PrintText
jp TextScriptEnd
OfficerJennyNoBadge:
text_far _OfficerJennyText1
text_end
OfficerJennyHasBadge:
text_far _OfficerJennyText2
text_end
OfficerJennyGive:
text_far _OfficerJennyText3
text_waitbutton
text_end
OfficerJennyRefuse:
text_far _OfficerJennyText4
text_end
OfficerJennyHowDoing:
text_far _OfficerJennyText5
text_end
VermillionCityPassSelectionText:
text_far _VermillionCityPassSelectionText
text_end
EventVermillionCityOldSeaMap:
text_far _VermillionCityOldSeaMap
text_end
EventVermillionCityCitrinePass:
text_far _VermillionCityCitrinePass
text_end
EventVermillionCitySSTicket:
text_far _SSAnneFlashedTicketText
text_end
PassRefuse:
text_far _VermillionCityHarborRefuse
text_end
; LGPE Beauty who gives you a Persian or Arcanine depending on the game.
; Here, we make it a Cats vs Dogs question and change based on that.
; The way it works is it makes the player catch the opposite Pokemon, and then get the one they picked.
; Meowth = Arcanine
; Growlithe = Persian
; This code is nightmare fuel but it does the job.
; Basically, at some point, wBeautyChoice stops working for reasons scientists are still trying to figure out.
VermilionBeauty:
text_asm
CheckEvent EVENT_VERMILION_BEAUTY_DONE ; First, check if the event is actually done.
jp nz, .beautyDone ; Yes? Alright, no need for this.
ld a, [wBeautyChoice] ; Next, we check if wBeautyChoice has been set. This saves an event constant.
cp 0 ; It will never be 0 if the player has made their choice.
jr z, .eventStart ; If it is, then the event needs to start.
; We need to do 2 skips here which triggers me but it works.
; You could put the finish check before the choice check, but then it gets a little weird.
; All it really achieves is weird architecture for like 3-4 less machine cycles.
ld a, [wBeautyCounter] ; Alright, if you got here, then the event is in progress.
cp 5 ; Do you have 5 of the scrunklies?
jr z, .eventIsFinished ; Big if true.
jr nz, .eventInProgress ; Small if false.
; Let us start the game.
.eventStart
ld hl, BeautyText1 ; Let's open the text.
call PrintText
call CatsDogsChoice
ld a, [wCurrentMenuItem] ; Let's load what they picked. 0 is cats, 1 is dogs.
and a
jr nz, .getArcanine ; Skip storing Growlithe if dogs.
ld a, GROWLITHE ; If they picked cats, then store Growlithe.
jr .skip ; I know this looks bad, but if it isn't here, it'd store Growlithe and then go to Meowth anyway.
.getArcanine ; If they get here, they picked dogs, so we store Meowth.
ld a, MEOWTH
.skip ; Now we land here.
ld [wBeautyChoice], a ; Finally store the choice in wBeautyChoice.
ld hl, BeautyText2 ; Now spit it out.
call PrintText
; This is a jump point for if the event was already started.
.eventInProgress
ld a, [wBeautyChoice]
ld [wd11e], a
call GetMonName
ld hl, BeautyChoice
call PrintText
jr .done ; no give pokemon. bad.
; Now if the event is finished, she needs to hand the Pokemon over.
.eventIsFinished
call SaveScreenTilesToBuffer1 ; saves us from some corruption disasters if nicknaming.
ld hl, BeautyFinish1
ld a, [wBeautyChoice]
cp GROWLITHE
jr z, .skip2
ld hl, BeautyFinish2
.skip2
call PrintText
call TheAutoskipStopinator
lb bc, PERSIAN, 16 ; because we're elitists, let's see if they chose cats first.
ld a, [wBeautyChoice] ; *sigh*, but if they're dog lovers, let's make sure they actually want Persian.
cp GROWLITHE ; Do they? If yes, skip.
jr z, .skip3 ; electric boogaloo
lb bc, ARCANINE, 16 ; ok but skip2 means arc never gets loaded in. very good sequel. disney would NEVER.
.skip3
call GivePokemon
jr nc, .done
call LoadScreenTilesFromBuffer1 ; saves us from some corruption disasters if nicknaming.
SetEvent EVENT_VERMILION_BEAUTY_DONE ; and now we can finally rest.
ld hl, wd72e
set 0, [hl]
jr .done
; Now if it's already been said and done, we go here.
; Due to man-made horrors beyond my comprehension, we need to split the text here.
.beautyDone
ld hl, BeautyExplain1
ld a, [wBeautyChoice]
cp GROWLITHE
jr z, .skip4
ld hl, BeautyExplain2
.skip4
call PrintText
ld hl, BeautyExplainCont
call PrintText
;fallthrough
.done
jp TextScriptEnd
; displays cats/dogs choice
CatsDogsChoice:
call SaveScreenTilesToBuffer1
call InitCatsDogsTextBoxParameters
jr DisplayCatsDogsChoice
InitCatsDogsTextBoxParameters:
ld a, $2 ; loads the value for the unused SOUTH/EAST choice, which was changed to say CATS/DOGS
ld [wTwoOptionMenuID], a
hlcoord 12, 8
lb bc, 10, 13
ret
DisplayCatsDogsChoice:
ld a, $14
ld [wTextBoxID], a
call DisplayTextBoxID
jp LoadScreenTilesFromBuffer1
BeautyText1:
text_far _BeautyText1
text_end
BeautyText2:
text_far _BeautyText2
text_end
BeautyChoice:
text_far _BeautyChoice
text_end
BeautyFinish1:
text_far _BeautyFinish1
text_end
BeautyFinish2:
text_far _BeautyFinish2
text_end
BeautyExplain1:
text_far _BeautyExplain1
text_end
BeautyExplain2:
text_far _BeautyExplain2
text_end
BeautyExplainCont:
text_far _BeautyExplainCont
text_end