'
'                             
'                           
'           
'                  
'             
'                             
'                             
'                               
' Original PICO-8 version is by 
'   Matt Thorson & Noel Berry   
'                               
' Ported to SMILEBASIC by OBONO 
'
'
' How to Play (あそびかた)
'
'  :Move  (いどう)
'   :Jump  (ジャンプ)
'   :Dash  (ダッシュ)
'   :Pause (ポーズ)
' +:Back to Title screen
'           (タイトルがめんにもどる)

'  VARIABLES 

OPTION STRICT

' GLOBAL
DIM TM_F   ' FRAMES OF PLAYING TIME
DIM TM_S   ' SECONDS OF PLAYING TIME
DIM TM_M   ' MINUTES OF PLAYING TIME
DIM DEATHS ' DEATHS COUNTER
DIM MAX_JP ' MAX DASH-JUMP COUNT
DIM ST_GM  ' IS STARTING GAME
DIM ST_GFL ' FLASHING COUNTER FOR GAME START
DIM ROOM_X ' ROOM X
DIM ROOM_Y ' ROOM Y
DIM NW_BG  ' IS NEW BACKGROUND
DIM FREEZE ' FREEZE TIMER
DIM SHAKE  ' SCREEN SHAKING TIMER
DIM SHX    ' SHAKE X
DIM SHY    ' SHAKE Y
DIM RST_RM ' WILL RESTART ROOM
DIM RST_DL ' DELAY OF RESTART ROOM
DIM GT_ICG[30]' GOT FRUIT ARRAY
DIM HS_DSH ' HAS DASHED
DIM SFX_TM ' SFX TIMER
DIM HS_KEY ' HAS KEY
DIM PZ_PLY ' PAUSE PLAYER
DIM KL_PLY ' KILLING PLAYER
DIM FL_BG  ' IS FLASHING BACKGROUND
DIM BGM_TM ' MUSIC TIMER

' BUTTONS
DIM K_LF=0 ' LEFT
DIM K_RT=1 ' RIGHT
DIM K_UP=2 ' UP
DIM K_DN=3 ' DOWN
DIM K_JP=4 ' JUMP
DIM K_DS=5 ' DASH

'BUTTON CODES
VAR BTN_UP% = 1
VAR BTN_DOWN% = 2
VAR BTN_LEFT% = 4
VAR BTN_RIGHT% = 8
VAR BTN_A% = 16
VAR BTN_B% = 32
VAR BTN_X% = 64
VAR BTN_Y% = 128
VAR BTN_L% = 256
VAR BTN_R% = 512

' PLAYER
DIM PLY_MD    ' MODE
DIM PLY_X     ' X
DIM PLY_Y     ' Y
DIM PLY_S     ' SPRITE
DIM PLY_FX    ' FLIP X
DIM PLY_TX    ' TARGET X
DIM PLY_TY    ' TARGET Y
DIM PLY_SX    ' SPEED X
DIM PLY_SY    ' SPEED Y
DIM PLY_RX    ' FRACTION X
DIM PLY_RY    ' FRACTION Y
DIM PLY_ST    ' STATE
DIM PLY_DL    ' DELAY
DIM PLY_PJ    ' PREVIOUS JUMP BUTTON STATE
DIM PLY_PD    ' PREVIOUS DASH BUTTON STATE
DIM PLY_GR    ' GRACE
DIM PLY_JB    ' JUMP BUFFER
DIM PLY_JP    ' AVAILABLE DASH-JUMP COUNT
DIM PLY_DT    ' DASH TIME
DIM PLY_DE    ' DASH EFFECT TIME
DIM PLY_DX    ' DASH TARGET X
DIM PLY_DY    ' DASH TARGET Y
DIM PLY_AX    ' DASH ACCEL X
DIM PLY_AY    ' DASH ACCEL Y
DIM PLY_SA    ' SPRITE ANIMATION OFFSET
DIM PLY_WG    ' WAS ON GROUND
DIM HAR_X[5]  ' HAIR X ARRAY
DIM HAR_Y[5]  ' HAIR Y ARRAY

' SPRINGS
DIM SRG_CT    ' COUNT
DIM SRG_X[16] ' X
DIM SRG_Y[16] ' Y
DIM SRG_S[16] ' SPRITE
DIM SRG_HI[16]' DISAPPEARING COUNTER
DIM SRG_HF[16]' HIDING COUNTER
DIM SRG_DL[16]' DELAY

' BALLOONS
DIM BLN_CT    ' COUNT
DIM BLN_X[8]  ' X
DIM BLN_Y[8]  ' Y
DIM BLN_S[8]  ' SPRITE
DIM BLN_OF[8] ' Y OFFSET
DIM BLN_IY[8] ' INITIAL Y
DIM BLN_TM[8] ' TIMER

' FALL FLOORS
DIM FLR_CT    ' COUNT
DIM FLR_X[16] ' X
DIM FLR_Y[16] ' Y
DIM FLR_ST[16]' STATE
DIM FLR_DL[16]' DELAY

' SMOKES
DIM SMK_I     ' RING BUFFER INDEX
DIM SMK_X[16] ' X
DIM SMK_Y[16] ' Y
DIM SMK_S[16] ' SPRITE
DIM SMK_SX[16]' SPEED X
DIM SMK_SY[16]' SPEED Y
DIM SMK_FX[16]' FLIP X
DIM SMK_FY[16]' FLIP Y

' FRUIT
DIM ICG_MD    ' MODE
DIM ICG_X     ' X
DIM ICG_Y     ' Y
DIM ICG_SY    ' SPEED Y
DIM ICG_IY    ' INITIAL Y
DIM ICG_F     ' IS FLYING
DIM ICG_ST    ' STEP
DIM ICG_XD    ' SFX DELAY

' FAKE WALL
DIM WAL_E     ' EXIST
DIM WAL_X     ' X
DIM WAL_Y     ' Y

' KEY
DIM KEY_E     ' EXIST
DIM KEY_X     ' X
DIM KEY_Y     ' Y
DIM KEY_S     ' SPRITE
DIM KEY_FX    ' FLIP X

' CHEST
DIM CST_E     ' EXIST
DIM CST_X     ' X
DIM CST_Y     ' Y
DIM CST_IX    ' INITIAL X
DIM CST_TM    ' TIMER

' PLATFORMS
DIM PFM_CT    ' COUNT
DIM PFM_X[16] ' X
DIM PFM_Y[16] ' Y
DIM PFM_D[16] ' DIRECTION

' MESSAGE
DIM MSG_E     ' EXIST
DIM MSG_X     ' X
DIM MSG_Y     ' Y
DIM MSG_I     ' INDEX

' BIG CHEST
DIM TRB_E     ' EXIST
DIM TRB_X     ' X
DIM TRB_Y     ' Y
DIM TRB_ST    ' STATE
DIM TRB_TM    ' TIMER

' ORB
DIM ORB_E     ' EXIST
DIM ORB_X     ' X
DIM ORB_Y     ' Y
DIM ORB_SY    ' SPEED Y

' FLAG
DIM FLG_E     ' EXIST
DIM FLG_X     ' X
DIM FLG_Y     ' Y
DIM FLG_SC    ' SCORE
DIM FLG_V     ' RESULT IS SHOWN

' ROOM TITLE
DIM ROOM_TITLE_DL ' DELAY

' CLOUDS
DIM CLD_X[16] ' X
DIM CLD_Y[16] ' Y
DIM CLD_SX[16]' SPEED
DIM CLD_W[16] ' WIDTH

' PARTICLES
DIM PTC_X[24] ' X
DIM PTC_Y[24] ' Y
DIM PTC_S[24] ' SIZE
DIM PTC_SX[24]' SPEED
DIM PTC_D[24] ' WAVE PHASE
DIM PTC_C[24] ' COLOR

' ZATSUYOS
DIM ZTS_X[8]  ' X
DIM ZTS_Y[8]  ' Y
DIM ZTS_SX[8] ' SPEED X
DIM ZTS_SY[8] ' SPEED Y

' SPRITE ID OFFSET
DIM S_ICG=0  '  0~  2
DIM S_WAL=3
DIM S_KEY=4
DIM S_CST=5
DIM S_TRB=6
DIM S_ORB=7
DIM S_PLY=14 '  8~ 14
DIM S_FLG=15
DIM S_SRG=16 ' 16~ 31
DIM S_BLN=32 ' 32~ 47
DIM S_FLR=48 ' 48~ 63
DIM S_SMK=64 ' 64~ 79
DIM S_PFM=80 ' 80~ 95
DIM S_CLD=96 ' 96~112
DIM S_PTC=112'112~135
DIM S_ZTS=136'136~143
DIM S_BAK=144
DIM S_BG=145 '145~146 TERRAIN LAYERS (WERE BG 0/1)

' BG REPLACEMENT (PI HAS NO BG SCREEN)
VAR BG_SRC=2   ' GRP2: PRE-HAIR TILESET SNAPSHOT
VAR BG_V=128   ' V OF TERRAIN AREA ON SPPAGE
VAR BG_Z=1     ' Z OF LAYER 0 (WAS BG Z=0; SPRITES SIT AT 0)

' PICO-8 MEMORY
DIM BTN[6]     ' BUTTON STATE ARRAY
DIM PAL%[16]   ' COLOR PALETTE
DIM GFF%[32]   ' SPRITE FLAG (128 / 4)
DIM MAP%[2048] ' MAP (128x32 / 4)

' MAIN LOOP 

ACLS
XSCREEN 400,240
FADE #BLACK
SETUP
INIT_CLOUDS
INIT_PARTICLES
TITLE_SCREEN
WHILE 1
 POLL_BTN
 UPDATE
 DRAW
 EXTRA_BTN
 VSYNC 2
WEND
END

' INITIALIZE FUNCTIONS 

DEF TITLE_SCREEN
 DIM I
 FOR I=0 TO 29
  GT_ICG[I]=FALSE
 NEXT
 TM_F=0
 DEATHS=0
 MAX_JP=1
 ST_GM=FALSE
 ST_GFL=0
 NW_BG=FALSE
 FREEZE=0
 SHAKE=0
 RST_RM=FALSE
 RST_DL=0
 SFX_TM=0
 PZ_PLY=FALSE
 KL_PLY=0
 FL_BG=FALSE
 BGM_TM=0

 GCLS
 MUSIC 40,0,7
 LOAD_ROOM 7,3
END

DEF BEGIN_GAME
 TM_F=0
 TM_S=0
 TM_M=0
 BGM_TM=0
 ST_GM=FALSE
 MUSIC 0,0,7
 LOAD_ROOM 0,0
END

' GAME STATE FUNCTIONS 

DEF LEVEL_INDEX()
 RETURN ROOM_X+ROOM_Y*8
END

DEF IS_TITLE()
 RETURN LEVEL_INDEX()==31
END

' PLAYER FUNCTIONS 

DEF CLEAR_PLAYER
 PLY_MD=0
 CLEAR_EXTRA_SPRITES S_PLY-6,S_PLY
END

DEF INIT_PLAYER X,Y,MODE
 PLY_MD=MODE
 PLY_X=X
 PLY_Y=Y
 IF MODE==1 THEN
  SFX 4
  PLY_S=3
  PLY_TX=PLY_X
  PLY_TY=PLY_Y
  PLY_Y=128
  PLY_SX=0
  PLY_SY=-4
  PLY_ST=0
  PLY_DL=0
  CREATE_HAIR
 ELSEIF MODE==2 THEN
  PLY_PJ=FALSE
  PLY_PD=FALSE
  PLY_GR=0
  PLY_JB=0
  PLY_JP=MAX_JP
  PLY_DT=0
  PLY_DE=0
  PLY_DX=0
  PLY_DY=0
  PLY_AX=0
  PLY_AY=0
  PLY_SA=0
  PLY_WG=FALSE
 ENDIF
END

DEF UPDATE_PLAYER

 IF PZ_PLY || !PLY_MD THEN RETURN

 '-- SPAWN
 IF PLY_MD==1 THEN
  '-- MOVE
  INC PLY_X,PLY_SX
  INC PLY_Y,PLY_SY
  '-- JUMPING UP
  IF PLY_ST==0 THEN
   IF PLY_Y < PLY_TY+16 THEN
    PLY_ST=1
    PLY_DL=3
   ENDIF
  '-- FALLING
  ELSEIF PLY_ST==1 THEN
   INC PLY_SY,0.5
   IF PLY_SY>0 AND PLY_DL>0 THEN
    PLY_SY=0
    DEC PLY_DL
   ENDIF
   IF PLY_SY>0 AND PLY_Y > PLY_TY THEN
    PLY_Y=PLY_TY
    PLY_SX=0
    PLY_SY=0
    PLY_ST=2
    PLY_DL=5
    SHAKE=5
    INIT_SMOKE PLY_X,PLY_Y+4
    SFX 5
   ENDIF
  '-- LANDING
  ELSEIF PLY_ST==2 THEN
   DEC PLY_DL
   PLY_S=6
   IF PLY_DL<0 THEN
    INIT_PLAYER PLY_X,PLY_Y,2
   ENDIF
  ENDIF
  RETURN
 ENDIF

 MOVE_PLAYER PLY_SX,PLY_SY

 '-- PLAYER CONTROL
 DIM H_INP=BTN[K_RT]-BTN[K_LF]

 '-- SPIKES COLLIDE
 IF SPIKES_AT(PLY_X+1,PLY_Y+3,6,5,PLY_SX,PLY_SY) THEN KILL_PLAYER

 '-- BOTTOM DEATH
 IF PLY_Y>128 THEN KILL_PLAYER

 DIM ON_GRD=IS_SOLID(0,1)
 DIM ON_ICE=IS_ICE(0,1)
 
 '-- SMOKE PARTICLES
 IF ON_GRD && !PLY_WG THEN
  INIT_SMOKE PLY_X,PLY_Y+4
 ENDIF

 DIM JUMP=BTN[K_JP] && !PLY_PJ
 PLY_PJ=BTN[K_JP]
 IF JUMP THEN
  PLY_JB=4
 ELSEIF PLY_JB>0 THEN
  DEC PLY_JB
 ENDIF

 DIM DASH=BTN[K_DS] && !PLY_PD
 PLY_PD=BTN[K_DS]

 IF ON_GRD THEN
  PLY_GR=6
  IF PLY_JP<MAX_JP THEN
   PSFX 54
   PLY_JP=MAX_JP
  ENDIF
 ELSEIF PLY_GR>0 THEN
  DEC PLY_GR
 ENDIF

 DEC PLY_DE
 IF PLY_DT>0 THEN
  INIT_SMOKE PLY_X,PLY_Y
  DEC PLY_DT
  PLY_SX=APPR(PLY_SX,PLY_DX,PLY_AX)
  PLY_SY=APPR(PLY_SY,PLY_DY,PLY_AY)
 ELSE
  '-- MOVE
  DIM MAXRUN=1,AC=0.6,DC=0.15

  IF !ON_GRD THEN
   AC=0.4
  ELSEIF ON_ICE THEN
   AC=0.05
   IF H_INP==1-PLY_FX*2 THEN
    AC=0.05
   ENDIF
  ENDIF

  IF ABS(PLY_SX)>MAXRUN THEN
   PLY_SX=APPR(PLY_SX,SGN(PLY_SX)*MAXRUN,DC)
  ELSE
   PLY_SX=APPR(PLY_SX,H_INP*MAXRUN,AC)
  ENDIF

  '-- FACING
  IF PLY_SX!=0 THEN PLY_FX=(PLY_SX<0)

  '-- GRAVITY
  DIM MAXF=2,GRV=0.21
  IF ABS(PLY_SY)<=0.15 THEN GRV=GRV*0.5

  '-- WALL SLIDE
  IF H_INP!=0 && IS_SOLID(H_INP,0) && !IS_ICE(H_INP,0) THEN
   MAXF=0.4
   IF RND(10)<2 THEN
    INIT_SMOKE PLY_X+H_INP*6,PLY_Y
   ENDIF
  ENDIF

  IF !ON_GRD THEN
   PLY_SY=APPR(PLY_SY,MAXF,GRV)
  ENDIF

  '-- JUMP
  IF PLY_JB>0 THEN
   IF PLY_GR>0 THEN
    '-- NORMAL JUMP
    PSFX 1
    PLY_JB=0
    PLY_GR=0
    PLY_SY=-2
    INIT_SMOKE PLY_X,PLY_Y+4
   ELSE
    '-- WALL JUMP
    DIM WALL_DIR=IS_SOLID(3,0)-IS_SOLID(-3,0)
    IF WALL_DIR!=0 THEN
     PSFX 2
     PLY_JB=0
     PLY_SY=-2
     PLY_SX=-WALL_DIR*(MAXRUN+1)
     IF !IS_ICE(WALL_DIR*3,0) THEN
      INIT_SMOKE PLY_X+WALL_DIR*6,PLY_Y
     ENDIF
    ENDIF
   ENDIF
  ENDIF

  '-- DASH
  DIM D_FULL=5
  DIM D_HALF=D_FULL*0.70710678118

  IF PLY_JP>0 && DASH THEN
   INIT_SMOKE PLY_X,PLY_Y
   DEC PLY_JP
   PLY_DT=4
   HS_DSH=TRUE
   PLY_DE=10
   DIM V_INP=BTN[K_DN]-BTN[K_UP]
   IF H_INP!=0 THEN
    IF V_INP!=0 THEN
     PLY_SX=H_INP*D_HALF
     PLY_SY=V_INP*D_HALF
    ELSE
     PLY_SX=H_INP*D_FULL
     PLY_SY=0
    ENDIF
   ELSEIF V_INP!=0 THEN
    PLY_SX=0
    PLY_SY=V_INP*D_FULL
   ELSE
    PLY_SX=1-PLY_FX*2
    PLY_SY=0
   ENDIF

   PSFX 3
   FREEZE=2
   SHAKE=6
   PLY_DX=2*SGN(PLY_SX)
   PLY_DY=2*SGN(PLY_SY)
   PLY_AX=1.5
   PLY_AY=1.5

   IF PLY_SY<0 THEN PLY_DY=PLY_DY*.75
   IF PLY_SY!=0 THEN
    PLY_AX=PLY_AX*0.70710678118
   ENDIF
   IF PLY_SX!=0 THEN
    PLY_AY=PLY_AY*0.70710678118
   ENDIF
  ELSEIF DASH && PLY_JP<=0 THEN
   PSFX 9
   INIT_SMOKE PLY_X,PLY_Y
  ENDIF

 ENDIF

 '-- ANIMATION
 INC PLY_SA,0.25
 IF !ON_GRD THEN
  IF IS_SOLID(H_INP,0) THEN
   PLY_S=5
  ELSE
   PLY_S=3
  ENDIF
 ELSEIF BTN[K_DN] THEN
  PLY_S=6
 ELSEIF BTN[K_UP] THEN
  PLY_S=7
 ELSEIF PLY_SX==0 || H_INP==0 THEN
  PLY_S=1
 ELSE
  PLY_S=1+PLY_SA MOD 4
 ENDIF

 '-- NEXT LEVEL
 IF PLY_Y<-4 && PLY_MD==2 THEN
  IF LEVEL_INDEX()<30 THEN NEXT_ROOM
 ENDIF

 '-- WAS ON THE GROUND
 PLY_WG=ON_GRD
END

' CHECK COLLISION

DEF COL_PLAYER(X,Y,HX,HY,HW,HH,OX,OY)
 DIM R 'HITBOX={X=1,Y=3,W=6,H=5}
 R=     PLY_MD==2
 R=R && PLY_X+7 > X+HX+OX
 R=R && PLY_Y+8 > Y+HY+OY
 R=R && PLY_X+1 < X+HX+HW+OX
 R=R && PLY_Y+3 < Y+HY+HH+OY
 RETURN R
END

DEF IS_SOLID(OX,OY)
 DIM X=PLY_X
 DIM Y=PLY_Y 'HITBOX={X=1,Y=3,W=6,H=5}
 DIM R
 R=     OY>0
 R=R && COL_PLATFORMS(X,Y,1,3,6,5,OX,0)<0
 R=R && COL_PLATFORMS(X,Y,1,3,6,5,OX,OY)>=0
 R=R || SOLID_AT(X+1+OX,Y+3+OY,6,5)
 R=R || COL_FALL_FLOORS(X,Y,1,3,6,5,OX,OY)>=0
 R=R || COL_FAKE_WALL(X,Y,1,3,6,5,OX,OY)
 RETURN R
END

DEF IS_ICE(OX,OY)
 RETURN ICE_AT(PLY_X+1+OX,PLY_Y+3+OY,6,5)
END

DEF SOLID_AT(X,Y,W,H)
 RETURN TILE_FLAG_AT(X,Y,W,H,0)
END

DEF ICE_AT(X,Y,W,H)
 RETURN TILE_FLAG_AT(X,Y,W,H,4)
END

DEF TILE_FLAG_AT(X,Y,W,H,FLAG)
 DIM I,IE=MIN(15,(X+W-1)/8)
 DIM J,JE=MIN(15,(Y+H-1)/8)
 FOR I=MAX(0,FLOOR(X/8)) TO IE
  FOR J=MAX(0,FLOOR(Y/8)) TO JE
   IF FGET(TILE_AT(I,J),FLAG) THEN
    RETURN TRUE
   ENDIF
  NEXT
 NEXT
 RETURN FALSE
END

DEF TILE_AT(X,Y)
 RETURN MGET(ROOM_X*16+X,ROOM_Y*16+Y)
END

DEF SPIKES_AT(X,Y,W,H,SX,SY)
 DIM I,IE=MIN(15,(X+W-1)/8)
 DIM J,JE=MIN(15,(Y+H-1)/8),T
 FOR I=MAX(0,FLOOR(X/8)) TO IE
  FOR J=MAX(0,FLOOR(Y/8)) TO JE
   T=TILE_AT(I,J)
   IF T==17 && ((Y+H-1) MOD 8>=6 || Y+H==J*8+8) && SY>=0 THEN
    RETURN TRUE
   ELSEIF T==27 && Y MOD 8<=2 && SY<=0 THEN
    RETURN TRUE
   ELSEIF T==43 && X MOD 8<=2 && SX<=0 THEN
    RETURN TRUE
   ELSEIF T==59 && ((X+W-1) MOD 8>=6 || X+W==I*8+8) && SX>=0 THEN
    RETURN TRUE
   ENDIF
  NEXT
 NEXT
 RETURN FALSE
END

' MOVE PLAYER

DEF MOVE_PLAYER OX,OY
 DIM AMOUNT
 '-- [X] GET MOVE AMOUNT
 INC PLY_RX,OX
 AMOUNT=FLOOR(PLY_RX+0.5)
 DEC PLY_RX,AMOUNT
 MOVE_PLAYER_X AMOUNT,0

 '-- [Y] GET MOVE AMOUNT
 INC PLY_RY,OY
 AMOUNT=FLOOR(PLY_RY+0.5)
 DEC PLY_RY,AMOUNT
 MOVE_PLAYER_Y AMOUNT,0
END

DEF MOVE_PLAYER_X AMOUNT,START
 DIM I
 DIM STEP=SGN(AMOUNT)
 FOR I=START TO ABS(AMOUNT)
  IF !IS_SOLID(STEP,0) THEN
   INC PLY_X,STEP
  ELSE
   PLY_SX=0
   PLY_RX=0
   BREAK
  ENDIF
 NEXT
END

DEF MOVE_PLAYER_Y AMOUNT,START
 DIM I
 DIM STEP=SGN(AMOUNT)
 FOR I=START TO ABS(AMOUNT)
  IF !IS_SOLID(0,STEP) THEN
   INC PLY_Y,STEP
  ELSE
   PLY_SY=0
   PLY_RY=0
   BREAK
  ENDIF
 NEXT
END

' KILL PLAYER

DEF KILL_PLAYER
 SFX_TM=12
 SFX 0
 INC DEATHS
 SHAKE=10
 DIM I,ANGLE
 FOR I=0 TO 7
  ANGLE=I/8
  ZTS_X[I]=PLY_X+4
  ZTS_Y[I]=PLY_Y+4
  ZTS_SX[I]=P8SIN(ANGLE)*3
  ZTS_SY[I]=P8COS(ANGLE)*3
 NEXT
 CLEAR_PLAYER
 KL_PLY=10
 RESTART_ROOM
END

' DRAW PLAYER

DEF DRAW_PLAYER
 IF !PLY_MD THEN RETURN
 IF PLY_MD==1 THEN
  SET_HAIR_COLOR(MAX_JP)
 ELSEIF PLY_MD==2 THEN
  '-- CLAMP IN SCREEN
  IF PLY_X<-1 || PLY_X>121 THEN
   PLY_X=CLAMP(PLY_X,-1,121)
   PLY_SX=0
  ENDIF
  SET_HAIR_COLOR(PLY_JP)
 ENDIF

 DRAW_HAIR 1-PLY_FX*2
 SPR S_PLY,PLY_S,PLY_X,PLY_Y,PLY_FX,FALSE
 UNSET_HAIR_COLOR
END

DEF PSFX NUM
 IF SFX_TM<=0 THEN SFX NUM
END

DEF CREATE_HAIR
 DIM I
 FOR I=0 TO 4
  HAR_X[I]=PLY_X
  HAR_Y[I]=PLY_Y
 NEXT
END

DEF SET_HAIR_COLOR DJUMP
 DIM I,C=8
 IF DJUMP==2 THEN C=7+FLOOR(TM_F/3) MOD 2*4
 IF DJUMP==0 THEN C=12
 FOR I=0 TO 5
  SPCOLOR S_PLY-1-I,PAL%[C]
 NEXT
END

DEF DRAW_HAIR FACING
 SPR S_PLY-1,72+PLY_S,PLY_X,PLY_Y,PLY_FX,FALSE
 DIM LX=PLY_X+4-FACING*2
 DIM LY=PLY_Y+3+BTN[K_DN]
 DIM I
 FOR I=0 TO 4
  INC HAR_X[I],(LX-HAR_X[I])/1.5
  INC HAR_Y[I],(LY+0.5-HAR_Y[I])/1.5
  SPR S_PLY-2-I,91-(I>1),HAR_X[I],HAR_Y[I],FALSE,FALSE
  LX=HAR_X[I]
  LY=HAR_Y[I]
 NEXT
END

DEF UNSET_HAIR_COLOR
END

' SPRINGS FUNCTIONS 

DEF INIT_SPRING X,Y
 DIM I=SRG_CT
 SRG_X[I]=X
 SRG_Y[I]=Y
 SRG_S[I]=18
 SRG_HI[I]=0
 SRG_HF[I]=0
 INC SRG_CT
END

DEF UPDATE_SPRINGS
 DIM I,HIT
 FOR I=0 TO SRG_CT-1
  IF SRG_HF[I]>0 THEN
   DEC SRG_HF[I]
   IF SRG_HF[I]<=0 THEN
    SRG_S[I]=18
    SRG_DL[I]=0
   ENDIF
  ELSEIF SRG_S[I]==18 THEN
   DIM X=SRG_X[I]
   DIM Y=SRG_Y[I]
   HIT=COL_PLAYER(X,Y,0,0,8,8,0,0)
   IF HIT && PLY_SY>=0 THEN
    SRG_S[I]=19
    PLY_Y=Y-4
    PLY_SX=PLY_SX*0.2
    PLY_SY=-3
    PLY_JP=MAX_JP
    SRG_DL[I]=10
    INIT_SMOKE X,Y

    '-- BREAKABLE BELOW US
    HIT=COL_FALL_FLOORS(X,Y,0,0,8,8,0,1)
    IF HIT>=0 THEN
     BREAK_FALL_FLOOR(HIT)
    ENDIF

    PSFX 8
   ENDIF
  ELSEIF SRG_DL[I]>0 THEN
   DEC SRG_DL[I]
   IF SRG_DL[I]<=0 THEN
    SRG_S[I]=18
   ENDIF
  ENDIF
  '-- BEGIN HIDING
  IF SRG_HI[I]>0 THEN
   DEC SRG_HI[I]
   IF SRG_HI[I]<=0 THEN
    SRG_HF[I]=60
    SRG_S[I]=0
   ENDIF
  ENDIF
 NEXT
END

DEF BREAK_SPRING I
 SRG_HI[I]=15
END

DEF COL_SPRINGS(X,Y,HX,HY,HW,HH,OX,OY)
 DIM I,R 'HITBOX={X=0,Y=0,W=8,H=8}
 FOR I=0 TO SRG_CT-1
  R=SRG_S[I]>0
  R=R && SRG_X[I]+8 > X+HX+OX
  R=R && SRG_Y[I]+8 > Y+HY+OY
  R=R && SRG_X[I] < X+HX+HW+OX
  R=R && SRG_Y[I] < Y+HY+HH+OY
  IF R THEN RETURN I
 NEXT
 RETURN -1
END

DEF DRAW_SPRINGS
 DIM I
 FOR I=0 TO SRG_CT-1
  SPR S_SRG+I,SRG_S[I],SRG_X[I],SRG_Y[I],FALSE,FALSE
 NEXT
END

' BALLOONS FUNCTIONS 

DEF INIT_BALLOON X,Y
 DIM I=BLN_CT
 BLN_X[I]=X
 BLN_Y[I]=Y
 BLN_S[I]=22
 BLN_OF[I]=RNDF()
 BLN_IY[I]=Y
 BLN_TM[I]=0
 INC BLN_CT
END

DEF UPDATE_BALLOONS
 DIM I
 FOR I=0 TO BLN_CT-1
  DIM X=BLN_X[I]
  DIM Y=BLN_Y[I]
  IF BLN_S[I]==22 THEN
   INC BLN_OF[I],0.01
   BLN_Y[I]=BLN_IY[I]+P8SIN(BLN_OF[I])*2
   DIM HIT=COL_PLAYER(X,Y,-1,-1,10,10,0,0)
   IF HIT && PLY_JP<MAX_JP THEN
    PSFX 6
    INIT_SMOKE X,Y
    PLY_JP=MAX_JP
    BLN_S[I]=0
    BLN_TM[I]=60
   ENDIF
  ELSEIF BLN_TM[I]>0 THEN
   DEC BLN_TM[I]
  ELSE
   PSFX 7
   INIT_SMOKE X,Y
   BLN_S[I]=22
  ENDIF
 NEXT
END

DEF DRAW_BALLOONS
 DIM I,S,U,X,Y
 FOR I=0 TO BLN_CT-1
  S=BLN_S[I]
  U=13+BLN_OF[I]*8 MOD 3
  X=BLN_X[I]
  Y=BLN_Y[I]
  SPR S_BLN+I*2+1,S,X,Y,FALSE,FALSE
  SPR S_BLN+I*2,U*(S==22),X,Y+6,FALSE,FALSE
 NEXT
END

' FALL FLOORS FUNCTIONS 

DEF INIT_FALL_FLOOR X,Y
 DIM I=FLR_CT
 FLR_X[I]=X
 FLR_Y[I]=Y
 FLR_ST[I]=0
 INC FLR_CT
END

DEF UPDATE_FALL_FLOORS
 DIM I,R,X,Y
 FOR I=0 TO FLR_CT-1
  X=FLR_X[I]
  Y=FLR_Y[I]
  '-- IDLING
  IF FLR_ST[I]==0 THEN
   R=     COL_PLAYER(X,Y,0,0,8,8,0,-1)
   R=R || COL_PLAYER(X,Y,0,0,8,8,-1,0)
   R=R || COL_PLAYER(X,Y,0,0,8,8,1,0)
   IF R THEN BREAK_FALL_FLOOR I
  '-- SHAKING
  ELSEIF FLR_ST[I]==1 THEN
   DEC FLR_DL[I]
   IF FLR_DL[I]<=0 THEN
    FLR_ST[I]=2
    FLR_DL[I]=60
   ENDIF
  '-- INVISIBLE, WAITING TO RESET
  ELSEIF FLR_ST[I]==2 THEN
   DEC FLR_DL[I]
   R=     FLR_DL[I]<=0
   R=R && !COL_PLAYER(X,Y,0,0,8,8,0,0)
   IF R THEN
    PSFX 7
    FLR_ST[I]=0
    INIT_SMOKE X,Y
   ENDIF
  ENDIF
 NEXT
END

DEF BREAK_FALL_FLOOR I
 IF FLR_ST[I]==0 THEN
  PSFX 15
  FLR_ST[I]=1
  FLR_DL[I]=15
  DIM X=FLR_X[I]
  DIM Y=FLR_Y[I]
  INIT_SMOKE X,Y
  DIM HIT=COL_SPRINGS(X,Y,0,0,8,8,0,-1)
  IF HIT>=0 THEN BREAK_SPRING(HIT)
 ENDIF
END

DEF COL_FALL_FLOORS(X,Y,HX,HY,HW,HH,OX,OY)
 DIM I,R 'HITBOX={X=0,Y=0,W=8,H=8}
 FOR I=0 TO FLR_CT-1
  R=FLR_ST[I]!=2
  R=R && FLR_X[I]+8 > X+HX+OX
  R=R && FLR_Y[I]+8 > Y+HY+OY
  R=R && FLR_X[I] < X+HX+HW+OX
  R=R && FLR_Y[I] < Y+HY+HH+OY
  IF R THEN RETURN I
 NEXT
 RETURN -1
END

DEF DRAW_FALL_FLOORS
 DIM I,S
 FOR I=0 TO FLR_CT-1
  IF FLR_ST[I]==0 THEN
   S=23
  ELSEIF FLR_ST[I]==1 THEN
   S=23+(15-FLR_DL[I])/5
  ELSEIF FLR_ST[I]==2 THEN
   S=0
  ENDIF
  SPR S_FLR+I,S,FLR_X[I],FLR_Y[I],FALSE,FALSE
 NEXT
END

' SMOKES FUNCTIONS 

DEF CLEAR_SMOKES
 DIM I
 FOR I=0 TO 15
  SMK_S[I]=32
 NEXT
 SMK_I=0
END

DEF INIT_SMOKE X,Y
 DIM I=SMK_I
 SMK_S[I]=29
 SMK_SY[I]=-0.1
 SMK_SX[I]=0.3+RNDF()*0.2
 SMK_X[I]=X-1+RNDF()*2
 SMK_Y[I]=Y-1+RNDF()*2
 SMK_FX[I]=MAYBE()
 SMK_FY[I]=MAYBE()
 SMK_I=(I+1) MOD 16
END

DEF UPDATE_SMOKES
 DIM I
 FOR I=0 TO 15
  IF SMK_S[I]>=32 THEN CONTINUE
  INC SMK_X[I],SMK_SX[I]
  INC SMK_Y[I],SMK_SY[I]
  INC SMK_S[I],0.2
 NEXT
END

DEF DRAW_SMOKES
 DIM I,S
 FOR I=0 TO 15
  S=SMK_S[I]
  SPR S_SMK+I,S*(S<32),SMK_X[I],SMK_Y[I],SMK_FX[I],SMK_FY[I]
 NEXT
END

' FRUIT FUNCTIONS 

DEF INIT_FRUIT X,Y,MODE
 IF GT_ICG[LEVEL_INDEX()] THEN RETURN
 ICG_MD=MODE
 ICG_X=X
 ICG_Y=Y
 ICG_SY=0
 ICG_IY=Y
 IF MODE==1 THEN
  ICG_ST=0
 ELSEIF MODE==2 THEN
  ICG_F=FALSE
  ICG_ST=0.5
  ICG_XD=8
 ENDIF
END

DEF UPDATE_FRUIT
 IF !ICG_MD THEN RETURN

 INC ICG_Y,ICG_SY
 IF ICG_MD==1 THEN
  INC ICG_ST
  ICG_Y=ICG_IY+P8SIN(ICG_ST/40)*2.5
 ELSEIF ICG_MD==2 THEN
  '--FLY AWAY
  IF ICG_F THEN
   IF ICG_XD>0 THEN
    DEC ICG_XD
    IF ICG_XD<=0 THEN
     SFX_TM=20
     SFX 14
    ENDIF
   ENDIF
   ICG_SY=APPR(ICG_SY,-3.5,0.25)
   IF ICG_Y<-16 THEN
    ICG_MD=0
   ENDIF
  '-- WAIT
  ELSE
   IF HS_DSH THEN
    ICG_F=TRUE
   ENDIF
   INC ICG_ST,0.05
   ICG_SY=P8SIN(ICG_ST)*0.5
  ENDIF
 ENDIF

 '-- COLLECT
 IF ICG_MD==3 THEN
  DEC ICG_ST
  IF ICG_ST<=0 THEN
   ICG_MD=0
  ENDIF
 ELSEIF COL_PLAYER(ICG_X,ICG_Y,-1,-1,10,10,0,0) THEN
  PLY_JP=MAX_JP
  SFX_TM=20
  SFX 13
  GT_ICG[LEVEL_INDEX()]=TRUE
  ICG_MD=3
  ICG_SY=-0.25
  ICG_ST=30
 ENDIF
END

DEF DRAW_FRUIT
 DIM FRUIT,WING
 IF ICG_MD==0 THEN
  FRUIT=0
  WING=0
 ELSEIF ICG_MD==1 THEN
  FRUIT=26
  WING=0
 ELSEIF ICG_MD==2 THEN
  DIM OFFS=0
  IF !ICG_F THEN
   IF ICG_SY<0 THEN
    OFFS=1+MAX(0,(ICG_Y-ICG_IY)/2)
   ENDIF
  ELSE
   OFFS=TM_F/2 MOD 3
  ENDIF
  FRUIT=28
  WING=45+OFFS
 ELSEIF ICG_MD==3 THEN
  FRUIT=26*(ICG_ST MOD 2)
 ENDIF

 SPR S_ICG,WING,ICG_X-6,ICG_Y-2,TRUE,FALSE
 SPR S_ICG+1,FRUIT,ICG_X,ICG_Y,FALSE,FALSE
 SPR S_ICG+2,WING,ICG_X+6,ICG_Y-2,FALSE,FALSE
END

' FAKE WALL FUNCTIONS 

DEF INIT_FAKE_WALL X,Y
 IF GT_ICG[LEVEL_INDEX()] THEN RETURN
 WAL_E=TRUE
 WAL_X=X
 WAL_Y=Y
END

DEF UPDATE_FAKE_WALL
 IF !WAL_E THEN RETURN
 IF PLY_DE>0 && COL_PLAYER(WAL_X,WAL_Y,-1,-1,18,18,0,0) THEN
  PLY_SX=-SGN(PLY_SX)*1.5
  PLY_SY=-1.5
  PLY_DT=-1
  SFX_TM=20
  SFX 16
  INIT_SMOKE WAL_X,  WAL_Y
  INIT_SMOKE WAL_X+8,WAL_Y
  INIT_SMOKE WAL_X,  WAL_Y+8
  INIT_SMOKE WAL_X+8,WAL_Y+8
  INIT_FRUIT WAL_X+4,WAL_Y+4,1
  WAL_E=FALSE
 ENDIF
END

DEF COL_FAKE_WALL(X,Y,HX,HY,HW,HH,OX,OY)
 DIM R 'HITBOX={X=0,Y=0,W=16,H=16}
 R=WAL_E
 R=R && WAL_X+16 > X+HX+OX
 R=R && WAL_Y+16 > Y+HY+OY
 R=R && WAL_X < X+HX+HW+OX
 R=R && WAL_Y < Y+HY+HH+OY
 RETURN R
END

DEF DRAW_FAKE_WALL
 SPR S_WAL,WAL_E*64,WAL_X,WAL_Y,FALSE,FALSE
END


' KEY FUNCTIONS 

DEF INIT_KEY X,Y
 IF GT_ICG[LEVEL_INDEX()] THEN RETURN
 KEY_E=TRUE
 KEY_X=X
 KEY_Y=Y
 KEY_S=8
END

DEF UPDATE_KEY
 IF !KEY_E THEN RETURN
 DIM WAS=FLOOR(KEY_S)
 KEY_S=9+(P8SIN(TM_F/30)+0.5)*1
 DIM IS=FLOOR(KEY_S)
 IF IS==10 && IS!=WAS THEN
  KEY_FX=!KEY_FX
 ENDIF
 IF COL_PLAYER(KEY_X,KEY_Y,0,0,8,8,0,0) THEN
  SFX 23
  SFX_TM=10
  HS_KEY=TRUE
  KEY_E=FALSE
 ENDIF
END

DEF DRAW_KEY
 SPR S_KEY,KEY_E*KEY_S,KEY_X,KEY_Y,KEY_FX,FALSE
END

' CHEST FUNCTIONS 

DEF INIT_CHEST X,Y
 IF GT_ICG[LEVEL_INDEX()] THEN RETURN
 CST_E=TRUE
 CST_X=X-4
 CST_Y=Y
 CST_IX=CST_X
 CST_TM=20
END

DEF UPDATE_CHEST
 IF !CST_E THEN RETURN
 IF HS_KEY THEN
  DEC CST_TM
  CST_X=CST_IX-1+RND(3)
  IF CST_TM<=0 THEN
   SFX_TM=20
   SFX 16
   INIT_FRUIT CST_X,CST_Y-4,1
   CST_E=FALSE
  ENDIF
 ENDIF
END

DEF DRAW_CHEST
 SPR S_CST,CST_E*20,CST_X,CST_Y,FALSE,FALSE
END

' PLATFORMS FUNCTIONS 

DEF INIT_PLATFORM X,Y,DIR
 DIM I=PFM_CT
 PFM_X[I]=X-4
 PFM_Y[I]=Y
 PFM_D[I]=DIR
 SPOFS S_PFM+I,,,BG_Z+1 'STAY BEHIND TERRAIN LAYER 0
 INC PFM_CT
END

DEF UPDATE_PLATFORMS
 DIM I
 FOR I=0 TO PFM_CT-1
  DIM X=PFM_X[I]
  DIM Y=PFM_Y[I]
  X=X+PFM_D[I]*0.65
  IF X<-16 THEN X=128
  IF X>128 THEN X=-16
  IF !COL_PLAYER(X,Y,0,0,16,8,0,0) THEN
   DIM HIT=COL_PLAYER(X,Y,0,0,16,8,0,-1)
   IF HIT THEN
    MOVE_PLAYER_X FLOOR(X)-FLOOR(PFM_X[I]),1
   ENDIF
  ENDIF
  PFM_X[I]=X
 NEXT
END

DEF COL_PLATFORMS(X,Y,HX,HY,HW,HH,OX,OY)
 DIM I,R 'HITBOX={X=0,Y=0,W=16,H=8}
 FOR I=0 TO PFM_CT-1
  R=     PFM_X[I]+16 > X+HX+OX
  R=R && PFM_Y[I]+8 > Y+HY+OY
  R=R && PFM_X[I] < X+HX+HW+OX
  R=R && PFM_Y[I] < Y+HY+HH+OY
  IF R THEN RETURN I
 NEXT
 RETURN -1
END

DEF DRAW_PLATFORMS
 DIM I
 FOR I=0 TO PFM_CT-1
  SPR S_PFM+I,11,PFM_X[I],PFM_Y[I]-1,FALSE,FALSE
 NEXT
END

' MESSAGE FUNCTIONS 

DEF INIT_MESSAGE X,Y
 MSG_E=TRUE
 MSG_X=X
 MSG_Y=Y
END

DEF DRAW_MESSAGE
 IF !MSG_E THEN RETURN
 IF COL_PLAYER(MSG_X,MSG_Y,0,0,8,8,4,0) THEN
  DIM MSG$="-- CELESTE MOUNTAIN --#THIS MEMORIAL TO THOSE#PERISHED ON THE CLIMB."
  IF MSG_I<LEN(MSG$)-1 THEN
   INC MSG_I,0.5
   IF MSG_I==FLOOR(MSG_I) THEN
    SFX 35
    DIM M$=MID$(MSG$,MSG_I,1)
    IF M$!="#" THEN
     DIM MX=8+(MSG_I MOD 23)*5
     DIM MY=96+(MSG_I DIV 23)*7
     RECTFILL MX-2,MY-2,MX+7,MY+6,7
     P8PRINT M$,MX,MY,0
    ENDIF
   ENDIF
  ENDIF
 ELSE
  IF MSG_I>=0 THEN
   GCLS
  ENDIF
  MSG_I=-0.5
 ENDIF
END

' BIG CHEST FUNCTIONS 

DEF INIT_BIG_CHEST X,Y
 TRB_E=TRUE
 TRB_X=X
 TRB_Y=Y
 TRB_ST=0
END

DEF UPDATE_BIG_CHEST
 IF !TRB_E THEN RETURN
 IF TRB_ST==0 THEN
  DIM HIT=COL_PLAYER(TRB_X,TRB_Y,0,0,16,8,0,8)
  IF HIT AND IS_SOLID(0,1) THEN
   MUSIC -1,500,7
   SFX 37
   PZ_PLY=TRUE
   PLY_SX=0
   PLY_SY=0
   TRB_ST=1
   INIT_SMOKE TRB_X,TRB_Y
   INIT_SMOKE TRB_X+8,TRB_Y
   TRB_TM=60
  ENDIF
 ELSEIF TRB_ST==1 THEN
  DEC TRB_TM
  SHAKE=5
  FL_BG=TRUE
  DIM I
  IF TRB_TM<=45 THEN
   I=(TRB_TM+1) MOD 8
   ZTS_X[I]=TRB_X+1+RND(14)
   ZTS_Y[I]=TRB_Y+8
   ZTS_SY[I]=8+RND(8)
   SPCOLOR S_ZTS+I,PAL%[7]
   SPSCALE S_ZTS+I,1,ZTS_SY[I]
  ENDIF
  IF TRB_TM<0 THEN
   TRB_ST=2
   FL_BG=FALSE
   NW_BG=TRUE
   INIT_ORB TRB_X+4,TRB_Y+4
   PZ_PLY=FALSE
  ENDIF
 ENDIF
END

DEF DRAW_BIG_CHEST
 DIM S
 IF !TRB_E THEN
  S=0
 ELSEIF TRB_ST==0 THEN
  S=96
 ELSE
  S=112
 ENDIF
 SPR S_TRB,S,TRB_X,TRB_Y+(S==112)*8,FALSE,FALSE
END

' ORB FUNCTIONS 

DEF INIT_ORB X,Y
 ORB_E=TRUE
 ORB_X=X
 ORB_Y=Y
 ORB_SY=-4
 DIM I
 FOR I=0 TO 7
  SPCOLOR S_ZTS+I,PAL%[7]
  SPSCALE S_ZTS+I,1,1
 NEXT
END

DEF UPDATE_ORB
 IF !ORB_E THEN RETURN
 INC ORB_Y,ORB_SY
 ORB_SY=APPR(ORB_SY,0,0.5)
 DIM HIT=COL_PLAYER(ORB_X,ORB_Y,0,0,8,8,0,0)
 IF ORB_SY==0 && HIT THEN
  BGM_TM=45
  SFX 51
  FREEZE=10
  SHAKE=10
  ORB_E=FALSE
  CLEAR_ZATSUYOS
  MAX_JP=2
  PLY_JP=2
 ENDIF
END

DEF DRAW_ORB
 SPR S_ORB,ORB_E*102,ORB_X,ORB_Y,FALSE,FALSE
END

' FLAG FUNCTIONS 

DEF INIT_FLAG X,Y
 FLG_E=TRUE
 FLG_X=X+5
 FLG_Y=Y
 FLG_SC=0
 FLG_V=FALSE
 DIM I
 FOR I=0 TO 29
  INC FLG_SC,GT_ICG[I]
 NEXT
END

DEF UPDATE_FLAG
 IF !FLG_E THEN RETURN
 DIM HIT=COL_PLAYER(FLG_X,FLG_Y,0,0,8,8,0,0)
 IF !FLG_V && HIT THEN
  RECTFILL 32,6,96,35,0
  'SPR S_ICG,26,55,10
  GCOPY SPPAGE(),160,16,175,31,182,12,FALSE
  P8PRINT "X"+STR$(FLG_SC),64,13,7
  DRAW_TIME 49,20
  P8PRINT "DEATHS:"+STR$(DEATHS),48,28,7
  SFX 55
  SFX_TM=30
  FLG_V=TRUE
 ENDIF
END

DEF DRAW_FLAG
 DIM S=0
 IF FLG_E THEN S=118+(TM_F/5) MOD 3
 SPR S_FLG,S,FLG_X,FLG_Y,FALSE,FALSE
END

' ROOM TITLE FUNCTIONS 

DEF INIT_ROOM_TITLE
 GCLS
 ROOM_TITLE_DL=5
END

DEF DRAW_ROOM_TITLE
 IF IS_TITLE() THEN RETURN
 IF ROOM_TITLE_DL<-30 THEN RETURN
 IF ROOM_TITLE_DL==0 THEN
  GPRIO -128
  RECTFILL 24,58,104,70,0
  DIM ROOM$=STR$((1+LEVEL_INDEX())*100)+" M"
  IF ROOM_X==3 && ROOM_Y==1 THEN
   P8PRINT "OLD SITE",48,62,7
  ELSEIF LEVEL_INDEX()==30 THEN
   P8PRINT "SUMMIT",53,62,7
  ELSE
   DIM LV=(1+LEVEL_INDEX())*100
   P8PRINT STR$(LV)+" M",52+(LV<1000)*2,62,7
  ENDIF
  DRAW_TIME 4,8
 ELSEIF ROOM_TITLE_DL==-30 THEN
  GCLS
 ENDIF
 DEC ROOM_TITLE_DL
END

' CLOUDS FUNCTIONS 

DEF INIT_CLOUDS
 DIM I,Z
 FOR I=0 TO 15
  CLD_X[I]=RND(128)
  CLD_Y[I]=RND(128)
  CLD_SX[I]=1+RNDF()*4
  CLD_W[I]=32+RND(32)
  Z=4+FLOOR((1-CLD_W[I]/64)*12)
  SPSCALE S_CLD+I,CLD_W[I],Z
  SPOFS S_CLD+I,,,1216-CLD_SX[I]*192
 NEXT
END

DEF UPDATE_CLOUDS
 DIM I
 FOR I=0 TO 15
  INC CLD_X[I],CLD_SX[I]
  IF CLD_X[I]>128 THEN
   CLD_X[I]=-CLD_W[I]
   CLD_Y[I]=RND(128)
  ENDIF
 NEXT
END

DEF DRAW_CLOUDS
 DIM I,C=0
 IF !IS_TITLE() THEN C=PAL%[1+NW_BG*13]
 FOR I=0 TO 15
  SPCOLOR S_CLD+I,C
  SPR S_CLD+I,105,CLD_X[I],CLD_Y[I],FALSE,FALSE
 NEXT
END

' PARTICLES FUNCTIONS 

DEF INIT_PARTICLES
 DIM I
 FOR I=0 TO 23
  PTC_X[I]=RND(128)
  PTC_Y[I]=RND(128)
  PTC_S[I]=105+!RND(5)
  PTC_SX[I]=0.25+RNDF()*5
  PTC_D[I]=RNDF()
  PTC_C[I]=6+RND(2)
  SPCOLOR S_PTC+I,PAL%[PTC_C[I]]
  SPOFS S_PTC+I,,,-PTC_SX[I]*48
 NEXT
END

DEF UPDATE_PARTICLES
 DIM I
 FOR I=0 TO 23
  INC PTC_X[I],PTC_SX[I]
  INC PTC_Y[I],P8SIN(PTC_D[I])
  INC PTC_D[I],MIN(0.05,PTC_SX[I]/32)
  IF PTC_X[I]>128+4 THEN
   PTC_X[I]=-4
   PTC_Y[I]=RND(128)
  ENDIF
 NEXT
END

DEF DRAW_PARTICLES
 DIM I
 FOR I=0 TO 23
  SPR S_PTC+I,PTC_S[I],PTC_X[I],PTC_Y[I],FALSE,FALSE
 NEXT
END

' ZATSUYOS FUNCTIONS 

DEF CLEAR_ZATSUYOS
 CLEAR_EXTRA_SPRITES S_ZTS,S_ZTS+7
END

DEF DRAW_ZATSUYOS
 DIM I
 IF KL_PLY>0 THEN
  '-- DEAD
  DEC KL_PLY
  IF KL_PLY<=0 THEN
   CLEAR_ZATSUYOS
  ELSE
   DIM T=FLOOR(KL_PLY*2/5)
   FOR I=0 TO 7
    INC ZTS_X[I],ZTS_SX[I]
    INC ZTS_Y[I],ZTS_SY[I]
    SPCOLOR S_ZTS+I,PAL%[14+KL_PLY MOD 2]
    SPR S_ZTS+I,105+T,ZTS_X[I]-T/2,ZTS_Y[I]-T/2,FALSE,FALSE
   NEXT
  ENDIF
 ELSEIF ORB_E THEN
  '-- ORB
  DIM OFFS=TM_F/30
  FOR I=0 TO 7
   SPR S_ZTS+I,90,ORB_X+4+P8COS(OFFS+I/8)*8,ORB_Y+4+P8SIN(OFFS+I/8)*8,FALSE,FALSE
  NEXT
 ELSEIF TRB_E && TRB_ST==1 THEN
  '-- BIG CHEST
  FOR I=0 TO 7
   DEC ZTS_Y[I],ZTS_SY[I]
   SPR S_ZTS+I,105,ZTS_X[I],ZTS_Y[I],FALSE,FALSE
  NEXT
 ENDIF
END

' HELPER FUNCTIONS 

DEF CLAMP(VAL,A,B)
 RETURN MAX(A, MIN(B, VAL))
END

DEF APPR(VAL,TARGET,AMOUNT)
 IF VAL>TARGET THEN
  RETURN MAX(VAL-AMOUNT,TARGET)
 ELSE
  RETURN MIN(VAL+AMOUNT,TARGET)
 ENDIF
END

DEF MAYBE()
 RETURN RND(2)
END

' ROOM FUNCTIONS 

DEF RESTART_ROOM
 RST_RM=TRUE
 RST_DL=15
END

DEF NEXT_ROOM
 IF ROOM_X==2 && ROOM_Y==1 THEN
  MUSIC 30,500,7
 ELSEIF ROOM_X==3 && ROOM_Y==1 THEN
  MUSIC 20,500,7
 ELSEIF ROOM_X==4 && ROOM_Y==2 THEN
  MUSIC 30,500,7
 ELSEIF ROOM_X==5 && ROOM_Y==3 THEN
  MUSIC 30,500,7
 ENDIF

 IF ROOM_X==7 THEN
  LOAD_ROOM 0,ROOM_Y+1
 ELSE
  LOAD_ROOM ROOM_X+1,ROOM_Y
 ENDIF
END

DEF LOAD_ROOM X,Y
 HS_DSH=FALSE
 HS_KEY=FALSE

 '-- REMOVE EXISTING OBJECTS
 CLEAR_PLAYER
 SRG_CT=0
 BLN_CT=0
 FLR_CT=0
 CLEAR_SMOKES
 ICG_MD=0
 WAL_E=FALSE
 KEY_E=FALSE
 CST_E=FALSE
 PFM_CT=0
 MSG_E=FALSE
 TRB_E=FALSE
 ORB_E=FALSE
 FLG_E=FALSE
 CLEAR_ZATSUYOS

 '-- CURRENT ROOM
 ROOM_X=X
 ROOM_Y=Y

 '-- ENTITIES
 DIM TX,TY,EX,EY,T
 BG_BEGIN
 FOR TX=0 TO 15
  FOR TY=0 TO 15
   T=TILE_AT(TX,TY)
   EX=TX*8:EY=TY*8
   IF T==1   THEN INIT_PLAYER EX,EY,1
   IF T==8   THEN INIT_KEY EX,EY
   IF T==11  THEN INIT_PLATFORM EX,EY,-1
   IF T==12  THEN INIT_PLATFORM EX,EY,1
   IF T==18  THEN INIT_SPRING EX,EY
   IF T==20  THEN INIT_CHEST EX,EY
   IF T==22  THEN INIT_BALLOON EX,EY
   IF T==23  THEN INIT_FALL_FLOOR EX,EY
   IF T==26  THEN INIT_FRUIT EX,EY,1
   IF T==28  THEN INIT_FRUIT EX,EY,2
   IF T==64  THEN INIT_FAKE_WALL EX,EY
   IF T==86  THEN INIT_MESSAGE EX,EY
   IF T==96  THEN INIT_BIG_CHEST EX,EY
   IF T==118 THEN INIT_FLAG EX,EY

   BG_PUT 0,TX,TY,T*FGET(T,1)
   DIM BT=T
   IF T>=32&&T<64&&T MOD 16<8 THEN BT=40
   BG_PUT 1,TX,TY,BT*FGET(BT,2)

  NEXT
 NEXT
 BG_END

 CLEAR_EXTRA_SPRITES S_SRG+SRG_CT,S_SRG+15
 CLEAR_EXTRA_SPRITES S_BLN+BLN_CT*2,S_BLN+15
 CLEAR_EXTRA_SPRITES S_FLR+FLR_CT,S_FLR+15
 CLEAR_EXTRA_SPRITES S_PFM+PFM_CT,S_PFM+15

 IF IS_TITLE() THEN
  '-- CREDITS
  GPRIO 0
  P8PRINT "MATT THORSON",42,84,5
  P8PRINT "NOEL BERRY",46,90,5
  P8PRINT "PORTED BY OBONO",36,102,5
  P8PRINT "PRESS A OR B TO START",24,114,6
 ELSE
  '-- ROOM TITLE
  INIT_ROOM_TITLE
 ENDIF
END

DEF CLEAR_EXTRA_SPRITES SPR_F,SPR_T
 DIM I
 FOR I=SPR_F TO SPR_T
  SPCHR I,0
 NEXT
END

' BG EMULATION
' THE PI HAS NO BG SCREEN, SO EACH BG LAYER IS A
' 256x256 IMAGE RENDERED ONCE PER ROOM INTO SPPAGE
' AND DISPLAYED AS ONE SPRITE (SEE S_BG).
' BG_PUT REPLACES BGPUT, AND DRAW USES SPOFS FOR BGOFS.

DEF BG_BEGIN
 GPAGE 0,SPPAGE()
 GFILL 0,BG_V,511,BG_V+255,0
END

DEF BG_PUT L,TX,TY,N
 IF N==0 THEN RETURN 'TILE 0 DRAWS NOTHING
 DIM U=(N MOD 16)*16
 DIM V=(N DIV 16)*16
 GCOPY BG_SRC,U,V,U+15,V+15,L*256+TX*16,BG_V+TY*16,TRUE
END

DEF BG_END
 GPAGE 0,0
END

' UPDATE FUNCTION 

DEF UPDATE
 TM_F=(TM_F+1) MOD 30
 IF !TM_F && LEVEL_INDEX()<30 THEN
  TM_S=(TM_S+1) MOD 60
  INC TM_M,!TM_S
 ENDIF

 IF BGM_TM>0 THEN
  DEC BGM_TM
  IF BGM_TM<=0 THEN MUSIC 10,0,7
 ENDIF
 
 IF SFX_TM>0 THEN DEC SFX_TM
 
 '-- CANCEL IF FREEZE
 IF FREEZE>0 THEN DEC FREEZE:RETURN

 '-- RESTART (SOON)
 IF RST_RM AND RST_DL>0 THEN
  DEC RST_DL
  IF RST_DL<=0 THEN
   RST_RM=FALSE
   LOAD_ROOM ROOM_X,ROOM_Y
  ENDIF
 ENDIF

 '-- UPDATE EACH OBJECT
 UPDATE_CLOUDS
 UPDATE_PARTICLES
 UPDATE_PLAYER
 UPDATE_SPRINGS
 UPDATE_BALLOONS
 UPDATE_FALL_FLOORS
 UPDATE_SMOKES
 UPDATE_FRUIT
 UPDATE_FAKE_WALL
 UPDATE_KEY
 UPDATE_CHEST
 UPDATE_PLATFORMS
 UPDATE_BIG_CHEST
 UPDATE_ORB
 UPDATE_FLAG

 '-- START GAME
 IF IS_TITLE() THEN
  IF !ST_GM && (BTN[K_JP]+BTN[K_DS]) THEN
   MUSIC -1,0,0
   ST_GFL=50
   ST_GM=TRUE
   SFX 38
  ENDIF
  IF ST_GM THEN
   DEC ST_GFL
   IF ST_GFL<=-30 THEN BEGIN_GAME
  ENDIF
 ENDIF
END

' DRAWING FUNCTIONS 

DEF DRAW
 DIM I

 '-- SCREENSHAKE
 IF SHAKE>0 THEN
  DEC SHAKE
  SHX=-2+RND(5)
  SHY=-2+RND(5)
 ELSE
  SHX=0
  SHY=0
 ENDIF

 '-- DRAW TERRAIN
 DIM DIFF=0
 DIM CLIP_L=72+SHX*2
 DIM CLIP_R=327+SHX*2
 IF LEVEL_INDEX()==30 THEN
  DIFF=MAX(0,MIN(24,40-ABS(PLY_X+4-64)))*2
 ENDIF
 '   BGOFS WAS A SCROLL OFFSET (SCREEN=TILE-OFS), SO THE
 '   SPRITE POSITION IS ITS NEGATION. BGCLIP IS DROPPED:
 '   THE SPCLIP BELOW USED THE SAME RECT AND IS GLOBAL.
 SPOFS S_BG+0,CLIP_L-IS_TITLE()*8,SHY*2-8,BG_Z
 SPOFS S_BG+1,CLIP_L-IS_TITLE()*8,SHY*2-8,256
 SPCLIP CLIP_L+DIFF,0,CLIP_R-DIFF,239

 '-- START GAME FLASH
 IF ST_GM THEN
  DIM FADE_A,FADE_C
  IF ST_GFL>=10 THEN
   FADE_A=(ST_GFL MOD 10<5)*127
   FADE_C=255
  ELSE
   FADE_A=((10-ST_GFL) DIV 5)*64
   FADE_C=0
  ENDIF
  FADE RGB(FADE_A,FADE_C,FADE_C,FADE_C)
 ELSE
  FADE 0
 ENDIF

 '-- BACK COLOR
 DIM BG_COL=0
 IF FL_BG THEN
  BG_COL=TM_F/5
 ELSEIF NW_BG THEN
  BG_COL=2
 ENDIF
 SPCOLOR S_BAK,PAL%[BG_COL]

 '-- DRAW OBJECTS
 DRAW_PLAYER
 DRAW_SPRINGS
 DRAW_BALLOONS
 DRAW_FALL_FLOORS
 DRAW_SMOKES
 DRAW_FRUIT
 DRAW_FAKE_WALL
 DRAW_KEY
 DRAW_CHEST
 DRAW_PLATFORMS
 DRAW_BIG_CHEST
 DRAW_MESSAGE
 DRAW_ORB
 DRAW_FLAG
 DRAW_CLOUDS
 DRAW_PARTICLES
 DRAW_ZATSUYOS
 DRAW_ROOM_TITLE

END

DEF DRAW_TIME X,Y
 DIM S=TM_S
 DIM M=TM_M MOD 60
 DIM H=FLOOR(TM_M/60)
 DIM T$=FORMAT$("%02D:%02D:%02D",H,M,S)
 RECTFILL X,Y,X+32,Y+6,0
 P8PRINT T$,X+1,Y+1,7
END

' EXTRA BUTTON FUNCTIONS 

DEF EXTRA_BTN
 DIM I,BTN=BUTTON()
 IF IS_TITLE() THEN RETURN
 IF BTN==BTN_L%+BTN_R% THEN
  '-- RESET GAME
  FOR I=0 TO 8
   VSYNC 1
   FADE RGB(I*32,0,0,0)
  NEXT
  TITLE_SCREEN
 ELSEIF BTN AND BTN_Y% THEN
  '-- PAUSE
  BGMVOL 0,16
  RECTFILL 103,113,123,119,0
  P8PRINT "PAUSE",104,114,10
  FADE RGB(128,0,0,0)
  FOR I=0 TO 2
   VSYNC 1
   I=I-((BUTTON() AND BTN_Y%)!=BTN_Y%*(I MOD 2))
  NEXT
  RECTFILL 103,113,123,119,-1
  BGMVOL 0,64
 ENDIF
END

' PICO-8 WRAPPER FUNCTIONS 

DEF POLL_BTN
 DIM B,SX,SY,X,Y
 B=BUTTON()
 STICK OUT SX,SY
 X=((B AND BTN_RIGHT%)>0)-((B AND BTN_LEFT%)>0)+SX
 'PI: STICK Y IS ↓POSITIVE (3DS WAS ↑POSITIVE)
 Y=((B AND BTN_DOWN% )>0)-((B AND BTN_UP%  )>0)+SY
 BTN[K_LF]=X <=-0.25
 BTN[K_RT]=X >= 0.25
 BTN[K_UP]=Y <=-0.25
 BTN[K_DN]=Y >= 0.25
 BTN[K_JP]=(B AND BTN_A%)>0
 BTN[K_DS]=(B AND BTN_B%)>0
END

DEF SPR I,S,X,Y,FX,FY
 SPCHR I,S OR 128*FX OR 256*FY
 IF S!=0 THEN
  X=FLOOR(X+SHX)*2+72
  Y=FLOOR(Y+SHY)*2-8
  SPOFS I,X,Y
 ENDIF
END

DEF RECTFILL X1,Y1,X2,Y2,C
 X1=FLOOR(X1)*2+72
 Y1=FLOOR(Y1)*2-8
 X2=FLOOR(X2)*2+73
 Y2=FLOOR(Y2)*2-7
 DIM COL
 IF C>=0 THEN COL=PAL%[C]
 GFILL X1,Y1,X2,Y2,COL
END

DEF P8PRINT S$,X,Y,C
 X=FLOOR(X)*2+72
 Y=FLOOR(Y)*2-8
 DIM I,L=LEN(S$)-1,S
 FOR I=0 TO L
  S=ASC(MID$(S$,I,1))-32
  GCOPY 1,S*8,C*12,S*8+7,11+C*12,X,Y,FALSE
  X=X+8
 NEXT
END

DEF P8COS(V)
 RETURN COS(V*2*PI())
END

DEF P8SIN(V)
 RETURN -SIN(V*2*PI())
END

DEF MGET(X,Y)
 RETURN MAP%[Y*32+X/4]>>(3-X MOD 4)*8 AND 255
END

DEF FGET(I,FLAG)
 DIM F=GFF%[I/4]>>(3-I MOD 4)*8
 RETURN (F AND 1<<FLAG)>0
END

DEF MUSIC NUM,DELAY,MASK
 IF NUM<0 THEN BGMSTOP ELSE BGMPLAY 0,NUM+200
END

DEF SFX NUM
 BGMPLAY 1,NUM+128
END

' SETUP FUNCTIONS 

DEF SETUP
 LOAD "DAT:PAL",PAL%
 LOAD "DAT:GFF",GFF%
 LOAD "DAT:MAP",MAP%
 SETUP_IMAGES
 SETUP_FONT
 GPAGE 0,0

 SETUP_AUDIO
END

' AUDIO: load pre-rendered OGGs instead of on-device synthesis.
' (The Pi renders WAVSET/MML harshly and drops {} macro music, so the
'  sound is rendered offline by audiotool/ and played back via BGMSETF.)
' Music track n  -> tune 200+n*10  (MUSIC fn plays BGMPLAY 0,NUM+200)
' Gameplay SFX n -> tune n+128      (SFX fn plays BGMPLAY 1,NUM+128)
DEF SETUP_AUDIO
 DIM SND$="/CELESTE/SND/"
 DIM I,N
 FOR I=0 TO 4
  BGMSETF 200+I*10,SND$+"music"+STR$(I)+".ogg"
 NEXT
 RESTORE @SFXIDS
 FOR I=0 TO 20
  READ N
  BGMSETF N+128,SND$+"sfx"+FORMAT$("%02D",N)+".ogg"
 NEXT
END

@SFXIDS
DATA 0,1,2,3,4,5,6,7,8,9,13,14,15,16,23,35,37,38,51,54,55

' CHARACTER IMAGES

DEF SETUP_IMAGES
 DIM GFX%[1024]
 DIM I,X,Y,V,PTN
 LOAD "DAT:GFX",GFX%

 '-- DRAW
 GPAGE 0,SPPAGE()
 GFILL 0,0,255,127,0
 FOR Y=0 TO 63
  FOR X=0 TO 127
   IF X MOD 8==0 THEN PTN=GFX%[Y*16+X/8]
   V=PTN >> (7-X MOD 8)*4 AND 15
   IF V>0 THEN
    GFILL X*2,Y*2,X*2+1,Y*2+1,PAL%[V]
   ENDIF
  NEXT
 NEXT

 '-- TILE SOURCE SNAPSHOT (REPLACES BG OPTIMIZE)
 '   PI HAS NO BG SCREEN, SO TERRAIN IS RENDERED INTO
 '   SPPAGE AND SHOWN AS 2 SPRITES. THIS SNAPSHOT MUST
 '   BE TAKEN BEFORE THE HAIR PARTS OVERWRITE TILES 73~127.
 GPAGE 0,BG_SRC
 GCOPY SPPAGE(),0,0,255,127,0,0,TRUE

 '-- EXRRACT HAIR PARTS
 GPAGE 0,SPPAGE()
 GFILL 144,64,255,127,0
 GCOLOR #WHITE
 FOR Y=0 TO 15:FOR X=16 TO 127
  IF GSPOIT(X,Y)==PAL%[8] THEN
   GPSET X,Y,0:GPSET X+128,Y+64
  ENDIF
 NEXT:NEXT

 '-- PARTICLE
 GFILL 144,80,145,81
 FOR I=0 TO 1
  GFILL 160+I*16,82,165+I*20,83+I*4
  GFILL 162+I*16,80,163+I*20,85+I*4
 NEXT
 FOR I=0 TO 6
  GFILL 144+I*16,96,145+I*18,97+I*2
 NEXT
 GFILL 240,112,255,127

 '-- SPDEF
 DIM A,W,H,M
 FOR A=0 TO 3
  FOR I=0 TO 127
   X=(I MOD 16)*16
   Y=(I DIV 16)*16
   W=16:H=16:M=0
   IF I==11||I==64||I==96||I==112 THEN W=32
   IF I==64||I==96 THEN H=32
   IF I>=89 && I<=91 THEN M=(I-89)*2
   SPDEF I+A*128,X,Y,W,H,M,M,#SPREVH*A+1
  NEXT
 NEXT

 '-- SPSET
 FOR I=0 TO S_BAK-1
  SPSET I,0
 NEXT

 '-- BACKGROUND
 SPSET S_BAK,127
 SPOFS S_BAK,64,0,1024
 SPSCALE S_BAK,17,15

 '-- TERRAIN LAYERS (REPLACE BG LAYERS 0/1)
 '   EACH IS A 256x256 SPRITE OVER A PRE-RENDERED
 '   ROOM IMAGE HELD ON SPPAGE.
 FOR I=0 TO 1
  SPSET S_BG+I,I*256,BG_V,256,256,1
 NEXT
END

' FONT IMAGES

DEF SETUP_FONT
 DIM FONT%[20]
 DIM I,CODE,X,Y,DX,DY,V,PTN,C
 LOAD "DAT:FONT",FONT%

 GPAGE 0,1
 FOR I=0 TO 39
  PTN=FONT%[I/2] >> (1-I MOD 2)*16
  FOR X=0 TO 2
   DX=CODE*8+X*2
   FOR Y=0 TO 4
    IF PTN >> 14-Y-X*5 AND 1 THEN
     FOR C=0 TO 15
      DY=C*12+Y*2
      GFILL DX,DY,DX+1,DY+1,PAL%[C]
     NEXT
    ENDIF
   NEXT
  NEXT
  IF I==0 THEN
   CODE=13
  ELSEIF I==2 THEN
   CODE=16
  ELSEIF I==13 THEN
   CODE=33
  ELSE
   INC CODE
  ENDIF
 NEXT
END

' SOUND TONE

' THE PI HAS NO WAVSETA (ARRAY FORM). ITS WAVSET IS THE
' SAME AS THE 3DS ONE AND TAKES A HEX WAVEFORM STRING,
' SO THE GENERATORS BUILD STRINGS INSTEAD OF ARRAYS.
' WAVSET CAPS AT 512 SAMPLES, SO THE NOISE DROPS FROM
' 65536 TO 512 (SHORTER LOOP, SLIGHTLY MORE TONAL).

DEF SETUP_TONE
 DIM I,W$
 FOR I=0 TO 7
  IF I!=6 THEN
   READ W$
   WAVSET 224+I,127,127,127,127,GEN_WAVE$(W$,512),33
  ELSE
   WAVSET 224+I,127,127,127,127,GEN_NOISE$(512),33
  ENDIF
 NEXT
END

DATA "128FF,25600,12880"
DATA "219FF,07400,21980"
DATA "256DA,00126,25580"
DATA "001C6,255C6,0013A,2553A"
DATA "001DA,170DA,00153,34053"
DATA "075E6,12837,128AB,12837,05380"
DATA "085DA,086DA,17026,08626,08580"

DEF GEN_WAVE$(PTN$,N)
 DIM I,J,L,P,C,T,W,O$
 T=128:L=N-1
 FOR I=0 TO L
  IF J==C THEN
   J=0:W=T
   C=VAL(MID$(PTN$,P*6,3))
   T=VAL("&H"+MID$(PTN$,P*6+3,2))
   P=P+1
  ENDIF
  J=J+1
  O$=O$+HEX$(FLOOR(W+(T-W)*J/C),2)
 NEXT
 RETURN O$
END

DEF GEN_NOISE$(N)
 DIM I,W,O$
 FOR I=0 TO N-1
  IF I MOD 16==0 THEN W=RND(192)
  O$=O$+HEX$(W+RND(64),2)
 NEXT
 RETURN O$
END

' SOUND EFFECT

DEF SETUP_SFX LIB$[]
 DIM SFX%[0]
 LOAD "DAT:SFX",SFX%

 DIM I,J,SPD,CNT,IDX
 DIM FREQ,L_FREQ,INST,L_INST
 DIM VOL ,L_VOL ,EFC ,L_EFC
 DIM MML$,SPD$,FIN$,FOUT$,HEAD$,NOTE$

 FOR I=0 TO 63
  SPD=GET8(SFX%,I*84+1)
  CNT=GET8(SFX%,I*84+3)
  IF SPD==1 THEN
   SPD$="T225L128"
  ELSEIF SPD<=3 THEN
   SPD$="T"+STR$(FLOOR(450/SPD))+"L64"
  ELSEIF SPD<=6 THEN
   SPD$="T"+STR$(FLOOR(900/SPD))+"L32"
  ELSEIF SPD<=12 THEN
   SPD$="T"+STR$(FLOOR(1800/SPD))+"L16"
  ELSE
   SPD$="T"+STR$(FLOOR(3600/SPD))+"L8"
  ENDIF
  IF CNT==0 THEN CNT=32
  L_FREQ=-1
  L_INST=-1
  L_VOL =-1
  L_EFC =0
  FIN$="@E"+STR$(127-SPD)+",127,127,127"
  FOUT$="@E127,"+STR$(127-SPD)+",0,127"
  MML$="@MP32,2,16,0@MOF"
  FOR J=0 TO CNT-1
   '-- GET PARAMS
   IDX=I*168+8+J*5
   FREQ=GET4(SFX%,IDX)*16+GET4(SFX%,IDX+1)
   INST=GET4(SFX%,IDX+2)
   VOL =GET4(SFX%,IDX+3)
   EFC =GET4(SFX%,IDX+4)
   IF VOL==0 THEN
    L_VOL=0
    MML$=MML$+"R"
    CONTINUE
   ENDIF
   '-- ATTRIBUTE
   HEAD$=""
   IF EFC==2 && L_EFC!=2 THEN HEAD$="@MON"
   IF EFC!=2 && L_EFC==2 THEN HEAD$="@MOF"
   IF INST!=L_INST THEN
    HEAD$=HEAD$+"@"+STR$(INST+224)
   ENDIF
   IF VOL!=L_VOL THEN
    HEAD$=HEAD$+"V"+STR$(VOL*18)
   ENDIF
   IF EFC==4 || EFC==1 && L_VOL==0 THEN
    HEAD$=HEAD$+FIN$
   ELSEIF EFC==5 THEN
    HEAD$=HEAD$+FOUT$
   ELSE
    HEAD$=HEAD$+"@ER"
   ENDIF
   '-- NOTE
   NOTE$=NOTE_MML$(FREQ)
   IF EFC==1 && L_FREQ>=0 THEN
    NOTE$=NOTE_MML$(L_FREQ)+"_"+NOTE$
   ELSEIF EFC==6 THEN
    NOTE$="L32[GA+]2L8" '--TRICK!
   ENDIF
   IF L_VOL>0 && FREQ==L_FREQ && L_EFC<5 THEN
    HEAD$="&"+HEAD$
   ENDIF
   '-- APPEND MML
   MML$=MML$+HEAD$+NOTE$
   L_FREQ=FREQ
   L_INST=INST
   L_VOL =VOL
   L_EFC =EFC
  NEXT
  LIB$[I]=MML$
  BGMSET I+128,SPD$+MML$
 NEXT
END

DEF GET8(D%,I)
 RETURN D%[I/4]>>(3-I MOD 4)*8 AND 255
END

DEF GET4(D%,I)
 RETURN D%[I/8]>>(7-I MOD 8)*4 AND 15
END

DEF NOTE_MML$(F)
 DIM O$="O"+STR$(F DIV 12+2)
 DIM C=F MOD 12
 C=(C+(C>4)+4) MOD 14
 RETURN O$+CHR$(65+C/2)+"#"*(C MOD 2)
END

' MUSIC

DEF SETUP_MUSIC LIB$[]
 DIM I,MML$
 FOR I=0 TO 4
  READ MML$
  BGMSET 200+I*10,GEN_MUSIC_MML$(MML$,LIB$)
 NEXT
END

DEF GEN_MUSIC_MML$(BASE$,LIB$[])
 DIM I,P,P$,H$,DEF$
 REPEAT
  I=INSTR(I,BASE$,"{")
  IF I>=0 THEN
   P$=MID$(BASE$,I,4)
   IF INSTR(H$,P$)==-1 THEN
    H$=H$+P$
    P=VAL(MID$(P$,2,2))
    DEF$=DEF$+P$+"="+LIB$[P]+"}"
   ENDIF
   I=I+5
  ENDIF
 UNTIL I==-1
 RETURN DEF$+BASE$
END

DATA "T112:1[L16[{M10}]6L32[{M20}]4L16[{M10}]3]:2[L8{M21}L16[{M22}]2L8{M11}{M19}L16[{M22}]2{M17}]:3[L8{M63}L16[{M12}]4[{M18}]2[{M12}]2{M18}]"
DATA "T75:1[L16[{M24}]2L32[{M27}]4[{M31}]2{M30}{M32}]:2[L16[{M25}]2{M28}{M29}L32[{M33}]2{M34}{M36}]:3[L32{M26}]"
DATA ":1[L32[{M41}]8[[{M41}]2[{M44}]2]2L8{M48}L32[{M49}]4[{M50}]4]:2[L8[{M42}]2L16[{M43}]4[{M45}]2L8{M52}{M53}]:3[L16[{M39}]8[{M47}]8[{M46}]4[{M39}]8]"
DATA "T112:1[L16{M61}]:2[L16[{M63}]3L4{M62}]"
DATA "T112:1[L16{M56}{M57}]:2[L16{M58}{M59}]:3[L16{M60}]"

' EOF

