dmgtris/src/grading.asm

1684 lines
42 KiB
NASM

; DMGTRIS
; Copyright (C) 2023 - Randy Thiemann <randy.thiemann@gmail.com>
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <https://www.gnu.org/licenses/>.
IF !DEF(GRADING_ASM)
DEF GRADING_ASM EQU 1
INCLUDE "globals.asm"
SECTION "Grade Variables", WRAM0
wDecayRate: ds 1
wInternalGradePoints: ds 1
wInternalGrade: ds 1
wDisplayedGrade:: ds 1
wEffectTimer:: ds 1
wRankingDisqualified:: ds 1
wDecayCounter: ds 1
wGradeGauge: ds 1
wSMult: ds 1
wDMult: ds 1
wTMult: ds 1
wSRate: ds 1
wDRate: ds 1
wTRate: ds 1
wQRate: ds 1
wPrevCOOL: ds 3
wCOOLIsActive:: ds 1
wSubgrade: ds 1
wREGRETChecked:: ds 1
wGradeBoosts: ds 1
wCOOLBoosts: ds 1
wTGM1level300RequirementMet: ds 1
wTGM1level500RequirementMet: ds 1
wTGM1level999RequirementMet: ds 1
SECTION "Grading Data", ROMX, BANK[BANK_GAMEPLAY]
sDMGTGrading:
db 125, 10, 20, 40, 50 ; Grade 9 — frames/decay, single base, double base, triple base, tetris base
db 80, 10, 20, 30, 40 ; Grade 8 — frames/decay, single base, double base, triple base, tetris base
db 80, 10, 20, 30, 40 ; Grade 7 — frames/decay, single base, double base, triple base, tetris base
db 40, 10, 20, 30, 40 ; Grade 6 — frames/decay, single base, double base, triple base, tetris base
db 40, 5, 20, 30, 40 ; Grade 5 — frames/decay, single base, double base, triple base, tetris base
db 40, 5, 20, 30, 40 ; Grade 4 — frames/decay, single base, double base, triple base, tetris base
db 40, 5, 20, 30, 40 ; Grade 3 — frames/decay, single base, double base, triple base, tetris base
db 40, 2, 20, 20, 30 ; Grade 2 — frames/decay, single base, double base, triple base, tetris base
db 40, 2, 15, 20, 30 ; Grade 1 — frames/decay, single base, double base, triple base, tetris base
db 20, 2, 15, 20, 30 ; Grade S1 — frames/decay, single base, double base, triple base, tetris base
db 20, 2, 15, 20, 30 ; Grade S2 — frames/decay, single base, double base, triple base, tetris base
db 20, 2, 15, 20, 30 ; Grade S3 — frames/decay, single base, double base, triple base, tetris base
db 20, 2, 15, 20, 30 ; Grade S4 — frames/decay, single base, double base, triple base, tetris base
db 20, 2, 15, 20, 30 ; Grade S5 — frames/decay, single base, double base, triple base, tetris base
db 20, 2, 15, 20, 30 ; Grade S6 — frames/decay, single base, double base, triple base, tetris base
db 20, 2, 15, 20, 30 ; Grade S7 — frames/decay, single base, double base, triple base, tetris base
db 20, 2, 15, 20, 30 ; Grade S8 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 15, 20, 30 ; Grade S9 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 15, 20, 30 ; Grade S10 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 15, 20, 30 ; Grade S11 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 15, 20, 30 ; Grade S12 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 12, 15, 30 ; Grade S13 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 12, 15, 30 ; Grade m1 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 12, 15, 30 ; Grade m2 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 12, 15, 30 ; Grade m3 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 12, 15, 30 ; Grade m4 — frames/decay, single base, double base, triple base, tetris base
db 15, 2, 12, 15, 30 ; Grade m5 — frames/decay, single base, double base, triple base, tetris base
db 10, 2, 12, 15, 30 ; Grade m6 — frames/decay, single base, double base, triple base, tetris base
db 10, 2, 12, 15, 30 ; Grade m7 — frames/decay, single base, double base, triple base, tetris base
db 10, 2, 12, 15, 30 ; Grade m8 — frames/decay, single base, double base, triple base, tetris base
db 5, 2, 8, 13, 30 ; Grade m9 — frames/decay, single base, double base, triple base, tetris base
db 5, 2, 8, 13, 30 ; Grade M — frames/decay, single base, double base, triple base, tetris base
db 5, 2, 8, 13, 30 ; Grade MK — frames/decay, single base, double base, triple base, tetris base
db 5, 1, 8, 13, 30 ; Grade MV — frames/decay, single base, double base, triple base, tetris base
db 5, 1, 8, 13, 20 ; Grade MO — frames/decay, single base, double base, triple base, tetris base
db 4, 1, 4, 10, 20 ; Grade MM — frames/decay, single base, double base, triple base, tetris base
; No entry for GM. We're done there.
sDMGTGaugeLUT:
db 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3
db 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7
db 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,10,10
db 10,10,11,11,11,11,11,12,12,12,12,13,13,13,13,13
db 14,14,14,14,14,15,15,15,15,16,16,16,16,16,17,17
db 17,17,17,18,18,18,18,19,19,19,19,19,20,20,20,20
db 20,21,21,21,21,21,22,22,22,22,23,23,23,23,23,24
db 24,24,24,24,25,25,25,25,26,26,26,26,26,27,27,27
db 27,27,28,28,28,28,29,29,29,29,29,30,30,30,30,31
db 31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
sTGM3GaugeLUT:
db 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5
db 5, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10
db 10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15
db 15,15,16,16,16,17,17,17,18,18,18,19,19,19,20,20
db 20,21,21,21,22,22,22,23,23,23,23,24,24,24,25,25
db 25,26,26,26,27,27,27,28,28,28,29,29,29,30,30,30
db 31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
db 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
sTGM1GradeScores:
dw $0004 ;00 — 8
dw $0008 ;00 — 7
dw $0014 ;00 — 6
dw $0020 ;00 — 5
dw $0035 ;00 — 4
dw $0055 ;00 — 3
dw $0080 ;00 — 2
dw $0120 ;00 — 1
dw $0160 ;00 — S1
dw $0220 ;00 — S2
dw $0300 ;00 — S3
dw $0400 ;00 — S4
dw $0520 ;00 — S5
dw $0660 ;00 — S6
dw $0820 ;00 — S7
dw $1000 ;00 — S8
dw $1200 ;00 — S9
sTGM3InternalGradeSystem:
db 125, 10, 20, 40, 50 ;Decay rate, (Internal grade points awarded for:) Single, Double, Triple, Tetris
db 80, 10, 20, 30, 40
db 80, 10, 20, 30, 40
db 50, 10, 15, 30, 40
db 45, 5, 15, 20, 40
db 45, 5, 15, 20, 30
db 45, 5, 10, 20, 30
db 40, 5, 10, 15, 30
db 40, 5, 10, 15, 30
db 40, 5, 10, 15, 30
db 40, 2, 12, 13, 30
db 40, 2, 12, 13, 30
db 30, 2, 12, 13, 30
db 30, 2, 12, 13, 30
db 30, 2, 12, 13, 30
db 20, 2, 12, 13, 30
db 20, 2, 12, 13, 30
db 20, 2, 12, 13, 30
db 20, 2, 12, 13, 30
db 20, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 15, 2, 12, 13, 30
db 10, 2, 12, 13, 30
db 10, 2, 12, 13, 30
sTGM3GradeBoosts:
db 0 ;9 (0 = add 0, 1 = add 1)
db 1 ;8
db 1 ;7
db 1 ;6
db 1 ;5
db 1 ;4
db 0 ;4
db 1 ;3
db 0 ;3
db 1 ;2
db 0 ;2
db 0 ;2
db 1 ;1
db 0 ;1
db 0 ;1
db 1 ;S1 (yes, here you finally get into the S grades, unless you are very COOL)
db 0 ;S1
db 1 ;S2
db 1 ;S3
db 1 ;S4
db 0 ;S4
db 0 ;S4
db 1 ;S5
db 0 ;S5
db 1 ;S6
db 0 ;S6
db 1 ;S7
db 0 ;S7
db 1 ;S8
db 0 ;S8
db 1 ;S9
sTGM3HowManyInternalGradesToDecrease:
db 0 ;9 (0 = substract 0, 1 = substract 1, etc.)
db 1 ;8
db 1 ;7
db 1 ;6
db 1 ;5
db 1 ;4
db 2 ;4
db 1 ;3
db 2 ;3
db 1 ;2
db 2 ;2
db 3 ;2
db 1 ;1
db 2 ;1
db 3 ;1
db 1 ;S1
db 2 ;S1
db 1 ;S2
db 1 ;S3
db 1 ;S4
db 2 ;S4
db 3 ;S4
db 1 ;S5
db 2;S5
db 1 ;S6
db 2 ;S6
db 1 ;S7
db 2 ;S7
db 1 ;S8
db 2 ;S8
db 1 ;S9
sTGM3ComboMultipliers:
db 1, 1, 1, 1, 1 ; Combo size, (Multiplier for: ) Single, Double, Triple, Tetris (Screw the fractional part, x.5 gets rounded down)
db 2, 1, 1, 1, 1
db 3, 1, 1, 1, 2
db 4, 1, 1, 2, 2
db 5, 1, 1, 2, 2
db 6, 1, 1, 2, 2
db 7, 1, 1, 2, 2
db 8, 1, 1, 2, 2
db 9, 1, 1, 2, 3
db 10, 2, 2, 3, 3
sTGM3LevelMultiplier:
db 2 ; 250-499
db 3 ; 500-749
db 4 ; 750-999
sTGM3BaselineCOOL:
db 00,52 ;070 (minutes, seconds)
db 00,52 ;170
db 00,49 ;270
db 00,45 ;370
db 00,45 ;470
db 00,42 ;570
db 00,42 ;670
db 00,38 ;770
db 00,38 ;870
sTGM3REGRETConditions:
db 1, 30 ;minutes, seconds
db 1, 15
db 1, 15
db 1, 8
db 1, 0
db 1, 0
db 0, 50
db 0, 50
db 0, 50
db 0, 50
sTGM3StaffrollGrading: ;subgrades awarded per line clear
db 1 ;Single
db 2 ;Double
db 3 ;Triple
db 10 ;Tetris
db 16 ;Clear
SECTION "Grading Functions Unbanked", ROM0
GradeInit::
ld b, BANK_GAMEPLAY
rst RSTSwitchBank
call GradeInitB
jp RSTRestoreBank
UpdateGrade::
ld b, BANK_GAMEPLAY
rst RSTSwitchBank
call UpdateGradeB
jp RSTRestoreBank
DecayGradeProcess::
ld b, BANK_GAMEPLAY
rst RSTSwitchBank
call DecayGradeProcessB
jp RSTRestoreBank
DecayGradeDelay::
ld b, BANK_GAMEPLAY
rst RSTSwitchBank
call DecayGradeDelayB
jp RSTRestoreBank
TGM3REGRETHandler::
ld b, BANK_GAMEPLAY
rst RSTSwitchBank
call TGM3REGRETHandlerB
jp RSTRestoreBank
TGM3COOLHandler::
ld b, BANK_GAMEPLAY
rst RSTSwitchBank
call TGM3COOLHandlerB
jp RSTRestoreBank
SECTION "Grading Functions Banked", ROMX, BANK[BANK_GAMEPLAY]
; Wipe the grading variables.
GradeInitB:
xor a, a
ld [wDecayRate], a
ld [wInternalGradePoints], a
ld [wDisplayedGrade], a
ld [wRankingDisqualified], a
ld [wEffectTimer], a
ld [wDecayCounter], a
ld [wGradeGauge], a
ld [wSubgrade], a
ld [wGradeBoosts], a
ld [wCOOLBoosts], a
ld [wCOOLIsActive], a
ld [wREGRETChecked], a
ld [wPrevCOOL], a
ld [wPrevCOOL+1], a
ld [wPrevCOOL+2], a
ld [wTGM1level300RequirementMet], a
ld [wTGM1level500RequirementMet], a
ld [wTGM1level999RequirementMet], a
; Most modes begin ungraded.
ld a, GRADE_NONE
ld [wDisplayedGrade], a
; TGM1, TGM3, and DMGT are the exceptions.
ld a, [wSpeedCurveState]
cp a, SCURVE_TGM1
jr z, .grade9start
cp a, SCURVE_TGM3
jr z, .grade9start
cp a, SCURVE_DMGT
jr z, .grade9start
jr .end
.grade9start
ld a, GRADE_9
ld [wDisplayedGrade], a
.end
; Falls through intentionally.
; Jumps to the grade update function for the current mode.
UpdateGradeB:
ld hl, .gradejumptable
ld a, [wSpeedCurveState]
ld b, a
add a, b
add a, b
ld b, 0
ld c, a
add hl, bc
jp hl
.gradejumptable
jp UpdateGradeDMGT ;DMGT
jp UpdateGradeTGM1 ;TGM1
jp UpdateGradeTGM3 ;TGM3
jp UpdateGradeDEAT ;DEAT
jp UpdateGradeSHIR ;SHIR
no_jump ;CHIL
no_jump ;MYCO
; Jumps to the grade decay function for the current mode.
; Called once per frame where a piece is in motion.
DecayGradeProcessB:
ld hl, .gradejumptable
ld a, [wSpeedCurveState]
ld b, a
add a, b
add a, b
ld b, 0
ld c, a
add hl, bc
jp hl
.gradejumptable
jp DecayGradeDMGT ;DMGT
no_jump ;TGM1
jp DecayGradeTGM3 ;TGM3
no_jump ;DEAT
no_jump ;SHIR
no_jump ;CHIL
no_jump ;MYCO
; Jumps to the grade decay function for the current mode.
; Called once per frame during ARE and line clear delay.
DecayGradeDelayB:
ld hl, .gradejumptable
ld a, [wSpeedCurveState]
ld b, a
add a, b
add a, b
ld b, 0
ld c, a
add hl, bc
jp hl
.gradejumptable
no_jump ;DMGT
no_jump ;TGM1
jp DecayGradeTGM3 ;TGM3
no_jump ;DEAT
no_jump ;SHIR
no_jump ;CHIL
no_jump ;MYCO
; Get the four most significant figures of the score in BC as BCD.
PrepareScore:
ldh a, [hScore+SCORE_HUNDREDS]
ld b, a
ldh a, [hScore+SCORE_THOUSANDS]
swap a
or b
ld c, a
ldh a, [hScore+SCORE_TENTHOUSANDS]
ld b, a
ldh a, [hScore+SCORE_HUNDREDTHOUSANDS]
swap a
or b
ld b, a
ret
DrawGradeProgressDMGT:
ld a, [wDisplayedGrade]
cp a, GRADE_GM
jr nz, :+
ld a, $FF
ld [wGradeGauge], a
: ld hl, sDMGTGaugeLUT
ld a, [wGradeGauge]
ld b, 0
ld c, a
add hl, bc
ld a, [hl]
call SetProgress
ret
DrawGradeProgressTGM3:
ld a, [wDisplayedGrade]
cp a, GRADE_GM
jr nz, :+
ld a, $FF
ld [wInternalGradePoints], a
: ld hl, sTGM3GaugeLUT
ld a, [wInternalGradePoints]
ld b, 0
ld c, a
add hl, bc
ld a, [hl]
call SetProgress
ret
UpdateGradeDMGT:
; Check if the torikan hasn't been calculated.
ld a, [wRankingDisqualified]
cp a, $FF
jr z, .checklineclears
; Have we hit the torikan level?
ldh a, [hCLevel+CLEVEL_HUNDREDS]
cp a, 5
jr nz, .checklineclears
; Mark it as checked and do the check.
ld a, $FF
ld [wRankingDisqualified], a
; There's a 8:00 torikan at 500.
ld b, 8
ld c, 0
call CheckTorikan
; If we failed it: DIE.
cp a, $FF
jp z, .checklineclears
ld a, $FF
ld [wLockLevel], a
ld a, 5
ldh [hCLevel+1], a
ldh [hNLevel+1], a
xor a, a
ldh [hCLevel], a
ldh [hNLevel], a
ldh [hCLevel+2], a
ldh [hNLevel+2], a
ldh [hCLevel+3], a
ldh [hNLevel+3], a
jp TriggerKillScreen
; Did we have line clears?
.checklineclears
ldh a, [hLineClearCt]
or a, a
jp z, DrawGradeProgressDMGT
; Bail if we're already GM.
ld a, [wDisplayedGrade]
cp a, GRADE_GM
jp z, DrawGradeProgressDMGT
; Get grade in BC.
ld b, 0
ld c, a
; Point HL to decay rate.
ld hl, sDMGTGrading
add hl, bc
add hl, bc
add hl, bc
add hl, bc
add hl, bc
; What is our single/double/triple/quad rate?
.clearrate
inc hl
ld a, [hl+]
ld [wSRate], a
ld a, [hl+]
ld [wDRate], a
ld a, [hl+]
ld [wTRate], a
ld a, [hl]
ld [wQRate], a
; What is our single/double/triple multiplier?
.combomult
ld a, [hComboCt]
cp a, 13
jr nc, .combo13
cp a, 8
jr nc, .combo8
jr .combo1
.combo13
ld a, 2
ld [wSMult], a
ld a, 3
ld [wDMult], a
ld a, 3
ld [wTMult], a
jr .prelevel
.combo8
ld a, 1
ld [wSMult], a
ld a, 2
ld [wDMult], a
ld a, 2
ld [wTMult], a
jr .prelevel
.combo1
ld a, 1
ld [wSMult], a
ld a, 1
ld [wDMult], a
ld a, 1
ld [wTMult], a
; Branch on line clear count.
.prelevel
ldh a, [hLineClearCt]
ld d, a
cp a, 4
jr z, .tetris
cp a, 3
jr z, .triple
cp a, 2
jr z, .double
; Singles are worth the single rate x1 or x2.
.single
ld a, [wSRate]
ld d, a
ld a, [wSMult]
cp a, 1
jr z, .levelmult
ld a, d
add a, d
ld d, a
jr .levelmult
; Doubles are worth the double rate x1, x2 or x3.
.double
ld a, [wDRate]
ld d, a
ld a, [wDMult]
cp a, 1
jr z, .levelmult
cp a, 2
ld a, d
jr z, .adddonce
add a, d
.adddonce
add a, d
ld d, a
jr .levelmult
; Triples are worth the triple rate x1, x2 or x3.
.triple
ld a, [wTRate]
ld d, a
ld a, [wTMult]
cp a, 1
jr z, .levelmult
cp a, 2
ld a, d
jr z, .addtonce
add a, d
.addtonce
add a, d
ld d, a
jr .levelmult
; Tetris are worth just tetris.
.tetris
ld a, [wQRate]
ld d, a
; What is our level multiplier?
; Running counter is in in D now.
.levelmult
ld a, [hCLevel+CLEVEL_THOUSANDS] ; thousands
cp a, 1
jr nc, .mult4
ld a, [hCLevel+CLEVEL_HUNDREDS] ; hundreds
cp a, 9
jr nc, .mult4
cp a, 5
jr nc, .mult3
cp a, 2
jr nc, .mult2
jr .mult1
.mult4
ld a, d
add a, d
add a, d
add a, d
jr .processgrade
.mult3
ld a, d
add a, d
add a, d
jr .processgrade
.mult2
ld a, d
add a, d
jr .processgrade
.mult1
ld a, d
; Increase the gauge.
; The value to add to the gauge is in A
.processgrade
ld d, a
ld a, [wGradeGauge]
add a, d
ld [wGradeGauge], a
; Did we overflow? Failsafe.
jr nc, .increasegrademaybe
xor a, a
ld [wGradeGauge], a
; Increment the grade.
ld a, [wDisplayedGrade]
inc a
ld [wDisplayedGrade], a
; GM?
cp a, GRADE_GM
jr z, .gotgm
; No, play the normal jingle.
call SFXKill
ld a, SFX_RANKUP
call SFXEnqueue
ld a, $0F
ld [wEffectTimer], a
jp DrawGradeProgressDMGT
.increasegrademaybe
; Do we have 150 in the gauge?
ld a, [wGradeGauge]
cp a, 150
ret c
; Yes, take 150 away.
sub a, 150
ld [wGradeGauge], a
; Increment the grade.
ld a, [wDisplayedGrade]
inc a
ld [wDisplayedGrade], a
; GM?
cp a, GRADE_GM
jr z, .gotgm
; No, play the normal jingle.
call SFXKill
ld a, SFX_RANKUP
call SFXEnqueue
ld a, $0F
ld [wEffectTimer], a
ret
.gotgm
call SFXKill
ld a, SFX_RANKGM
call SFXEnqueue
ld a, $0F
ld [wEffectTimer], a
ret
DecayGradeDMGT:
; Bail if the gauge is empty.
ld a, [wGradeGauge]
or a, a
jp z, DrawGradeProgressDMGT
; Bail if we're already GM.
ld a, [wDisplayedGrade]
cp a, GRADE_GM
jp z, DrawGradeProgressDMGT
; Get grade in BC.
ld b, 0
ld c, a
; Point HL to decay rate.
ld hl, sDMGTGrading
add hl, bc
add hl, bc
add hl, bc
add hl, bc
add hl, bc
; Increment the decay.
ld a, [wDecayCounter]
inc a
; Did we hit the rate?
ld b, a
ld a, [hl]
cp a, b
jr z, .decay
; Nope, don't decay, but do save.
.nodecay
ld a, b
ld [wDecayCounter], a
jp DrawGradeProgressDMGT
; Yes, decay.
.decay
ld a, [wGradeGauge]
dec a
ld [wGradeGauge], a
xor a, a
ld [wDecayCounter], a
jp DrawGradeProgressDMGT
UpdateGradeTGM1:
; Bail if we're already GM.
ld a, [wDisplayedGrade]
cp a, GRADE_GM
ret z
; Bail if we didn't make the 999 check.
ld a, [wTGM1level999RequirementMet]
or a, a
ret nz
; Skip to GM check if already S9.
ld a, [wDisplayedGrade]
cp a, GRADE_S9
jp nc, .check999
.trygradeup
; Otherwise, check if we can increase the grade.
; Get our score into BC
call PrepareScore
; Double our current grade and use it as an offset into the scoring table.
ld a, [wDisplayedGrade]
add a
ld d, 0
ld e, a
; Have HL point to the next required score and get it into DE.
ld hl, sTGM1GradeScores
add hl, de
; LSB
ld a, [hl+]
ld e, a
; MSB
ld a, [hl]
ld d, a
; Check if BC >= DE...
; Return if B < D.
ld a, b
cp a, d
ret c
; We can confidently increase the grade if B > D.
jr nz, .increasegrade
; If B == D, we need to check C and E...
; Return if C < E. Otherwise increase the grade.
ld a, c
cp a, e
jr c, .check300
.increasegrade
; Add 1 to the grade.
ld a, [wDisplayedGrade]
inc a
ld [wDisplayedGrade], a
; Play the jingle, if not already doing so.
ldh a, [hCurrentlyPlaying]
cp a, SFX_RANKUP
jr z, .skipjingle
call SFXKill
ld a, SFX_RANKUP
call SFXEnqueue
; Prepare the effect stuff
.skipjingle
ld a, $0F
ld [wEffectTimer], a
; Loop and see if we can increment more grades.
ld a, [wDisplayedGrade]
cp a, GRADE_S9 ; Don't go past S9.
jr nz, .trygradeup
.check300
; Are we at level 300?
ld a, [hCLevel+CLEVEL_HUNDREDS]
cp a, 3
ret c
; Have we judged the requirement before?
ld a, [wTGM1level300RequirementMet]
or a, a
jr nz, .check500
; Rank?
ld a, [wDisplayedGrade]
cp a, GRADE_1
jr c, .fail300
; Time?
ld b, 4
ld c, 15
call CheckTorikan
cp a, $FF
jr nz, .fail300
.success300
ld a, $FF
ld [wTGM1level300RequirementMet], a
jr .check500
.fail300
ld a, $01
ld [wTGM1level300RequirementMet], a
jr .check500
.check500
; Are we at level 500?
ld a, [hCLevel+CLEVEL_HUNDREDS]
cp a, 5
ret c
; Have we judged the requirement before?
ld a, [wTGM1level500RequirementMet]
or a, a
jr nz, .check999
; Rank?
ld a, [wDisplayedGrade]
cp a, GRADE_S4
jr c, .fail500
; Time?
ld b, 7
ld c, 30
call CheckTorikan
cp a, $FF
jr nz, .fail500
.success500
ld a, $FF
ld [wTGM1level500RequirementMet], a
jr .check999
.fail500
ld a, $01
ld [wTGM1level500RequirementMet], a
jr .check999
.check999
; Level needs to be 999.
ld a, [hCLevel+CLEVEL_HUNDREDS]
cp a, 9
ret nz
ld a, [hCLevel+CLEVEL_TENS]
cp a, 9
ret nz
ld a, [hCLevel+CLEVEL_ONES]
cp a, 9
ret nz
; Have we judged the requirement before?
ld a, [wTGM1level999RequirementMet]
or a, a
ret nz
; Did both other checks succeed?
ld a, [wTGM1level300RequirementMet]
cp a, $FF
jr nz, .fail999
ld a, [wTGM1level500RequirementMet]
cp a, $FF
jr nz, .fail999
; Rank? (This is technically slightly wrong but it's nearly impossible to miss the real requirement but make this one, 6000 points.)
ld a, [wDisplayedGrade]
cp a, GRADE_S9
jr c, .fail999
; Time?
ld b, 13
ld c, 30
call CheckTorikan
cp a, $FF
jr nz, .fail999
.success999
ld a, $FF
ld [wTGM1level999RequirementMet], a
; Set the grade to GM
ld a, GRADE_GM
ld [wDisplayedGrade], a
; Sound effect
call SFXKill
ld a, SFX_RANKGM
jp SFXEnqueue
; Prepare the effect stuff
ld a, $0F
ld [wEffectTimer], a
; Return
ret
.fail999
ld a, $01
ld [wTGM1level999RequirementMet], a
ret
UpdateGradeDEAT:
; If we're disqualified, don't update the grade.
ld a, [wRankingDisqualified]
cp a, $FF
ret z
; If we are already GM, don't do anything.
ld a, [wDisplayedGrade]
cp a, GRADE_GM
ret z
.notgm
; If we're M, check if we should be GM.
cp a, GRADE_M
jr nz, .notm
; We should be GM if we're at or past level 999.
ldh a, [hCLevel+CLEVEL_HUNDREDS] ; Level, hundreds digit.
cp a, 9
ret c ; If hundreds are less than 9, return.
ldh a, [hCLevel+CLEVEL_TENS] ; Level, tens digit.
cp a, 9
ret c ; If tens are less than 9,
ldh a, [hCLevel+CLEVEL_ONES] ; Level, ones digit.
cp a, 9
ret c ; If ones are less than 9, return
; Otherwise give the grade!
ld a, GRADE_GM
ld [wDisplayedGrade], a
; Play the jingle.
call SFXKill
ld a, SFX_RANKGM
call SFXEnqueue
; Prepare the effect stuff
ld a, $0F
ld [wEffectTimer], a
ret
.notm
; If we're not M, check if we should be M.
ldh a, [hCLevel+CLEVEL_HUNDREDS] ; Level, hundreds digit.
cp a, 5
ret c ; If less than 500, return.
; There's a 3:25 torikan for M.
ld b, 3
ld c, 25
call CheckTorikan
; If we failed the Torikan, disqualify from ranking.
cp a, $FF
jr nz, .disqualify
; Otherwise award M.
ld a, GRADE_M
ld [wDisplayedGrade], a
; Play the jingle.
call SFXKill
ld a, SFX_RANKUP
call SFXEnqueue
; Prepare the effect stuff
ld a, $0F
ld [wEffectTimer], a
ret
.disqualify
; Disqualify from ranking.
ld a, $FF
ld [wLockLevel], a
ld [wRankingDisqualified], a
ld a, 5
ldh [hCLevel+1], a
ldh [hNLevel+1], a
xor a, a
ldh [hCLevel], a
ldh [hNLevel], a
ldh [hCLevel+2], a
ldh [hNLevel+2], a
ldh [hCLevel+3], a
ldh [hNLevel+3], a
jp TriggerKillScreen
UpdateGradeSHIR:
; If we're disqualified, don't update the grade any higher.
ld a, [wRankingDisqualified]
cp a, $FF
ret z
; If we are already GM, don't do anything.
ld a, [wDisplayedGrade]
cp a, GRADE_S13
ret z
; We don't give out a grade until level 100.
ldh a, [hCLevel+CLEVEL_HUNDREDS] ; Level, hundreds digit.
or a, a
ret z
; Get the hundreds and thousands of the level as a hex number.
ld b, a ; Hundreds
ldh a, [hCLevel+CLEVEL_THOUSANDS] ; Thousands
swap a
or b
; Convert the BCD to hex.
ld c, a ; C = A
and a, $F0 ; A = A & $F0. A is now $00 to $90 if the number was correct BCD.
srl a ; A = A >> 1
ld b, a ; B = A
srl a
srl a ; A = A >> 2
add a, b ; A += B
ld b, a ; B = A. At this point B is 10, 20, 30, ... 90.
ld a, c ; A = C
and a, $0F ; A = A & $0F. A is now $00 to $09 if the number was correct BCD.
add a, b ; Adding B to A gives us the converted number.
; Adding GRADE_1 to this will give us the grade.
add a, GRADE_1
ld b, a
ld a, [wDisplayedGrade]
cp a, b
ret z ; If the grade is already correct, return.
ld a, b
ld [wDisplayedGrade], a ; Otherwise, set the grade.
; Play the jingle.
call SFXKill
ld a, SFX_RANKUP
call SFXEnqueue
; Prepare the effect stuff
ld a, $0F
ld [wEffectTimer], a
; There's a few torikans for Shirase.
ld a, [wDisplayedGrade]
.s5torikan
cp a, GRADE_S5
jr nz, .s10torikan
; There's a 2:28 torikan after S5.
ld b, 2
ld c, 28
call CheckTorikan
; If we failed the Torikan, disqualify from ranking up further.
cp a, $FF
jr nz, .disqualify
ret
.s10torikan
cp a, GRADE_S10
ret nz
; There's a 4:56 torikan after S10.
ld b, 4
ld c, 56
call CheckTorikan
; If we failed the Torikan, disqualify from ranking up further.
cp a, $FF
jr nz, .disqualify
ret
.disqualify
; Disqualify from ranking.
ld a, $FF
ld [wLockLevel], a
ld [wRankingDisqualified], a
ld a, [wDisplayedGrade]
cp a, GRADE_S5
jr z, .l500
.l1000
ld a, 1
ldh [hCLevel], a
ldh [hNLevel], a
xor a, a
ldh [hCLevel+1], a
ldh [hNLevel+1], a
ldh [hCLevel+2], a
ldh [hNLevel+2], a
ldh [hCLevel+3], a
ldh [hNLevel+3], a
jp TriggerKillScreen
.l500
ld a, 5
ldh [hCLevel+1], a
ldh [hNLevel+1], a
xor a, a
ldh [hCLevel], a
ldh [hNLevel], a
ldh [hCLevel+2], a
ldh [hNLevel+2], a
ldh [hCLevel+3], a
ldh [hNLevel+3], a
jp TriggerKillScreen
UpdateGradeTGM3:
; First things first, Update our grade points.
.GradePoints
; Load the Table address to HL.
ld hl, sTGM3InternalGradeSystem
; Get the correct offset using the lines cleared on the frame and our current Internal Grade.
; Make sure the offsets are set properly and that the amount of Cleared lines isn't 0.
ld a, [wInternalGrade] ; Example: 3
cp a, 0 ; If it's 0, we don't need to do weird math.
jr z, .GetOffset
ld d, a ; ld d, 3
ld b, 5
ld a, b ; ld a, 5
dec d ;dec 3 to 2, so we don't accidentally add more than intended
: add a, b ; 5+5 = 10 ; 10+5 = 15
dec d
jr nz, :- ; go back if d isn't 0
ld b, a ; ld b, 15
.GetOffset
ld a, [hLineClearCt]
cp a, 0 ; If no lines were cleared, we don't need to do anything, just continue
jp z, .IncreaseInternalGrade
add a, b
ld b, 0
ld c, a
add hl, bc
ld a, [hl]
ld e, a ; We will use almost all registers to get the multipliers, so we need to keep the points we should add in a safe spot
jp .multipliers
.loadpoints
ld hl, wInternalGradePoints
add a, [hl]
ld [wInternalGradePoints], a
; Draw the progress
call DrawGradeProgressTGM3
jp .IncreaseInternalGrade
.multipliers
; There are some multipliers to help us increase our grade faster
ld hl, sTGM3ComboMultipliers
ld a, [hComboCt] ; Example: 3
cp a, 0
ld d, a ; ld d, 3
ld b, 5
ld a, b ; ld a, 5
dec d ;dec 3 to 2, so we don't accidentally add more than intended
: add a, b ; 5+5 = 10 ; 10+5 = 15
dec d
jr nz, :- ; go back if d isn't 0
ld b, a ; ld b, 15
ld a, [hLineClearCt]
cp a, 0 ; If no lines were cleared, we don't need to do anything, just continue
jr z, .levelmultiplier
add a, b
ld b, 0
ld c, a
add hl, bc
ld a, [hl] ; Now we got our multiplier!, let's apply it.
dec a ; A multiplier of 1 shouldn't change anything, so let's get rid of them
cp a, 0
jr z, .levelmultiplier; Continue
ld b, a ; Load the multiplier into B
ld a, e ; Remember the points we got earlier?, here they are
ld c, a ; We will add from C
: add a, c
dec b
jr nz, :-
; Finally!, we can now load the points, right?, NO!, there is yet another multiplier...
; We have to keep the points safe again...
ld e, a
.levelmultiplier
; Make HL point to the table that contains the level multipliers
ld hl, sTGM3LevelMultiplier
; Get our level into BC
ld a, [hCLevel+3]
ld b, a
ld a, [hCLevel+2]
swap a
or b
ld c, a
ld a, [hCLevel+1]
ld b, a
ld a, [hCLevel]
swap a
or b
ld b, a
.Level750
; Is our level 750 or higher?
ld a, b
cp a, LEVEL_MULT_3A
; If B is less than 7, that means we are not even in level 700, so check for 500
jr c, .Level500
; If B is NOT less than 7, we might be in level 750 or greater, so check the remaining digits.
ld a, c
cp a, LEVEL_MULT_3B
; If C is less than 50, we didn't reach level 750 yet, but we are for sure in the 7** Section, load the corresponding offset.
jp .under750
; If C is equal or greater than 50, then congrats!, load the corresponding offset
ld b, 0
ld c, 2
add hl, bc
ld a, [hl]
jp .Multiply
.under750
ld b, 0
ld c, 1
add hl, bc
ld a, [hl]
.Level500
; Is our level 500 or higher?
ld a, b
cp a, LEVEL_MULT_2A
; If B is less than 5, that means we are not even in level 500, so check for 250
jr c, .Level250
; If B is NOT less than 5, we are in level 500 or greater
ld b, 0
ld c, 2
add hl, bc
ld a, [hl]
jp .Multiply
.Level250 ; There is no Offset, so just get the multiplier
; Is our level 750 or higher?
ld a, b
cp a, LEVEL_MULT_1A
; If B is less than 2, that means we are not even in level 200, so no multiplier
jr c, .under250
; If B is NOT less than 2, we might be in level 250 or greater, so check the remaining digits.
ld a, c
cp a, LEVEL_MULT_1B
; If C is less than 50, we didn't reach level 250 yet, so no multiplier
jp .under250
; If C is equal or greater than 50, then congrats!, load the corresponding offset (I said there is no Offset!)
ld a, [hl]
jp .Multiply
.under250 ; There is no multiplier, so just load the points
ld a, e
jp .loadpoints
.Multiply ; FINALLY!!!!!, This took forever!
ld b, a ; Load the multiplier into B
ld a, e ; Remember the points we got earlier?, here they are... Again.
ld c, a ; We will add from C
: add a, c
dec b
jr nz, :-
; AND NOW WE ARE DONE!, LOAD THOSE POINTS!
jp .loadpoints
.IncreaseInternalGrade
; Are we on level *00-*05?
ld a, [hCLevel+CLEVEL_TENS]
cp a, 0
jr nz, .nocool
ld a, [hCLevel+CLEVEL_ONES]
cp a, 6
; If we are, jump to the update COOL grade funcion just in case we have to apply a section COOL
call c, CheckCOOL
; If not, continue
.nocool
; Do we have 100 Grade Points?
ld a, [wInternalGradePoints]
cp a, 100
ret c ; If the Internal Grade Points is less than 100, return, we don't have to change our Grade
xor a, a ; Reset the points to 0 and increase the internal grade
ld [wInternalGradePoints], a
ld a, [wInternalGrade]
inc a
ld [wInternalGrade], a
call DrawGradeProgressTGM3
; This falls to the next function, this is intentional
TGM3UpdateDisplayedGrade:
ld a, [wGradeBoosts] ; If we are an S9 Grade, return
cp a, GRADE_S9
ret z
ld a, GRADE_9 ; Load the lowest grade into a
ld b, a ; Then save it into b
ld hl, sTGM3GradeBoosts ; Make HL point to the Grade boosts table
ld a, [wInternalGrade] ; Get the offset
ld b, 0
ld c, a
add hl, bc
ld a, [hl] ; Load the boosts to add into a...
ld b, a ; And then into b.
.update
ld a, [wGradeBoosts] ; Load the boosts variable into A
add a, b ;Add the boosts
ld [wGradeBoosts], a ; And load them.
ld b, a
ld a, [wCOOLBoosts] ; Add our Section COOL boosts
add a, b
ld b, a
ld a, [wDisplayedGrade]
cp a, b
ret z ; If the grade is the same, return.
ld a, b
; Is our Grade S10 or higher?
cp a, GRADE_S10
jr c, .notaboves10 ; No, it isn't
add a, GRADE_S10_PLUS ; Yes, it is
.notaboves10
ld [wDisplayedGrade], a ; Otherwise, set the grade.
; ...Play the jingle.
ld a, SFX_RANKUP
call SFXEnqueue
; Prepare the effect stuff
ld a, $0f
ld [wEffectTimer], a
ret
CheckCOOL:
ld a, [wCOOLIsActive] ; Did the player get a cool on this section?
cp a, 1
ret nz
; If it did, check if we are at level *00-*05
ld a, [hCLevel+CLEVEL_TENS]
cp a, 0
ret nz
ld a, [hCLevel+CLEVEL_ONES]
cp a, 5
jr c, .cool
ret nz ; If not, proceed as normal
.cool
ld a, [wCOOLBoosts] ; Load our COOL Boosts into A
inc a ; Increase A
ld [wCOOLBoosts], a ; And load the result
; Now let's display our new grade!
ld b, a
ld a, [wDisplayedGrade]
inc a
; Does it result in an S10 grade?
cp a, GRADE_S10
jr nz, .nots10 ; No, it doesn't
add a, GRADE_S10_PLUS ; Yes, it does
.nots10
ld [wDisplayedGrade], a ; Load the boosts into the displayed grade
xor a, a
ld [wCOOLIsActive], a ; Make the cool no longer be active
jp SkipSection
DecayGradeTGM3:
; Check if we can decrease the Grade points, if not, decrease the timer
ld a, [wDecayRate]
cp a, 0
jr z, .points
ld b, a ; Save the timer
ldh a, [hComboCt] ; If there is an active combo, do not decrease the counter, check if we can increase our internal grade instead
dec a
and a
call nz, UpdateGradeTGM3.IncreaseInternalGrade
ld a, b ; Load the timer back
dec a
ld [wDecayRate], a
ret
.points
ld a, [wInternalGradePoints] ; Do we have 0 grade points?
cp a, 0
ret z ; If so, return
dec a
cp a, 0 ; Do we have 0 now?
jr z, .lpoints ; If so, load the points, since we don't have any points to decay
; Else, load the points and the corresponding Decay Rate
ld [wInternalGradePoints], a
call DrawGradeProgressTGM3
; Get the Decay Rate required
ld hl, sTGM3InternalGradeSystem
ld a, [wInternalGrade] ; Example: 3
cp a, 0 ; If it's 0, we don't need to do weird math.
jr z, .GetOffset
ld d, a ; ld d, 3
ld b, 5
ld a, b ; ld a, 5
dec d ;dec 3 to 2, so we don't accidentally add more than intended
: add a, b ; 5+5 = 10 ; 10+5 = 15
dec d
jr nz, :- ; go back if d isn't 0
.GetOffset
ld b, 0
ld c, a
add hl, bc
ld a, [hl] ; Load the rate into a...
ld [wDecayRate], a ; ... and then into the timer
ret
.lpoints
ld [wInternalGradePoints], a
jp DrawGradeProgressTGM3
TGM3COOLHandlerB:
; First check our previous cool
ld a, [wPrevCOOL] ; Are the minutes 0?
cp a, 0
jr nz, .checkCOOL
; If so, check the seconds
ld a, [wPrevCOOL+1]
cp a, 0
jr nz, .checkCOOL
; The seconds are 0 too?, hmm, check the frames
ld a, [wPrevCOOL+2]
cp a, 0
jr nz, .checkCOOL
; No cool???, check the baseline cool then...
ld hl, sTGM3BaselineCOOL
ld a, [hCLevel+1]
add a
ld b, 0
ld c, a
add hl, bc
ld a, [hl+]
ld b, a
ld a, [hl]
ld c, a
jp .checkBaselineCOOL
.checkCOOL
; Load the minutes.
ld a, [wPrevCOOL]
ld b, a
; Load the seconds.
ld a, [wPrevCOOL+1]
add a, 2 ; Give the player 2 seconds to spare
cp a, 60 ; Are we above 60 now?
jr c, .nocarry ; If so, add 1 to the minutes and subtract 60 from the seconds
inc b
sub a, 60
.nocarry
ld c, a
; Load the frames.
ld a, [wPrevCOOL+2]
ld d, a
.checkBaselineCOOL
call CheckCOOL_REGRET
cp a, $ff
jp nz, .nocool
.cool ; If the player got a cool, set the active cool variable to 1, also set the previous cool variables to the correct value
ld a, 1
ld [wCOOLIsActive], a
ld a, [wSectionMinutes]
ld [wPrevCOOL], a
ld a, [wSectionSeconds]
ld [wPrevCOOL+1], a
ld a, [wSectionFrames]
ld [wPrevCOOL+2], a
ld a, 1 ; Leave a value in A so we know if the code ran
ret ; Done
.nocool ; If the player misses a cool, set the previous cool variables to 0, then return
ld [wPrevCOOL], a
ld [wPrevCOOL+1], a
ld [wPrevCOOL+2], a
ld a, 1 ; Leave a value in A so we know if the code ran
ret
TGM3REGRETHandlerB: ; Check if we took too much time to complete a section
ld a, [wREGRETChecked] ; First, make sure we haven't checked before
cp a, 1
ret z ; If we did, just return
ld hl, sTGM3REGRETConditions
ld a, [hCLevel+1]
dec a
add a
ld b, 0
ld c, a
add hl, bc
ld a, [hl+]
ld b, a
ld a, [hl]
ld c, a
call CheckCOOL_REGRET
cp a, 0
ret nz
.regret
ld a, [wInternalGrade]
cp a, 0
ret z
xor a, a
ld [wInternalGradePoints], a
ld a, [wGradeBoosts]
dec a
ld [wGradeBoosts], a
ld hl, sTGM3HowManyInternalGradesToDecrease ; Make HL point to the..., yeah.
ld a, [wInternalGrade] ; Get the offset
ld d, a ; save the internal grade because we need it later
ld b, 0
ld c, a
add hl, bc
ld a, [hl] ; Load the amount of internal grades to decrease into a
sub a, d ; Decrease the internal grades
ld [wInternalGrade], a ; Load them
ld a, [wGradeBoosts] ; Load the boosts variable into A
ld [wDisplayedGrade], a ; Load the boosts into the displayed grade
ld a, 1
ld [wREGRETChecked], a
ret ; Done
TGM3StaffRollGradeUpdate:
; Make HL Point to the Staffroll Table
ld hl, sTGM3StaffrollGrading
; Get the offset, if no lines were cleared, return
ld a, [hLineClearCt]
and a
ret z
dec a
ld b, 0
ld c, a
add hl, bc
; Load the value into A
ld a, [hl]
; And then add that to the subgrade variable
ld b, a
ld a, [wSubgrade]
add a, b
ld [wSubgrade], a
.UpdateGrade
cp a, $a
ret c
sub a, $a
ld [wSubgrade], a
ld a, [wDisplayedGrade]
inc a
ld [wDisplayedGrade], a
ret
ENDC