packages feed

tetris-0.27177: Main.hs

module Main where

import Graphics.UI.GLUT hiding (Red, Green, Blue, rotate)
import System.Exit
import Data.List (union, delete)
import Data.IORef
import Data.Bits ((.&.))

import Board
import Pad
import Player


screenWidth  = 320
screenHeight = 400
-- タイマの間隔
timerInterval :: Int
timerInterval = 1000 `div` frameRate

--------------------------------
-- エントリ

data GameStat = Title | Game | GameOver

main :: IO ()
main = do
        initialize "Tetris" []
	gameStatRef <- newIORef Title
	playerRef <- newIORef initialPlayer
	padRef <- newIORef newPad

	--GLUTの初期化
	initialDisplayMode $= [RGBAMode, DoubleBuffered]
	initialWindowSize $= Size screenWidth screenHeight

	--ウィンドウを作る
	createWindow "Tetris in Haskell & GLUT"

	--表示に使うコールバック関数の指定
	displayCallback $= display gameStatRef playerRef

	--キーボードやマウスのコールバック
	keyboardMouseCallback $= Just (keyboardProc padRef)

	--タイマを作る
	setTimerProc gameStatRef playerRef padRef

	--GLUTのメインループに入る
	mainLoop


--キー入力の処理
keyboardProc :: IORef Pad -> Key -> KeyState -> t -> t1 -> IO ()
keyboardProc _ (Char 'q') _ _ _ = exitWith ExitSuccess
keyboardProc padRef key Down _ _ = modifyIORef padRef (\pad -> pad { pressed = union [key] (pressed pad) })
keyboardProc padRef key Up   _ _ = modifyIORef padRef (\pad -> pad { pressed = delete key (pressed pad) })
keyboardProc _ _ _ _ _ = return ()


-- タイマ割り込み設定
setTimerProc :: IORef GameStat -> IORef Player -> IORef Pad -> IO ()
setTimerProc gameStatRef playerRef padRef = do
	writeIORef gameStatRef Title
	setNext $ titleProc

	where
		setNext = addTimerCallback timerInterval

		-- タイトル
		titleProc = do
			modifyIORef padRef updatePad
			pad <- readIORef padRef

			postRedisplay Nothing

			if (trig pad .&. padA) /= 0
				then do
					writeIORef gameStatRef Game
					newPlayer >>= writeIORef playerRef
					setNext $ gameProc
				else
					setNext $ titleProc

		-- ゲーム中
		gameProc = do
			modifyIORef padRef updatePad
			pad <- readIORef padRef

			player' <- readIORef playerRef >>= updatePlayer pad
			writeIORef playerRef player'

			postRedisplay Nothing

			if not $ isDead player'
				then	setNext $ gameProc
				else do
					writeIORef gameStatRef GameOver
					setNext $ gameoverProc

		-- ゲームオーバー
		gameoverProc = gameoverProc2 0
		gameoverProc2 y' = do
			modifyIORef padRef updatePad

			player <- readIORef playerRef
			let player' = player { board_of = graynize (board_of player) y'  }
			writeIORef playerRef player'

			postRedisplay Nothing

			if (y' < boardHeight-2)
				then	setNext $ gameoverProc2 (y'+1)
				else	setNext $ gameoverProc3 0
		gameoverProc3 count = do
			modifyIORef padRef updatePad
			pad <- readIORef padRef

			postRedisplay Nothing

			if (trig pad .&. padA) /= 0
				then do
					writeIORef gameStatRef Game
					newPlayer >>= writeIORef playerRef
					setNext $ gameProc
				else
					if count < frameRate * 3
						then setNext $ gameoverProc3 (count + 1)
						else do
							writeIORef gameStatRef Title
							setNext $ titleProc


-- 文字列表示
putText :: Float -> Float -> String -> IO () 
putText x' y' str =
	preservingMatrix $ do
		translate (Vector3 (scrx x') (scry y') 0 ::Vector3 Float)
		scale 0.0007 0.0005 (1.0 :: Double)
		renderString Roman str


-- 表示
display :: IORef GameStat -> IORef Player -> IO ()
display gameStatRef playerRef = do
	gameStat <- readIORef gameStatRef
	player <- readIORef playerRef

	--背景を黒にする
	clear [ColorBuffer]

	--単位行列を読み込む
	loadIdentity

	--表示
	renderPlayer player

	color3i 255 255 255
	putText 200 20 $ "SCORE:" ++ show (score player)

	case gameStat of
		Title -> do
			putText 70 50 "TETRIS"
			putText 50 300 "PRESS SPACE"
		GameOver -> do
			putText 200 350 "GAME OVER"
		_ -> return ()

	putText 200 200 "MOVE: J L"
	putText 200 220 "FALL: K"
	putText 200 240 "ROT: Space, Z"

	--バッファの入れ替え
	swapBuffers