;;-----------------------------LICENSE NOTICE------------------------------------
;;  This file is part of Dragon Attack - An entry for CPCRetroDev2016
;;  Copyright (C) 2016  Paul Kooistra
;;
;;  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 <http://www.gnu.org/licenses/>.
;;
;;  For questions about the source you can PM me, Axelay, on the CPCWiki forums
;;-------------------------------------------------------------------------------
org &40
run start
nolist
write direct "a:code.bin"

InfiniteLives equ 0 ; test game setting, set to 0 for normal build
StartRound equ 0 ; for testing, set to 0 for normal build
InitGame equ 0 ; set to 1 for running from assembler, 0 for writing runnable file with loader
SpriteExplosionFrame equ 22
SpriteExplosionFrameEnd equ SpriteExplosionFrame+7
FinalRound equ 12
MenuMusicOff equ 7


; menu jump block and addresses while decompressed
DrawTitleLogo equ &8151
ProcessMainMenu equ &8154
GetRomChars equ &8157
Init_Display equ &815a
DisplayGameEnding equ &815d
MenuMusic equ &815d+3
NameEntryMusic equ &8312+3
VictoryMusic equ &83e6
VictoryMusicBadEnd equ &84e0

GameDisplayFont equ &5f50 ; ends at &6000 - used by time bonus font
LowScrLUTable equ &6000
HighScrLUTable equ &6200
LowScrLUTableLinear equ &6400
HighScrLUTableLinear equ &6600 ; ends at &6820
; high score table starts at &6820
EBYsp equ &6a00
EBY equ &6b00
EBX equ &6c00
EBXsp equ &6d00
EBMv equ &6e00

ESprHighAddr equ &76e0 ; &120
EPlyHighAddr equ &7860 ; 24+12
ESprLowAddr equ &7560 ; &120
EPlyLowAddr equ &7060 ; 24+12
EBHighAddr equ &78d0 ;&400+48
EBLowAddr equ &70d0 ;&400+48

BossFrameLU equ &7d00
FastFontLU equ &7d40
FastFont0 equ &7d5e
FastFont1 equ &7d90
ShrapnelDirList equ &7dc0
ShotgunDirList equ &7de0

EBDirY equ &7e00
EBDirX equ &7f00

; define variables in the unused screen ram areas
; &8000-&807f & &8800-&807f reserved for use by sprite printing as offscreen buffer space
FireSeqTable equ &9000 ; uses all 128 bytes

Score equ &9800 ; 6 bytes in 3 words storing 0-9999
BCD_Score_Segment equ &9806; stores one of the 3 words across 4 bytes, 0-9
ScoreUpdater equ &980a ; counter 0-5 for updating the score display in stages
RoundTimeDsp equ &980b ; last displayed counter of seconds left in round 0-99
RoundTime equ &980c ; counter of seconds left in round 0-99
Round50Sec equ &980d ; counter of 50ths of seconds left in round second 1-50
BCD_Round_Mult equ &980e ; two bytes used to display roundtime and multiplier on screen
Lives equ &9810 ; Current Lives 0-3
Round equ &9811 ; Current Round 1-?
MultProgBar equ &9812 ; multiplier fraction 0-159
MultProgBarFrame equ &9813 ; multiplier fraction for active frame
ProgBarDisplay equ &9814 ; 10 byte buffer for progress bar display, to &981d
TailYListPtr equ &981e ; pointer to tailylist for tails to follow head
FrameHits equ &981f ; number of player bullet strikes this frame
FrameScore equ &9820 ; score for this frame
FrameMult equ &9821 ; multiplier for this frame, equals number of segments destroyed simulataneously
PlayerExp equ &9822 ; timer for player explosion, also 2 bits used to denote previous display of player exp
SfxTrigger equ &9823 ; 2 bytes, first is trigger, second is timer for blocking player shot sfx
BossMvY equ &9825 ; boss current y mv
BossYsp equ &9826 ; current sub pixel fraction offset of boss in y
BossMvX equ &9827 ; boss current x mv
BossXsp equ &9828 ; current sub pixel fraction offset of boss in x
RoundEnd equ &9829 ; set to a counter when boss killed and used as timer to start next round
BossXLimits equ &982a ; 2 bytes, left x limit, right x limit
KeyInY equ &982c ; 3 bytes for player input y,x,fire
PBullDelay equ &982f ; timer for regulating player shot repeat
PlayerHitTimer equ &9830 ; 
BossColours equ &9831 ; 2 bytes for colour 2 & 3
WorkScr equ &9833 ; current work screen (hidden) will be either &80 or &c0
PrnEnemy equ &9834 ; set non zero when ok to display dragon sprites under interrupt
MultiplierDsp equ &9835 ; last displayed score multiplier 1-99
Multiplier equ &9836 ; current score multiplier 1-99
NewFastShotY equ &9837 ; y co-ord of new fast enemy laser shot
NewFastShotX equ &9838 ; x co-ord of new fast enemy laser shot
TimeBonus equ &9839 ; amount of time when dragon destroyed, 0 if no bonus
TimeBonusDspTimer equ &983a ; timer for displaying time bonus message
BCD_TimeBonus equ &983b ; 2 bytes to store the two character time bonus display here
KeepPlayerInv equ &983d ; byte used to indicate player is to be kept invulnerable
PlayerAutoPilot equ &983e ; byte to indicate player should fly off screen
DragonType equ &983f ; type 0 standard, type 1 long, type 2 final
DragonHeadPtr equ &9840 ; two byte ptr to y co-ord of current dragon's head
DragonLeaving equ &9842 ; byte set non zero when dragon leaves at end of round timer
TailXListPtr equ &9843 ; pointer to tailxlist for long dragon tails to follow head
FinalBossActiveHead equ &9844 ; value 0,2 or 4, points to current active head
FinalBossHitCheck equ &9845 ; contains the level below which the next head change occurs
DragonsDestroyed equ &9846 ; count of dragons destroyed, determines game ending on completion

TailYList equ &a000 ; 32 bytes for trailing y co-ord list of head, for 'tail' effect
; for ease of display code, make data format ?,?,?,type
EnemyYX equ &a800 ; &20 for co-ords, then &20 for frame,state,hits?
EnemyData1 equ &a820 ; &20 state,hits
EnemyData2 equ &a840 ; &20 frame,score
PlayerYXShrapnel equ &a860 ; &10 for yx, &10 for yxmv in pixel steps

KBMatrixBuf equ &b000 ;&7d00 ; must be at base of page
PlayerYX equ &b070 ;&7d10
PlayerTestY equ &b072 ;&7d12
PlayerTestX equ &b073 ;&7d13
EBRingYX equ &b040 ;&7d40 ; &10 for co-ords, format is ysp,y,xsp,x
EBRingYXMv equ &b050 ;&7d50 ; &10 for move, format xmv,ymv, then direction, then type/counter
EBRingHits equ &b060 ;&7d60 ; &10 format unused,unused,unused,hit status (non zero for rings, cleared if shot)
FastShotYX equ &b010 ;&b800 ;&6880 ; &10 each, 8 shots at 2 bytes only
FastShotYXLow equ &b020 ;&b810 ;&6890
FastShotYXHigh equ &b030 ;&b820 ;&68a0

TailXList equ &b800 ; 128 bytes for trailing x co-ord list of head, only used on type 1 dragons

; lookup table for music
.MusicIndex
    defw Music06of12
    defw Music03of12
    defw Music10of12
    defw Music09of12
    defw Music13of12
    defw Music04of12
    defw Music02of12
    defw Music01of12
    defw Music08of12
    defw Music05of12
    defw Music12of12
    defw Music11of12
    defw MusicDead
    defw Music01of12
    defw Music01of12
    defw Music01of12

read "Music/01of12b.asm"
read "Music/02of12b.asm"
read "Music/03of12b.asm"
read "Music/04of12b.asm"
read "Music/05of12.asm"
read "Music/06of12.asm"
read "Music/13of12.asm"
read "Music/08of12.asm"
read "Music/09of12.asm"
read "Music/10of12.asm"
read "Music/11of12.asm"
read "Music/12of12_b.asm"
read "Music/Dead_b.asm"
.EndMusic
    defs &1800-EndMusic-15-12-10
; variables in fixed place visible to decompressed menu code
.MenuColourPtr
    defw BlankMenuCols+3
.BlankMenuCols
    defb &40,&40,&40,&40
.MenuCols
    defb &40,&54,&4e,&4b
.TitleMusic_Control
    defb MenuMusicOff ; title music starts disabled
.OptionsByte
    defb 1 ; joystick control
.StartLives
    defb 3
.Ind_wait_int ; jump to wait_int
    jp wait_int
.Ind_FindNumber8bit ; jump to FindNumber8bit
    jp FindNumber8bit
.Ind_FindNumber ; jump to FindNumber
    jp FindNumber16bit
read "CPCtelera_random_mod.asm"
list
.start
nolist

; if InitGame set for running directly in emulator
; set up some things normally set up in loader
if InitGame = 1

    di
    ld sp,&38

;; set width of display window

;; this value is compatible with crtc type 2
ld bc,&bc01
out (c),c
ld bc,&bd00+32
out (c),c

;; set horizontal sync position and therefore the
;; horizontal position of the display window
;; within the monitor display
ld bc,&bc02
out (c),c
ld bc,&bd00+42
out (c),c

;; set height of display window
ld bc,&bc06
out (c),c
ld bc,&bd00+32
out (c),c

;; set vertical sync position and therefore the
;; vertical position of the display window
;; within the monitor display
ld bc,&bc07
out (c),c
ld bc,&bd00+34
out (c),c

; set all 16 colours
    ld hl,LoaderCols+15
    call SetColours

;; set interrupt jump instruction
    ld a,&c3
    ld (&0038),a

endif

;; set interrupt jump address
    ld hl,Int_start
    ld (&0039),hl

; set up start condition for game loop - interrupt is normally active
    call Start_Interrupt

; copy bullet dir table
; this must occur before other tables clear so this data can reside in location of lowest tables
; during loading if need be
    call MoveBulletDirTable

; main game loop
.TitleStart
; clear both buffer screens
    ld a,&80
    call ClearScr
    ld a,&c0
    call ClearScr
; decompress menu code & data to &8100
    ld hl,MenuCompressed
    ld de,&8100
    call deexo
    call wait_exit
    di
    call GetROMChars
    call Start_Interrupt
    ld a,(LastGameScore+6) ; get last round number from last game
    cp a,13
    jr nz,SkipEndingDisplay
; game was just won, so display game ending
    ld hl,MenuCols+3
    ld de,MenuColourPtr
    call DisplayGameEnding
    ld hl,BlankMenuCols+3
    ld (MenuColourPtr),hl
    call wait_int ; give colour set time to take effect before clearing screen
    ld a,&c0 ; clear the screen after the colours are blank to ready screen for menu
    call ClearScr
; carry on with menu display
.SkipEndingDisplay
    call DrawTitleLogo
; set menu to visible
    ld hl,MenuCols+3
    ld (MenuColourPtr),hl
; go to menu routine, when exits, will be game start
    call ProcessMainMenu
; game started, do init for game
    ld hl,BlankMenuCols+3
    ld (MenuColourPtr),hl
    call wait_int
    ld a,&c0
    call ClearScr

; write score line
    call Init_Display

    ld a,&80
    call ClearScr

; reset game vars
    call ResetGameVariables
; initial display setup of lives
    call UpdateLivesDisplay

; ply bullet co-ord init
    ld hl,PlayerYX+5
    ld de,PlayerYX+7
    ld (hl),128
    ld bc,10
    ldir

; fill scr addr lists with dummy addr
    ld hl,EBLowAddr
    ld (hl),0
    ld de,EBLowAddr+1
    ld bc,&c2f ; includes &100 of buffer
    ldir
    ld hl,EPlyLowAddr
    ld (hl),0
    inc l
    ld (hl),&70
    ld de,EPlyLowAddr+2
    ld bc,36 ; includes &100 of buffer
    ldir
    ld hl,EPlyLowAddr
    ld de,EPlyHighAddr
    ld bc,36 ; includes &100 of buffer
    ldir

; clear co-ords for enemy bullets
    ld hl,EBYsp
    ld de,EBYsp+1
    ld (hl),0
    ld bc,&5ff
    ldir

; if testing final round, set all previous dragons killed to test endings
if StartRound = 11
    ld a,11
    ld (DragonsDestroyed),a
endif
; set up trigger for first round
    ld a,20
    ld (RoundEnd),a

; ensure at beginning of a frame before triggering interrupt switch
    call Wait_int
    ld a,1
    ld (Int_6_Title_StartTrigger+1),a

; now begin main game loop
.MainLoop
    call Wait_int

; these next two need to occur before second interrupt, so perform immediately after wait_int
    call EnemyCollision ; see DA_Collision6.asm
    call UpdateScoring ; see DA_Display6.asm

    call ClearNBullets ; see DA_EnemyBulletDisplay1.asm
    call MoveSprites ; see DA_Move8.asm
; must move sprites before this, also calcs new addresses
    call ClearNSprites ; see DA_EnemySpriteDisplay1.asm

; now printed under interrupt, sprites take 38 lines to display
; if not under interrupt, this is taking 53 scan lines
;    call PrintEnemySprites

    call MoveNBullets ; see DA_Move8.asm
    call PrintBullets ; see DA_EnemyBulletDisplay1.asm

    call CheckFire ; see DA_Fire6.asm

; now check player status for game exit, and repeat MainLoop if game is to continue
    ld a,(KeepPlayerInv)
    or a
    call nz,MonitorPlayerState
    ld a,(RoundEnd)
    or a
    call nz,CheckRoundComplete
; check if time to exit game
    ld a,(PlayerHitTimer)
    cp a,128
    jr c,MainLoop
; player has died or completed final round, so pause for a time, then exit to menu loop
    inc a
    ld (PlayerHitTimer),a
    cp a,128+58 ; 3.5 seconds
    jr c,MainLoop
; timer expired, copy score, round and set menu interrupt and go to main menu
    ld hl,Score
    ld de,LastGameScore
    ld bc,6
    ldir ; score copied
    ld a,(Round)
    ld (de),a ; round copied
    ld (Int_6_Game_EndTrigger+1),a ; any non zero value will do
    ld a,(DragonsDestroyed)
    inc e
    ld (de),a ; store dragons destroyed from buffer in case game completed
    call wait_int
    jp TitleStart

; set colour routines
.SetColoursMode1
    ld bc,&7F04
    jr SetColsCommon
.SetColours
    ld bc,&7F10
.SetColsCommon
    xor a
.SetColLp
; HL points to list
    ld d,(hl)
    dec c
    out (c),c
    out (c),d
    dec hl
    cp a,c
    ret z
    jr SetColLp

.CheckRoundComplete
; if called, a holds current non zero value of RoundEnd
    dec a
    ld (RoundEnd),a
    ret nz
; if round end hits 0, time to trigger next round
    call GetRoundData
; check a and exit game loop if last round complete, a/=0
    or a
    ret z
; finished last round, time to get player to exit, if player not dead
    ld a,(Lives)
    or a
    ret z
; player not all out of lives
    ld (PlayerAutoPilot),a ; set a to non zero to trigger autopilot
    ld a,13
    ld (Round),a ; used as trigger for game won sequence
    ret

.MonitorPlayerState
; if called, player is set to remain invulnerable, so keep PlayerHitTimer in certain range
    ld a,(PlayerHitTimer)
    cp a,128
    jr nc,MonPlyStateAbort
; player needs to be kept invulnerable
    cp a,60
    ret nc ; PlayerHitTimer is still high enough
    add a,40
    ld (PlayerHitTimer),a
    ret
.MonPlyStateAbort
; if PlayerHitTimer has bit 7 set during this process, it means the player lost their last life
; just prior to destruction of final boss, so this process should be aborted
    xor a
    ld (KeepPlayerInv),a
    ret

read "DA_int13_split_12songs.asm"
read "DA_EnemyBulletDisplay1.asm"
read "DA_EnemySpriteDisplay1.asm"
read "DA_PlayerSpriteDisplay10.asm"
read "DA_Move8.asm"
read "DA_Fire6.asm"
read "DA_collision6.asm"
read "DA_FastShot4.asm"
read "DA_SoundFX3.asm"
read "DA_RoundData7.asm"

.SpritesStart
read "Player_Compiled_Frames4.asm"
.SpritesEnemyStart
read "SpritesBossFrames8_reduced.asm"
.SpritesEnd
read "ArkosTrackerPlayer_CPC_MSX.asm"

; if game ran from assembler, need list of colours for initial set up
if InitGame = 1
.LoaderCols
    defb &44,&54,&4e,&44
    defb &4b,&58,&5f,&5b
    defb &43,&43,&54,&43
    defb &53,&43,&43,&43
endif

read "DA_Display6.asm"
read "GameDisplayFont_Compiled3_25.asm"
read "TimeBonusText3.asm"

; reset game variables
.ResetGameVariables
    xor a
    ld (ScoreUpdater),a
    ld (MNSFinalBossHeadOS+1),a ; set final boss centre head start offset
    inc a
    ld (Multiplier),a ; multiplier starts at 1
    ld a,StartRound
    ld (Round),a ; starts at zero, but is configurable for testing
    ld a,&c0
    ld (WorkScr),a ; first workscr is high screen at &c000
    ld a,&7f ; put dummy values in last displayed variables so they'll be updated immediately
    ld (MultiplierDsp),a
    ld (RoundTimeDsp),a
    ld a,(StartLives) ;3 ; initial lives, might be configurable later
    ld (Lives),a
    ld a,10 ; blank the multiplier display list
    ld (BCD_Round_Mult),a
    ld (BCD_Round_Mult+1),a
; initialise final boss specific variables
    ld a,2
    ld (FinalBossActiveHead),a ; set first head
    ld a,224
    ld (FinalBossHitCheck),a ; set first head change marker
; player start location
    ld a,62
    ld (PlayerTestX),a
    ld a,200
    ld (PlayerTestY),a
; check the control option and set key reading code to appropriate jump
    ld a,(OptionsByte)
    ld c,a ; preserve options byte
    rra
    jr nc,RGVNotJoystick
    ld hl,UseJoystick
    jr RGVSetControlCommon
.RGVNotJoystick
    rra
    jr nc,RGVNotKeyboard
    ld hl,UseKeyboard
    jr RGVSetControlCommon
.RGVNotKeyboard
    rra
    jr nc,RGVNotCursor
    ld hl,UseCursor
    jr RGVSetControlCommon
.RGVNotCursor
; must be multiplay
    ld hl,UseMultiPlay
.RGVSetControlCommon
; write jump for selected control method to key scanning routine
    ld (ReadJoystick+1),hl
; c contains option byte, so check bit 3
    bit 3,c
    jr z,RGVNormalFire
; has inverted fire mode
    ld a,16
    jr RGVSkipNormalFire
.RGVNormalFire
    xor a
.RGVSkipNormalFire
    ld (RdJyInvert+1),a
    ret

read "deexo.asm"
read "Exo/Menu_Compressed21_Exo.asm"

list
.EndGameCode
nolist
; LowScrLUTable = &6000
    defs LowScrLUTable-EndGameCode-&b0-&ea;-378;-343 ; &b0 for game display font

read "music/DASoundFX2.asm"
read "TimeBonusDigits3.asm" ; starts at &5f50
read "ScrAddr_32_Interleaved2.asm" ; starts at &6000
read "ScrAddr_32_2.asm"

; high score table entries
.HighScoreTableBase
;high score entry 1
    defb 128+32
    defb #31,#20,#44,#72,#61,#67,#6f,#6e
    defb #20,#41,#74,#74,#61,#63,#6b,#20
    defb #20,#2e,#2e,#2e,#31,#30,#30,#30
    defb #30,#30,#30,#30,#30,#20,#20,#36
    defb 128+1,2,192+29,1,128+2,1,5
    defb 255
    defw 1,0,0 ; 47 bytes per entry
;high score entry 2
    defb 128+32
    defb #32,#20,#44,#72,#61,#67,#6f,#6e
    defb #20,#41,#74,#74,#61,#63,#6b,#20
    defb #20,#2e,#2e,#2e,#2e,#31,#30,#30
    defb #30,#30,#30,#30,#30,#20,#20,#35
    defb 128+1,2,192+29,1,128+2,1,5
    defb 255
    defw 0,1000,0 ; 47 bytes per entry
;high score entry 3
    defb 128+32
    defb #33,#20,#44,#72,#61,#67,#6f,#6e
    defb #20,#41,#74,#74,#61,#63,#6b,#20
    defb #20,#2e,#2e,#2e,#2e,#2e,#31,#30
    defb #30,#30,#30,#30,#30,#20,#20,#34
    defb 128+1,2,192+29,1,128+2,1,5
    defb 255
    defw 0,100,0 ; 47 bytes per entry
;high score entry 4
    defb 128+32
    defb #34,#20,#44,#72,#61,#67,#6f,#6e
    defb #20,#41,#74,#74,#61,#63,#6b,#20
    defb #20,#2e,#2e,#2e,#2e,#2e,#2e,#31
    defb #30,#30,#30,#30,#30,#20,#20,#33
    defb 128+1,2,192+29,1,128+2,1,5
    defb 255
    defw 0,10,0 ; 47 bytes per entry
;high score entry 5
    defb 128+32
    defb #35,#20,#44,#72,#61,#67,#6f,#6e
    defb #20,#41,#74,#74,#61,#63,#6b,#20
    defb #20,#2e,#2e,#2e,#2e,#2e,#2e,#2e
    defb #31,#30,#30,#30,#30,#20,#20,#32
    defb 128+1,2,192+29,1,128+2,1,5
    defb 255
    defw 0,1,0 ; 47 bytes per entry
.LastGameScore ; at game end the following data is copied from temp var space to here for use by menu code
    defw 0,0,0 ; used for checking if high score entry required from last game
    defb 0 ; round at game exit
    defb 0 ; number of dragons killed
read "GameDisplayFont_Compiled3_610.asm"
; the follow routine called once at startup to move data following this routine to correct
; place in memory
.MoveBulletDirTable
; temorary code run once at start of game
; copy tables for sprite frame lookup, bullet move direction tables
    ld hl,BossFrameLUTemp
    ld de,BossFrameLU
    ld bc,&300
    ldir
    ret

; the following tables are shifted at game startup
.BossFrameLUTemp
    defw BossFrame1
    defw BossFrame0
    defw BossFrame1 ;3
    defw BossFrame0 ;2
    defw BossFrame5
    defw BossFrame4
    defw BossFrame9 ;7
    defw BossFrame8 ;6
    defw BossFrame9
    defw BossFrame8
    defw BossFrame11
    defw BossFrame10
    defw BossFrame13
    defw BossFrame12
    defw BossFrame15
    defw BossFrame14
    defw BossFrame17
    defw BossFrame16
    defw BossFrame19
    defw BossFrame18
    defw BossFrame21
    defw BossFrame20
    defw BossFrame23 ; explosion start x
    defw BossFrame22
    defw BossFrame23
    defw BossFrame22
    defw BossFrame25
    defw BossFrame24
    defw BossFrame25
    defw BossFrame24
    defs 4 ; now 64 bytes
    defw FastFont0
    defw FastFont1
    defw FastFont2
    defw FastFont3
    defw FastFont4
    defw FastFont5
    defw FastFont6
    defw FastFont7
    defw FastFont8
    defw FastFont9
    defw FastFont10

    defs 8

read "GameDisplayFont_Compiled3_01.asm"

.ShrapnelDirListTemp
db #00,#7f,#1a,#7d,#34,#74,#4b,#67
db #5f,#55,#6e,#40,#79,#27,#7f,#0d
db #7f,#f2,#79,#d8,#6e,#c0,#5f,#aa
db #4b,#98,#34,#8b,#1a,#82,#00,#80
.ShotgunDirListTemp
db #5a,#5a,#63,#50,#6b,#45,#72,#3a
db #77,#2d,#7b,#21,#7e,#14,#7f,#06
db #7f,#f9,#7e,#eb,#7b,#de,#77,#d2
db #72,#c5,#6b,#ba,#63,#af,#5a,#a5
.BulletDirsTemp
read "BulletDirs2.asm"
list
.endcode