; 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(RNG_ASM)
DEF RNG_ASM EQU 1


INCLUDE "globals.asm"


SECTION "High RNG Variables", HRAM
hRNGSeed:      ds 4
hPieceHistory: ds 4
hNextPiece::   ds 1
hUpcomingPiece1:: ds 1
hUpcomingPiece2:: ds 1


SECTION "TGM3 RNG Variables", WRAM0
wTGM3Bag:             ds 35
wTGM3Droughts:        ds 7
wTGM3GeneratedIdx:    ds 1
wTGM3WorstDroughtIdx: ds 1


section "RNG Functions", ROM0
HarvestEntropy::
    ld hl, $C000
    ld de, $E000
.loop
    ldh a, [hRNGSeed]
    xor a, [hl]
    inc hl
    ldh [hRNGSeed], a

    ldh a, [hRNGSeed+1]
    xor a, [hl]
    inc hl
    ldh [hRNGSeed+1], a

    ldh a, [hRNGSeed+2]
    xor a, [hl]
    inc hl
    ldh [hRNGSeed+2], a

    ldh a, [hRNGSeed+3]
    xor a, [hl]
    inc hl
    ldh [hRNGSeed+3], a

    ld a, h
    cp a, d
    jr nz, .loop

    ld a, l
    cp a, e
    jr nz, .loop

    ret

    ; Snapshots the initial seed for a game, then initializes the history and piece queue.
RNGInit::
    ; Do some bit fuckery on the seed using the gameboy's free-running timers.
    ld hl, rDIV
    ldh a, [hRNGSeed]
    xor a, [hl]
    ldh [hRNGSeed], a
    ldh a, [hRNGSeed+2]
    xor a, [hl]
    ldh [hRNGSeed+2], a

    ld hl, rTIMA
    ldh a, [hRNGSeed+1]
    xor a, [hl]
    ldh [hRNGSeed+1], a
    ldh a, [hRNGSeed+3]
    xor a, [hl]
    ldh [hRNGSeed+3], a

    ; TGM3 vars
    ld b, BANK_GAMEPLAY
    rst RSTSwitchBank
    ld de, sTGM3Bag
    ld hl, wTGM3Bag
    ld bc, 35
    call UnsafeMemCopy
    ld de, sTGM3Droughts
    ld hl, wTGM3Droughts
    ld bc, 7
    call UnsafeMemCopy
    rst RSTRestoreBank

    ; Start with no piece held.
    ld a, PIECE_NONE
    ldh [hHeldPiece], a

    ; If we're in HELL mode, we don't care about anything but a random piece to start with.
    ld a, [wRNGModeState]
    cp a, RNG_MODE_HELL
    jr nz, .complexinit

.hellinit
    call Next7Piece
    ld [hUpcomingPiece2], a
    call Next7Piece
    ld [hUpcomingPiece1], a
    call Next7Piece
    ld [hNextPiece], a
    xor a, a
    ldh [hPieceHistory], a
    ldh [hPieceHistory+1], a
    ldh [hPieceHistory+2], a
    ldh [hPieceHistory+3], a
    ret

    ; Otherwise do complex init.
.complexinit
    ld a, PIECE_Z
    ldh [hPieceHistory], a
    ldh [hPieceHistory+1], a
    ldh [hPieceHistory+2], a
    ldh [hPieceHistory+3], a

    ld a, [wRNGModeState]
    cp a, RNG_MODE_TGM1
    jr z, .getfirstpiece
    ld a, PIECE_S
    ldh [hPieceHistory+2], a
    ldh [hPieceHistory+3], a

    ; Get the first piece and make sure it's not Z, S or O.
.getfirstpiece
    call Next7Piece
    cp a, PIECE_Z
    jr z, .getfirstpiece
    cp a, PIECE_S
    jr z, .getfirstpiece
    cp a, PIECE_O
    jr z, .getfirstpiece

    ; Save the generated piece and put it in the history.
    ldh [hPieceHistory], a
    ldh [hUpcomingPiece1], a
    ldh [hUpcomingPiece2], a

    ; Generate the next 2 to fill up the queue.
.getqueue
    call GetNextPiece
    jp GetNextPiece


    ; Shift the generated piece into the history and save it.
ShiftHistory:
    ld b, a
    ldh a, [hUpcomingPiece1]
    ldh [hNextPiece], a
    ldh a, [hUpcomingPiece2]
    ldh [hUpcomingPiece1], a
    ld a, b

    ldh [hUpcomingPiece2], a
    ldh a, [hPieceHistory+2]
    ldh [hPieceHistory+3], a
    ldh a, [hPieceHistory+1]
    ldh [hPieceHistory+2], a
    ldh a, [hPieceHistory]
    ldh [hPieceHistory+1], a
    ldh a, [hUpcomingPiece2]
    ldh [hPieceHistory], a
    ret


    ; A random piece. Get fucked.
GetNextHellPiece:
    call Next7Piece
    jr ShiftHistory


    ; 4 history, 4 rerolls.
GetNextTGM1Piece:
    ld a, 5
    ld e, a

:   dec e
    jr z, :+

    call Next7Piece
    ld hl, hPieceHistory
    cp a, [hl]
    jr z, :-
    inc hl
    cp a, [hl]
    jr z, :-
    inc hl
    cp a, [hl]
    jr z, :-
    inc hl
    cp a, [hl]
    jr z, :-

:   jr ShiftHistory


    ; 4 history, 6 rerolls.
GetNextTGM2Piece:
    ld a, 7
    ld e, a

:   dec e
    jr z, :+

    call Next7Piece
    ld hl, hPieceHistory
    cp a, [hl]
    jr z, :-
    inc hl
    cp a, [hl]
    jr z, :-
    inc hl
    cp a, [hl]
    jr z, :-
    inc hl
    cp a, [hl]
    jr z, :-

:   jr ShiftHistory


    ; 1 history, 1 reroll.
GetNextNesPiece:
    call Next7Piece
    ld hl, hPieceHistory
    cp a, [hl]
    jr nz, ShiftHistory
    call Next7Piece
    jr ShiftHistory


    ; TGM3 mode... It's complex.
GetNextTGM3Piece:
    ld a, 7
    ld e, a

:   dec e
    jr z, :+

    ; Get a random index into the 35bag
    call Next35Piece
    ld [wTGM3GeneratedIdx], a

    ; Fetch the piece from the 35bag.
    ld c, a
    xor a, a
    ld b, a
    ld hl, wTGM3Bag
    add hl, bc
    ld a, [hl]

    ; Is it in the history?
    ld hl, hPieceHistory
    cp a, [hl]
    jr z, :-
    inc hl
    cp a, [hl]
    jr z, :-
    inc hl
    cp a, [hl]
    jr z, :-
    inc hl
    cp a, [hl]
    jr z, :-

    ; We have a piece. Save it.
:   call ShiftHistory

    ; Increment all drought counters.
:   ld hl, wTGM3Droughts
    inc [hl]
    inc hl
    inc [hl]
    inc hl
    inc [hl]
    inc hl
    inc [hl]
    inc hl
    inc [hl]
    inc hl
    inc [hl]
    inc hl
    inc [hl]

    ; Set the drought of our most recently drawn piece to 0.
:   ldh a, [hUpcomingPiece2]
    ld c, a
    xor a, a
    ld b, a
    ld hl, wTGM3Droughts
    add hl, bc
    ld [hl], a

    ; We pick an arbitrary piece to have the worst drought.
:   call Next7Piece
    ld [wTGM3WorstDroughtIdx], a

    ; And then save that drought in e.
    ld c, a
    xor a, a
    ld b, a
    ld hl, wTGM3Droughts
    add hl, bc
    ld e, [hl]

    ; Is idx 0 worse?
:   ld hl, wTGM3Droughts
    ld a, [hl+]
    cp a, e
    jr z, :+    ; Same.
    jr c, :+    ; Nope.
    ld e, a
    xor a, a
    ld [wTGM3WorstDroughtIdx], a

    ; Is idx 1 worse?
:   ld a, [hl+]
    cp a, e
    jr z, :+    ; Same.
    jr c, :+    ; Nope.
    ld e, a
    ld a, 1
    ld [wTGM3WorstDroughtIdx], a

    ; Is idx 2 worse?
:   ld a, [hl+]
    cp a, e
    jr z, :+    ; Same.
    jr c, :+    ; Nope.
    ld e, a
    ld a, 2
    ld [wTGM3WorstDroughtIdx], a

    ; Is idx 3 worse?
:   ld a, [hl+]
    cp a, e
    jr z, :+    ; Same.
    jr c, :+    ; Nope.
    ld e, a
    ld a, 3
    ld [wTGM3WorstDroughtIdx], a

    ; Is idx 4 worse?
:   ld a, [hl+]
    cp a, e
    jr z, :+    ; Same.
    jr c, :+    ; Nope.
    ld e, a
    ld a, 4
    ld [wTGM3WorstDroughtIdx], a

    ; Is idx 5 worse?
:   ld a, [hl+]
    cp a, e
    jr z, :+    ; Same.
    jr c, :+    ; Nope.
    ld e, a
    ld a, 5
    ld [wTGM3WorstDroughtIdx], a

    ; Is idx 6 worse?
:   ld a, [hl+]
    cp a, e
    jr z, :+    ; Same.
    jr c, :+    ; Nope.
    ld e, a
    ld a, 6
    ld [wTGM3WorstDroughtIdx], a

    ; We now have the worst drought index as well as the slot in the bag that needs to be replaced.
:   ld a, [wTGM3GeneratedIdx]
    ld c, a
    xor a, a
    ld b, a
    ld hl, wTGM3Bag
    add hl, bc
    ld a, [wTGM3WorstDroughtIdx]

    ; Replace that slot.
    ld [hl], a
    ret

    ; Gets the next piece depending on RNG mode.
GetNextPiece::
    ld hl, .nextpiecejumps
    ld a, [wRNGModeState]
    ld b, 0
    ld c, a
    add a, c
    add a, c
    ld c, a
    add hl, bc
    jp hl

.nextpiecejumps
    jp GetNextTGM1Piece
    jp GetNextTGM2Piece
    jp GetNextTGM3Piece
    jp GetNextHellPiece
    jp GetNextNesPiece


    ; Tries generating bytes until it gets one in [0; 35)
Next35Piece:
:   call NextByte
    and a, $3F
    cp a, 35
    jr nc, :-
    ret


    ; Tries generating bytes until it gets one in [0; 7)
Next7Piece:
:   call NextByte
    and a, $07
    cp a, 7
    jr nc, :-
    ret


    ; Cyrcles the RNG returning a random byte in a.
NextByte:
    ; Load seed
    ld hl, hRNGSeed+3
    ld a, [hl-]
    ld b, a
    ld a, [hl-]
    ld c, a
    ld a, [hl-]

    ; Multiply by 0x01010101
    add [hl]
    ld d, a
    adc c
    ld c, a
    adc b
    ld b, a

    ; Add 0x31415927 and write back
    ld a, [hl]
    add $27
    ld [hl+], a
    ld a, d
    adc $59
    ld [hl+], a
    ld a, c
    adc $41
    ld [hl+], a
    ld c, a
    ld a, b
    adc $31
    ld [hl], a
    ret


ENDC
