; DMGTRIS ; Copyright (C) 2023 - Randy Thiemann ; 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 . 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