    include "spelunk-constants.asm"
    include "autogenerated/spelunk-sound-sfx-table.asm"

    org #4000   ; Start in the 2nd slot

;-----------------------------------------------
    db "AB"     ; ROM signature
    dw Execute  ; start address
    db 0,0,0,0,0,0,0,0,0,0,0,0
;-----------------------------------------------


;-----------------------------------------------
; Code that gets executed when the game starts
Execute:
    ; init the stack:
    ld sp,#F380
    ; reset some interrupts to make sure it runs in some MSX computers 
    ; with disk controllers installed in some interrupt handlers
    di
    ld a,#C9
    ld (HKEY),a
;    ld (TIMI),a
    ei

    call setupROMRAMslots

    ; Silence, init keyboard, and clear config:
    xor a
    ld hl,config_rope_jump
    ld (hl),a
    inc hl
    ld (hl),a
    inc hl
    ld (hl),a
    inc hl
    ld (CLIKSW),a
    ; Change background colors:
    ld (BAKCLR),a
    ld (BDRCLR),a
    call CHGCLR
   
    ld a,2      ; Change screen mode
    call CHGMOD

    ;; 16x16 sprites:
    ld bc,#e201  ;; write #e2 in VDP register #01 (activate sprites, generate interrupts, 16x16 sprites with no magnification)
    call WRTVDP

    ;; clear the screen, set up music interrupt, and decompress data:
    call FILLSCREEN
    call SETUP_MUSIC_INTERRUPT
    ; player sprites are not decompressed here, since we need their space for the level patches
    ; they are decompressed in the interlevel screen
    ld hl,itemsprites_pletter
    ld de,item_sprites
    call pletter_unpack
    ld hl,enemysprites_pletter
    ld de,enemy_sprites
    call pletter_unpack

    ld hl,compressed_sfx
    ld de,decompressed_sfx
    call pletter_unpack

    call CheckIf60Hz
    ld (isComputer50HzOr60Hz),a

    jp state_intro


;-----------------------------------------------
; additional assembler files
; ource code:
    include "spelunk-auxiliar.asm"
    include "spelunk-input.asm"
    include "spelunk-sound.asm"
    include "spelunk-gfx.asm"
    include "spelunk-player.asm"
    include "spelunk-player-bullets.asm"
    include "spelunk-explosions.asm"
    include "spelunk-items.asm"
    include "spelunk-enemies.asm"
    include "spelunk-maps.asm"
    include "spelunk-gui.asm"
    include "spelunk-pcg.asm"
    include "spelunk-intro.asm"
    include "spelunk-title.asm"
    include "spelunk-config.asm"
    include "spelunk-interlevel.asm"
    include "spelunk-gameloop.asm"
    include "spelunk-ending.asm"
    include "spelunk-letters.asm"
EndofCode:

playersprites_pletter:
    incbin "autogenerated/spelunk-playersprites.plt"
itemsprites_pletter:
    incbin "autogenerated/spelunk-itemsprites.plt"
enemysprites_pletter:
    incbin "autogenerated/spelunk-enemysprites.plt"
item_selection_sprite:
    db #ff,#80,#80,#80,#80,#80,#80,#80,#80,#80,#80,#80,#80,#80,#80,#ff
    db #ff,#01,#01,#01,#01,#01,#01,#01,#01,#01,#01,#01,#01,#01,#01,#ff
EndOfSpriteData:

    include "autogenerated/pcg-jungle-chunks.asm"
    include "autogenerated/pcg-outerruins-chunks.asm"
    include "autogenerated/pcg-innerruins-chunks.asm"
EndOfChunkData:

patterns_title_pletter:
    incbin "autogenerated/patterns-title.plt"
patterns_jungle_pletter:
    incbin "autogenerated/patterns-jungle.plt"
    ; note that "patches" can be at most 2KB when decompressed, since that's the size of buffer2
patterns_title_outer_ruins_patch:
    incbin "autogenerated/patterns-jungle-outer-ruins-patch.plt"
patterns_title_inner_ruins_patch:
    incbin "autogenerated/patterns-outer-inner-ruins-patch.plt"
EndOfPatternData:

music_intro:
    incbin "autogenerated/spelunk-intro-song.plt"
music_letter:
    incbin "autogenerated/spelunk-letter-song.plt"
music_gamestart:
    incbin "autogenerated/spelunk-gamestart-song.plt"
music_gameover:
    incbin "autogenerated/spelunk-gameover-song.plt"
music_ingame:
    incbin "autogenerated/spelunk-ingame-song.plt"
music_ingame2:
    incbin "autogenerated/spelunk-ingame2-song.plt"

Piano_instrument_profile:
    db 4,8,12,11,10,10,9,9,8,8,7,7,6,#ff
Wind_instrument_profile:
    db 0,3,6,8,10,11,12, #ff
SquareWave_instrument_volume:   equ 12
compressed_sfx:
    incbin "autogenerated/spelunk-sound-sfx.plt"
EndOfSoundData:

intro_data:
    incbin "autogenerated/intro-data.plt"
title_data:
    incbin "autogenerated/title-data.plt"
letter1:
    incbin "autogenerated/letter1.plt"
letter2:
    incbin "autogenerated/letter2.plt"
letter3:
    incbin "autogenerated/letter3.plt"
letter4:
    incbin "autogenerated/letter4.plt"



;-----------------------------------------------
; Game variables to be copied to RAM
ROMtoRAM_compressed:
    incbin "autogenerated/spelunk-rom-to-ram.plt"

fallYOffsetTable:
    db 1,1,1,2,2,2,3,3,3,4
fallYOffsetTableEnd:
fallYOffsetTable_belt:
    db 1,1,1,2
fallYOffsetTable_beltEnd:

jumpYOffsetTable:
    db 4,4,4,4,3,3,3,3,2,2,2,1,1,1,0,0,0,0        ; 37 total jump height
jumpYOffsetTableEnd:
jumpYOffsetTable_boots:
    db 4,4,4,4,4,4,4,3,3,3,3,3,2,2,2,2,1,1,1,0,0,0,0        ; 53 total jump height
jumpYOffsetTable_bootsEnd:
hurtYOffsetTable:
    db 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,2,3,3,3,4
hurtYOffsetTableEnd:

swimmingYOffsetTable:   ; length 32
    db  1, 1, 0, 1 
    db  0, 0, 0,-1
    db -1,-1, 0,-1
    db  0, 0, 0, 1
swimmingYOffsetTableEnd:

empty_item_patterns:
    db 123,123,123,123
machete_patterns:
    db 0,99,95,0
bomb_patterns:
    db 116,117,118,119
rope_patterns:
    db 28,29,30,31
shield_patterns:
    db 108,109,110,111
bow_patterns:
    db 120,0,121,122
arrow_patterns_gui:
    db 41,0,40,0
arrow_patterns:
    db 41,40
stone_patterns_gui:
    db 0,0,34,0
stone_pattern:  equ   34
boots_patterns:
    db 96,0,97,98
belt_patterns:
    db 100,101,102,103
scubamask_patterns:
    db 112,113,114,115
idol_patterns:
    db 104,105,106,107
phaser_patterns:
    db 1,2,3,4
boulder_patterns:
    db 242,244,245,247
button_up_patterns:
    db 0,0,5,6
button_down_patterns:
    db 0,0,7,8
door_patterns:
    db 254,255,248,249,248,249,252,253

; the last two numbers are for direction "none" (when we are dropping a bomb)
player_bullet_initial_x_offset:
    db -9,-5,4,3,11,15,  8,0
player_bullet_initial_y_offset:
    db 3,-3,-7,-7,-3,3,  6,6

phaser_sprite_y_offsets:
    db 0,0 ; ,-8,-8,0,0
bow_sprite_x_offsets:
phaser_sprite_x_offsets:
    db -8,-8,0,0,8,8

phaser_bullet_speed:
    db -4,  0
    db -3, -3
    db  0, -4
    db  3, -3
    db  4,  0

thrown_stone_bomb_speed:
    db -2,  0
    db -2, -2
    db  0, -3
    db  2, -2
    db  2,  0
    db  0,  0

thrown_rope_speed:
    db -2, -1
    db -2, -4
    db  0, -5
    db  2, -4
    db  2, -1

arrow_bullet_speed:
    db -5, -1
    db -4, -4
    db  0, -5
    db  4, -4
    db  5, -1


;-----------------------------------------------
; Definition of the tile animations:
; water animation:
animation_water:  ; triggered by tile 129
  db 2,3  ; animation tempo and number of frames (tempo is actually multiplied by 4 in game)
  db 129,130,131  ; sequence of patterns
animation_torch:  ; triggered by tile 13
  db 1,4  ; animation tempo and number of frames (tempo is actually multiplied by 4 in game)
  db 13,14,15,14  ; sequence of patterns
animation_computer:  ; triggered by tile 146
  db 4,2
  db 146,147
animation_beam_emitter:  ; triggered by tile 124
  db 1,2
  db 124,125
animation_beam:  ; triggered by tile 126
  db 1,2
  db 126,127

largeExplosion_tiles:
    db "BCD","FGJ","KNO"

smallExplosion_tiles:
    db "PQR"


;-----------------------------------------------
; PCG constants:
pcg_chunk_type_table:
  db PCG_CHUNK_FG_TYPE5,PCG_CHUNK_FG_TYPE6,0
  db PCG_CHUNK_FG_TYPE3,PCG_CHUNK_FG_TYPE1,PCG_CHUNK_FG_TYPE2
  db 0,PCG_CHUNK_FG_TYPE4,PCG_CHUNK_FG_TYPE5

pcg_item_supply:
  db 0,0,0,ITEM_BOMB,ITEM_ROPE,ITEM_ARROW
pcg_item_supply_end:

pcg_item_any_jungle:
pcg_item_any_ruins:
  db 0,0,0,0,0,ITEM_BOMB,ITEM_BOMB,ITEM_BOMB,ITEM_ROPE,ITEM_ROPE,ITEM_ROPE,ITEM_ARROW,ITEM_ARROW,ITEM_ARROW
  db ITEM_SHIELD,ITEM_BOW,ITEM_BOW,ITEM_BOOTS,ITEM_SCUBAMASK,ITEM_BOULDER,ITEM_BOULDER
pcg_item_any_jungle_end:
  db ITEM_PHASER,ITEM_PHASER,ITEM_BELT,ITEM_BELT
pcg_item_any_ruins_end:

pcg_item_good_item_jungle:
pcg_item_good_item_ruins:
  db ITEM_SHIELD,ITEM_BOW,ITEM_BOOTS,ITEM_SCUBAMASK
pcg_item_good_item_jungle_end:
  db ITEM_PHASER,ITEM_PHASER,ITEM_BELT,ITEM_BELT
pcg_item_good_item_ruins_end:


;-----------------------------------------------
; Level progression table
; data is: 
;   - width log 2, wdth, height, width in patterns, height in patterns, 
;   - enemies (10 bytes): probability of each enemy type (9 bytes) + minimum number of enemies (1 byte)
;   - chunk table ptr (2 bytes)
level_1:
  db  6, 64,32,  4,2     
  db  4, 0,3,32, 0, 0, 0, 0, 0,    4
  dw jungle_chunk_table
level_2:
  db  6, 64,64,  4,4
  db  8, 0,3,32, 0, 0, 0, 0, 0,    8
  dw jungle_chunk_table
level_3:
  db  7,128,32,  8,2
  db 12,24,3,32,40, 0, 0, 0, 0,   8
  dw jungle_chunk_table
level_4:
  db  7,128,32,  8,2
  db 12,16,3,24,32, 0, 0, 0, 0,   10
  dw jungle_chunk_table

level_5:
  db  6, 64,64,  4,4
  db  0, 0,3,32,48,32, 0, 0, 0,    8
  dw outerruins_chunk_table
level_6:
  db  7,128,32,  8,2
  db  0, 0,3,32,40,32,64, 0, 0,    8
  dw outerruins_chunk_table
level_7:
  db  6, 64,64,  4,4
  db  0, 0,3,24,32,32,32, 0, 0,    10
  dw outerruins_chunk_table
level_8:
  db  7,128,32,  8,2
  db  0, 0,3,24,32,24,24, 0, 0,    10
  dw outerruins_chunk_table

level_9:
  db  6, 64,64,  4,4
  db  0, 0,4, 0, 0,32,32,32,48,    8
  dw innerruins_chunk_table
level_10:
  db  6, 64,64,  4,4
  db  0, 0,4, 0, 0,32,32,32,32,    8
  dw innerruins_chunk_table
level_11:
  db  6, 64,64,  4,4
  db  0, 0,4, 0, 0,24,24,24,32,    10
  dw innerruins_chunk_table
level_12:
  db  7,128,32,  8,2
  db  0, 0,6, 0, 0,24,24,24,32,    10
  dw innerruins_chunk_table


;-----------------------------------------------
; Messages, and other title/splash screen data
braingames_text:
  db "BRAIN GAMES PRESENTS"
braingames_text_end:

intro_bubble1_text:
  db "  ELAINE  "
  db " SINCLAIR?"

intro_bubble2_text:
  db "   YES,   "
  db "THAT'S ME!"

intro_bubble3_text:
  db "MAIL FROM "
  db "MR XAVIER!"

ending_bubble2_text:
  db " ANOTHER  "
  db "  LETTER? "

ending_bubble3_text:
  db "NOT ONE,  "
  db "   THREE!!"

ending_bubble4_text:
  db "MAN! HE'S "
  db "TALKATIVE!"

ending_text:
  db "THANKS FOR PLAYING!"
ending_text_end:

ending_text2:
  db "TOTAL TIME 00:00"
ending_text2_end:

gamestart_text:
  db "SPACE TO START"
gamestart_text_end:

title_config_text:
  db "M TO CONFIGURE"
title_config_text_end:

credits_text:
  db "SANTI ONTANON 2017"
credits_text_end:

interlevel_text:
  db "LEVEL 1-1"
interlevel_text_end:

interlevel_text2:
  db "WAIT..."
interlevel_text2_end:

gameover_text:
  db "GAME OVER"
gameover_text_end:

;config_text1a:
;  db "ROPE JUMP: DOUBLE TAP UP"
;config_text1a_end:
;config_text1b:
;  db "ROPE JUMP: TRIGGER A.   "
;config_text1b_end:
;config_text2a:
;  db "MACHETE AUTOSELECT: ON "
;config_text2a_end:
;config_text2b:
;  db "MACHETE AUTOSELECT: OFF"
;config_text2b_end:

config_text1:
  db "ROPE JUMP:"
config_text1_end:
config_text1a:
  db "DOUBLE TAP UP"
config_text1a_end:
config_text1b:
  db "TRIGGER A.   "
config_text1b_end:

config_text2:
  db "MACHETE AUTOSELECT:"
config_text2_end:
config_text2a:
config_text3a:
  db "ON "
config_text2a_end:
config_text3a_end:
config_text2b:
config_text3b:
  db "OFF"
config_text2b_end:
config_text3b_end:

config_text3:
  db "IN-GAME MUSIC:"
config_text3_end:


endOfROM:
    ds ((($-1)/#4000)+1)*#4000-$

;-----------------------------------------------
; RAM:
    org #c000   ; RAM goes to the 4th slot

RAM:
; Space for ROMtoRAM:
sprite_attributes:                  ds virtual NUMBER_OF_SPRITES_USED*4
player_selected_item:               ds virtual 1
;previous_trigger1:                  ds virtual 1
game_cycle:                         ds virtual 1
player_input_buffer:                ds virtual 4    ; current input, previous input, new keys pressed, double clicks
player_input_double_click_state:    ds virtual 2
player_input_numbers_buffer:        ds virtual 1    ; for selecting items with numbers
player_jump_x_inertia:              ds virtual 1    ; this is 1 if the player is jumping right, -1 if left, and 0 is jumping upwards
player_health:                      ds virtual 1
player_inventory:                   ds virtual INVENTORY_SIZE*2 ; for each item, we have 1 byte for item type, and 1 for number of items (max is 9)
current_level_section:              ds virtual 1
current_level:                      ds virtual 1
accumulated_time:                   ds virtual 2    ; time in seconds used to complete the whole game
HUD:                                ds virtual 32*2

rand:
; Notice that I do not initialize these two variables to anything, since if they contain garbage at program start, that's even better for random number generation:
randSeedIndex:                      ds virtual 1    ; this is used to store which byte of the randData should we update next when setting the seed
randData:                           ds virtual 2    ; state of the random number generator

isComputer50HzOr60Hz:               ds virtual 1    ; 0 is 50Hz, and 1 is 60Hz

game_state:                         ds virtual 1
config_rope_jump:                   ds virtual 1
config_machete_autoselect:          ds virtual 1
config_music:                       ds virtual 1
config_selected:                    ds virtual 1

buffer:
map:                                ds virtual MAX_MAP_SIZE
map_width_log2:                     ds virtual 1    ; this is the log 2 of the width of the map
map_width:                          ds virtual 1
map_height:                         ds virtual 1
map_n_enemies:                      ds virtual 1
map_enemies:                        ds virtual MAX_MAP_ENEMIES*ENEMY_STRUCT_SIZE
map_n_items:                        ds virtual 1
map_items:                          ds virtual MAX_MAP_ITEMS*ITEM_STRUCT_SIZE
map_n_animations:                   ds virtual 1
map_animations:                     ds virtual ANIMATION_STRUCT_SIZE*MAX_MAP_ANIMATIONS ; each animation has "pointer to animation definition", "pointer to tile", "timer", and "current frame"

; I put these pcg variables here, since they need to be zeroed:
pcg_chunk_map_buffer:               ds virtual 16   ; this is the maximum size of any of the generated maps (8x2 or 4x4)
pcg_button_in_map:                  ds virtual 1    ; there can only be one button in a room
map_end:

; PCG variables:
pcg_add_pinecones:                  ds virtual 1
pcg_add_monkeys:                    ds virtual 1
pcg_add_piranhas:                   ds virtual 1
pcg_add_scorpions:                  ds virtual 1
pcg_add_bee_nests:                  ds virtual 1
pcg_add_snakes:                     ds virtual 1
pcg_add_mayas:                      ds virtual 1
pcg_add_aliens:                     ds virtual 1
pcg_add_sentinels:                  ds virtual 1

pcg_minimum_number_of_enemies:      ds virtual 1  ; if a map is generated with less than this, it'll be regenerated

pcg_width_in_chunks:                ds virtual 1
pcg_height_in_chunks:               ds virtual 1
;pcg_chunk_buffer:                   ds virtual 540  ; the maximum size of a chunk
pcg_current_chunk_width:            ds virtual 1
pcg_current_map_pointer:                            ; since "pcg_current_map_pointer" will never be used together with "pcg_current_chunk_y/x", I reuse the RAM space
pcg_current_chunk_y:                ds virtual 1
pcg_current_chunk_x:                ds virtual 1
pcg_current_chunk_type:             ds virtual 1
pcg_exit_y_coordinate:              ds virtual 1
pcg_exit_x_coordinate:              ds virtual 1
pcg_path_length:                    ds virtual 1
pcg_n_items_buffer:                 ds virtual 1    ; stores the value of 'map_n_items' before starting step 6 of PCG, in case we need to backtrack
pcg_n_monkeys_per_column:           ds virtual 1

RAM_to_zero_on_new_map:
need_to_redraw_map:                 ds virtual 2    ; if 0 -> redraw!
map_redrawing_cycle:                ds virtual 2    ; when we do not need to redraw the whole map, at least parts of it are redrawn to keep animations playing. This variable controls which part will be redrawn each cycle
player_y:                           ds virtual 2    ; 1st byte is the tile coordinate, and 2nd the pixel
player_x:                           ds virtual 2    ; 1st byte is the tile coordinate, and 2nd the pixel
player_state:                       ds virtual 1
player_state_timer:                 ds virtual 1
player_machete_timer:               ds virtual 1    ; in some states (such as jump), machete is not a new state, but just an add-on, controlled by this timmer
player_no_climb_timer:              ds virtual 1    ; player can grab to a rope/vine only if this is 0
player_vine_x_coordinate:           ds virtual 1    ; the tile x coordinate at which the vine/rope the player is holding is located
player_inmune_timer:                ds virtual 1
player_push_timer:                  ds virtual 1    ; for how long have we been trying to push at a wall
player_directional_weapon_direction:ds virtual 1    ; 0: left, 1: left-up, 2: up (facing left), 3: up (facing right), 4: right-up, 5: right
player_bullets:                     ds virtual MAX_PLAYER_BULLETS*PLAYER_BULLET_STRUCT_SIZE
bomb_explosions:                    ds virtual MAX_EXPLOSIONS*EXPLOSION_STRUCT_SIZE

tmp_position_buffer:                ds virtual 2    ; a buffer to store a previous position of an object

next_enemy_sprite_slot:             ds virtual 1    ; next sprite slot
next_enemy_sprite_slot_ptr:         ds virtual 2
n_enemy_sprites_last_frame:         ds virtual 1
enemy_sprite_patterns_cache:        ds virtual ENEMY_SPRITE_SLOTS*2 ; a table that stores which enemy sprite patterns are loaded in VRAM to avoid loading them again and again
enemy_sprite_patterns_cache_next:   ds virtual 1            ; if we need to load a new one, which is the next one to remove
RAM_to_zero_on_new_map_end:

target_scroll_map_y:                ds virtual 1
target_scroll_map_x:                ds virtual 1

scroll_map_y:                       ds virtual 1
scroll_map_x:                       ds virtual 1
scroll_y_limit:                     ds virtual 1
scroll_x_limit:                     ds virtual 1

; decompressed sprites:
buffer3:
player_sprites:                     ds virtual 1920
item_sprites:                       ds virtual 800
enemy_sprites:                      ds virtual 1888

; sound variables:
Music_tempo:                        ds virtual 1
SFX_play:                           ds virtual 1
MUSIC_play:                         ds virtual 1
MUSIC_tempo_counter:                ds virtual 1
MUSIC_instruments:                  ds virtual 3
MUSIC_channel3_instrument_buffer:   ds virtual 1    ;; this stores the instrument of channel 3, which is special, since SFX might overwrite it
MUSIC_start_pointer:                ds virtual 2  
SFX_pointer:                        ds virtual 2
MUSIC_pointer:                      ds virtual 2
MUSIC_repeat_stack_ptr:             ds virtual 2
MUSIC_repeat_stack:                 ds virtual 4*3
MUSIC_instrument_envelope_ptr:      ds virtual 3*2
SFX_priority:                       ds virtual 1  ; the SFX from the game have more priority than those triggered by music

buffer2:
music_buffer:                       ds virtual MAX_SONG_SIZE

decompressed_sfx:                   ds virtual SFX_end_ptr-SFX_start_ptr  ; should be 816 bytes

endOfRAM:

