*define
useescspc
defsub init
defsub main
defsub dispatchbtn
defsub moveRight
defsub moveDown
defsub moveLeft
defsub turnLeft
defsub turnRight
defsub dropDown
defsub makeBlockSprite
defsub loadBlockSprite
defsub loadBlockData
defsub numtocell
defsub blockRightEdge
defsub blockBottomEdge
defsub blockOnBottom
defsub nextBlock
defsub blockLeftEdge
defsub loadField
defsub blockOnLeft
defsub blockOnRight
defsub isEndOfPlay
defsub loadNextBlock
defsub loadNextFrame
defsub loadScore
defsub loadLevel

; 別名
numalias CURRENT_SPRITE,200
numalias NEXT_SPRITE,201
numalias NEXT_TEXT_SPRITE,202
numalias NEXT_FRAME_SPRITE,203
numalias DELETE_LINE_SPRITE,100
numalias FIELD_SPRITE,999
numalias FIELD_BOTTOM,479
numalias FIELD_LEFT,220
numalias FIELD_RIGHT,420-1
numalias TRUE,1
numalias FALSE,0

numalias BLOCK_COUNT,7
numalias BLOCK_PATTERN,4

numalias field,0
numalias block,1
numalias delete_lines,2
numalias rest_lines,3

numalias FIELD_LINE,24
numalias FIELD_COLUMN,10
numalias FIELD_CELLSIZE,20
dim ?field[FIELD_LINE+3][FIELD_COLUMN+3]
numalias BLOCK_WIDTH,4
numalias BLOCK_HEIGHT,4
dim ?block[BLOCK_COUNT * BLOCK_PATTERN][BLOCK_HEIGHT-1][BLOCK_WIDTH-1]
dim ?delete_lines[BLOCK_HEIGHT]
dim ?rest_lines[FIELD_LINE+BLOCK_HEIGHT]

; %変数
numalias block_file,0
numalias block_id,1
numalias block_sprite,2
numalias block_x,3
numalias block_y,4
numalias btn,5
numalias cell,6
numalias current_block,7
numalias current_pattern,8
numalias elapse,9
numalias line,10
numalias next_block,11
numalias next_label,12
numalias next_pattern,13
numalias number,14
numalias pattern_id,15
numalias sprite,16
numalias tmp,17
numalias result,18
numalias column_bottom,19
numalias field_x,20
numalias field_y,21
numalias level,22
numalias score,23
numalias value,24
numalias cell0,30
numalias cell1,31
numalias cell2,32
numalias cell3,33
game

*start
loadBlockData "block.csv"

*init
; レベル表示
mov %level,5
loadLevel
mov %elapse,1000-%level*100
mov %block_x,300
mov %block_y,0
rnd %current_block,BLOCK_COUNT
rnd %current_pattern,BLOCK_PATTERN
makeBlockSprite $current_block,%current_block,%current_pattern
loadBlockSprite CURRENT_SPRITE,$current_block
rnd %next_block,BLOCK_COUNT
rnd %next_pattern,BLOCK_PATTERN
makeBlockSprite $next_block,%next_block,%next_pattern
loadNextBlock $next_block
; NEXT表示
loadNextFrame
; フレーム表示
bar 0,100,FIELD_LEFT-2,0,2,480,100,#ff0000
bar 1,100,FIELD_RIGHT+1,0,2,480,100,#ff0000
; スコア表示
mov %score,1000
loadScore

*main
goto *wait_loop


;; 待ちループ
*wait_loop
btndef clear
getcursor
getzxc
btntime %elapse
btnwait %btn
mov $btn,"*waist_time"
dispatchbtn $btn,%btn
goto $btn


;; 時間消費再ループ
*waist_time
getbtntimer %tmp
sub %elapse,%tmp
if %elapse<1 mov %elapse,1
goto *wait_loop


;; 入力振り分け
*dispatchbtn
getparam s%next_label,%btn
if %btn==-40 return
if %btn==-41 moveRight $%next_label:return
if %btn==-42 moveDown $%next_label:return
if %btn==-43 moveLeft $%next_label:return
if %btn==-51 turnLeft $%next_label:return
if %btn==-52 turnRight $%next_label:return
if %btn==-53 dropDown $%next_label:return
if %btn==-2 mov %elapse,1000-%level*100:mov $%next_label,"*wait_loop":moveDown $%next_label:return
if %btn==-10 end
if %btn==-1 end
return


;; 右移動
*moveRight
getparam s%next_label
blockRightEdge %tmp,%current_block,%current_pattern
if %tmp>=FIELD_RIGHT return
blockOnRight %tmp,%current_block,%current_pattern
if %tmp==TRUE return
msp CURRENT_SPRITE,FIELD_CELLSIZE,0
add %block_x,FIELD_CELLSIZE
return


;; 下移動
*moveDown
getparam s%next_label
blockBottomEdge %tmp,%current_block,%current_pattern
if %tmp>=FIELD_BOTTOM nextBlock $%next_label:return
blockOnBottom %tmp,%current_block,%current_pattern
if %tmp==TRUE nextBlock $%next_label:return
msp CURRENT_SPRITE,0,FIELD_CELLSIZE
add %block_y,FIELD_CELLSIZE
return


;; 左移動
*moveLeft
getparam s%next_label
blockLeftEdge %tmp,%current_block,%current_pattern
;itoa $tmp,%tmp:mesbox $tmp,""
if %tmp<=FIELD_LEFT return
blockOnLeft %tmp,%current_block,%current_pattern
if %tmp==TRUE return
msp CURRENT_SPRITE,-FIELD_CELLSIZE,0
sub %block_x,FIELD_CELLSIZE
return


;; 左回転
*turnLeft
getparam s%next_label
dec %current_pattern
if %current_pattern<0 mov %current_pattern,BLOCK_PATTERN-1
makeBlockSprite $current_block,%current_block,%current_pattern
loadBlockSprite CURRENT_SPRITE,$current_block
return


;; 右回転
*turnRight
getparam s%next_label
inc %current_pattern
if %current_pattern==BLOCK_PATTERN mov %current_pattern,0
makeBlockSprite $current_block,%current_block,%current_pattern
loadBlockSprite CURRENT_SPRITE,$current_block
return


;; 下まで落とす
*dropDown
getparam s%next_label
; 遅すぎるので、本来はダイレクトに動かす。
*dropDown_loop
blockBottomEdge %tmp,%current_block,%current_pattern
if %tmp>=FIELD_BOTTOM return
blockOnBottom %tmp,%current_block,%current_pattern
if %tmp==TRUE return
msp CURRENT_SPRITE,0,FIELD_CELLSIZE
add %block_y,FIELD_CELLSIZE
goto *dropDown_loop
return


;; 確定して次へ
*nextBlock
getparam s%next_label
; 確定する
mov %field_x,(%block_x-FIELD_LEFT)/20
mov %field_y,%block_y/20
mov %block_id,%current_block * BLOCK_PATTERN + %current_pattern
for %line=0 to BLOCK_HEIGHT-1
	if %field_y+%line<0 goto *determineBlock_continue
	for %cell=0 to BLOCK_WIDTH-1
		if %field_x+%cell>=0 mov ?field[%field_y+%line][%field_x+%cell],?field[%field_y+%line][%field_x+%cell]+?block[%block_id][%line][%cell]
	next
*determineBlock_continue
next
; 消去判定
*deleteBlock
mov ?delete_lines[0],0
for %line=0 to BLOCK_HEIGHT-1
	mov %tmp,1
	if %field_y+%line<0 goto *deleteBlock_continue
	; セルすべてが確定した場合は消去対象とする。
	for %cell=0 to FIELD_COLUMN-1
		if ?field[%field_y+%line][%cell]==0 mov %tmp,0:break
	next
	if %tmp==0 goto *deleteBlock_continue
	mov ?delete_lines[?delete_lines[0]+1],%field_y+%line
	mov ?delete_lines[0],?delete_lines[0]+1
*deleteBlock_continue
next
if ?delete_lines[0]==0 goto *nextBlock_donext
;itoa $tmp,?delete_lines[0]:mesbox $tmp,""
;itoa $tmp,?delete_lines[1]:mesbox $tmp,""
; 削除エフェクト処理
for %tmp=1 to ?delete_lines[0]
	lsp DELETE_LINE_SPRITE+%tmp-1,":s/20,20,0;#ffffaa■■■■■■■■■■",FIELD_LEFT,?delete_lines[%tmp]*FIELD_CELLSIZE
next
rnd2 %tmp,2,14
print %tmp,500
; 残存行
mov ?rest_lines[0],0
for %line=%field_y+BLOCK_HEIGHT-1 to 0 step -1
	; 消去対象でなければ残存行とする。
	for %tmp=1 to ?delete_lines[0]
		if %line==?delete_lines[%tmp] break *restLines_continue
	next
	mov ?rest_lines[?rest_lines[0]+1],%line
	mov ?rest_lines[0],?rest_lines[0]+1
*restLines_continue
next
;mov $tmp,""
;for %tmp=1 to ?rest_lines[0]
;	itoa $line,?rest_lines[%tmp]
;	add $tmp,$line+","
;next
;mesbox $tmp,"rest_lines"
; 消去処理
for %tmp=1 to ?rest_lines[0]
	; 残存行からフィールドへコピーする。
	for %cell=0 to FIELD_COLUMN-1
		mov ?field[%field_y+BLOCK_HEIGHT-%tmp][%cell],?field[?rest_lines[%tmp]][%cell]
	next
next
; 削除分を埋めておく。
for %line=0 to ?delete_lines[0]-1
	for %cell=0 to FIELD_COLUMN-1
		mov ?field[%line][%cell],0
	next
next
; 削除エフェクト完了処理
for %tmp=1 to ?delete_lines[0]
	csp DELETE_LINE_SPRITE+%tmp-1
next
; 得点加算
add %score,(%level+1)*?delete_lines[0]*100
loadScore
*nextBlock_donext
; 次のブロックへ
loadField
mov %elapse,1000-%level*100
mov %block_x,300
mov %block_y,0
mov %current_block,%next_block
mov %current_pattern,%next_pattern
; 次のブロックが衝突していたらアウト
isEndOfPlay %tmp,%current_block,%current_pattern
if %tmp==0 skip 3
yesnobox %tmp,"ゲームを終了しますか?","失敗しました":if %tmp==1 end
if %tmp==0 reset
makeBlockSprite $current_block,%current_block,%current_pattern
loadBlockSprite CURRENT_SPRITE,$current_block
rnd %next_block,BLOCK_COUNT
rnd %next_pattern,BLOCK_PATTERN
makeBlockSprite $next_block,%next_block,%next_pattern
loadNextBlock $next_block
return


;; スプライトを作成する。
*makeBlockSprite
getparam s%sprite,%block_id,%pattern_id
mov %block_id,%block_id * BLOCK_PATTERN + %pattern_id
mov $%sprite,""
for %line=0 to BLOCK_PATTERN-1
	numtocell $cell,?block[%block_id][%line][0]:add $%sprite,$cell
	numtocell $cell,?block[%block_id][%line][1]:add $%sprite,$cell
	numtocell $cell,?block[%block_id][%line][2]:add $%sprite,$cell
	numtocell $cell,?block[%block_id][%line][3]:add $%sprite,$cell
	if %line!=BLOCK_PATTERN-1 add $block_sprite,"\" ; "
next
return


;; スプライトを読み込む。
*loadBlockSprite
getparam %sprite,$sprite
strsp %sprite,$sprite,%block_x,%block_y,4,4,20,20,0,0,0,0,#ffffff
return


;; ファイルからブロックデータを読み込む。
*loadBlockData
getparam $block_file
csvopen $block_file,"r"
mov %block_id,0
mov %line,0
mov %pattern_id,0
*loadBlockData_loop
csveof %tmp
if %tmp!=0 goto *loadBlockData_end
for %line=0 to BLOCK_PATTERN-1
	csvread %cell0,%cell1,%cell2,%cell3
	movl ?block[%block_id * BLOCK_PATTERN + %pattern_id][%line],%cell0,%cell1,%cell2,%cell3
next
inc %pattern_id
if %pattern_id==BLOCK_PATTERN mov %pattern_id,0:inc %block_id
if %block_id==BLOCK_COUNT goto *loadBlockData_end
goto *loadBlockData_loop
*loadBlockData_end
csvclose
return


;; 数値を文字に変える。
*numtocell
getparam s%cell,%number
if %number!=0 mov $%cell,"■":return
mov $%cell," "
return


*blockRightEdge
getparam i%result,%block_id,%pattern_id
; 端から調べて一つでも有効なセルがあればそれが端
for %cell=BLOCK_WIDTH-1 to 0 step -1
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][0][%cell]!=0 break
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][1][%cell]!=0 break
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][2][%cell]!=0 break
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][3][%cell]!=0 break
next
mov %%result,%block_x+(%cell+1)*FIELD_CELLSIZE-1
return


*blockBottomEdge
getparam i%result,%block_id,%pattern_id
; 端から調べて一つでも有効なセルがあればそれが端
for %line=BLOCK_HEIGHT-1 to 0 step -1
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][%line][0]!=0 break
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][%line][1]!=0 break
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][%line][2]!=0 break
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][%line][3]!=0 break
next
mov %%result,%block_y+(%line+1)*FIELD_CELLSIZE-1
return


*blockOnBottom
getparam i%result,%block_id,%pattern_id
mov %%result,FALSE
mov %field_x,(%block_x-FIELD_LEFT)/FIELD_CELLSIZE
;itoa $field_x,%field_x:mesbox $field_x,""
; 各列の下端について判定する。
for %cell=0 to BLOCK_WIDTH-1
	if %field_x+%cell<0 goto *blockOnBottom_continue
	mov %column_bottom,-1
	for %line=BLOCK_HEIGHT-1 to 0 step -1
		if ?block[%block_id*BLOCK_PATTERN+%pattern_id][%line][%cell]!=0 mov %column_bottom,%block_y+(%line+1)*FIELD_CELLSIZE-1:break
	next
	; 各列についてフィールド上端で判定する。
	for %field_y=0 to FIELD_LINE-1
		if ?field[%field_y][%field_x+%cell]!=0 break
	next
	if %column_bottom>=%field_y*FIELD_CELLSIZE-1 mov %%result,TRUE:break
*blockOnBottom_continue
next
return


*blockLeftEdge
getparam i%result,%block_id,%pattern_id
; 端から調べて一つでも有効なセルがあればそれが端
for %cell=0 to BLOCK_WIDTH-1
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][0][%cell]!=0 break
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][1][%cell]!=0 break
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][2][%cell]!=0 break
	if ?block[%block_id*BLOCK_PATTERN+%pattern_id][3][%cell]!=0 break
next
mov %%result,%block_x+%cell*FIELD_CELLSIZE
return


;; フィールドを読み込む。
*loadField
mov $sprite,""
for %field_y=0 to FIELD_LINE-1
	mov $field_y,""
	for %field_x=0 to FIELD_COLUMN-1
	numtocell $field_x,?field[%field_y][%field_x]
	add $field_y,$field_x
	next
	add $sprite,$field_y
	if %field_y!=FIELD_LINE-1 add $sprite,"\" ; "
next
strsp FIELD_SPRITE,$sprite,FIELD_LEFT,0,FIELD_COLUMN,FIELD_LINE,FIELD_CELLSIZE,FIELD_CELLSIZE,0,0,0,0,#c0c0c0
return


*blockOnLeft
getparam i%result,%block_id,%pattern_id
mov %%result,FALSE
mov %field_x,(%block_x-FIELD_LEFT)/FIELD_CELLSIZE
if %field_x==0 return
mov %field_y,%block_y/FIELD_CELLSIZE
mov %block_id,%block_id*BLOCK_PATTERN+%pattern_id
; 各行の左端について判定する。
for %line=0 to BLOCK_HEIGHT-1
	; 有効領域外は無視
	if %field_y+%line>=FIELD_LINE goto *blockOnLeft_continue
	for %cell=0 to BLOCK_WIDTH-1
		if ?block[%block_id][%line][%cell]!=0 break
	next
	; 有効セルの無い行は無視
	if %cell==BLOCK_WIDTH goto *blockOnLeft_continue
	; 各行についてフィールド衝突判定する。
	if %field_x+%cell==0 mov %%result,TRUE:break
	if ?field[%field_y+%line][%field_x+%cell-1]!=0 mov %%result,TRUE:break
*blockOnLeft_continue
next
return


*blockOnRight
getparam i%result,%block_id,%pattern_id
mov %%result,FALSE
mov %field_x,(%block_x-FIELD_LEFT)/FIELD_CELLSIZE
if %field_x==FIELD_COLUMN-1 return
mov %field_y,%block_y/FIELD_CELLSIZE
mov %block_id,%block_id*BLOCK_PATTERN+%pattern_id
; 各行の右端について判定する。
for %line=0 to BLOCK_HEIGHT-1
	; 有効領域外は無視
	if %field_y+%line>=FIELD_LINE goto *blockOnRight_continue
	for %cell=BLOCK_WIDTH-1 to 0 step -1
		if ?block[%block_id][%line][%cell]!=0 break
	next
	; 有効セルの無い行は無視
	if %cell==-1 goto *blockOnRight_continue
;	itoa $cell,%cell:mesbox $cell,"blockOnRight cell"
	; 各行についてフィールド衝突判定する。
	if %field_x+%cell==FIELD_LINE-1 mov %%result,TRUE:break
	if ?field[%field_y+%line][%field_x+%cell+1]!=0 mov %%result,TRUE:break
*blockOnRight_continue
next
return


;; プレイ終了かどうかを確認する。
*isEndOfPlay
getparam i%result,%block_id,%pattern_id
mov %%result,FALSE
mov %block_id,%block_id * BLOCK_PATTERN + %pattern_id
for %line=0 to BLOCK_HEIGHT-1
	for %cell=0 to BLOCK_WIDTH-1
		if ?block[%block_id][%line][%cell]==1 && ?field[%line][%cell]!=0 mov %%result,TRUE:break
	next
next
return


*loadNextBlock
getparam $sprite
loadBlockSprite NEXT_SPRITE,$sprite
amsp NEXT_SPRITE,FIELD_RIGHT+60,60
return


*loadNextFrame
lsp NEXT_TEXT_SPRITE,":s/20,20,0;#ffffffNEXT",FIELD_RIGHT+60,0
lsp NEXT_FRAME_SPRITE,":s/200,200,0;#ffffff□",FIELD_RIGHT,0
return


*loadScore
itoa $score,%score
len %cell,$score
for %tmp=%cell-1 to 0 step -1
	mid $value,$score,%tmp,1
	atoi %value,$value
	prnum %tmp,%value,FIELD_LEFT-20-20*(%cell-1-%tmp+3),20,20,20,#ffffff
next
return


*loadLevel
return