packages feed

bloxorz-0.1: Cubezor.hs

module Cubezor where

import Graphics.Rendering.OpenGL
import Etc
import Maps
import Quaternion


-- Cube types
data CubePos = CubePos {
  x :: Int,
  y :: Int
} deriving Show

data CubeOrientation = Standing | Horizontal | Vertical deriving Eq

data CubeRolling = CrNo | CrUp | CrDown | CrRight | CrLeft | CrFalling | CrWinning deriving (Eq, Show)

data Cube = Cube { pos1      :: CubePos
                 , pos2      :: CubePos
                 , angle     :: GLfloat
                 , rolling   :: CubeRolling
                 , quat      :: Quaternion
                 , rotOffset :: Vector3 GLfloat
                 , rotAxis   :: Vector3 GLfloat
                 , fallOffset :: GLfloat
                 } deriving Show


cubeRollingSpeed :: GLfloat
cubeRollingSpeed = 360.0


newCube :: Maps.Level -> Cube
newCube level = Cube {
    pos1  = p,
    pos2  = p,
    angle = 0.0,
    rolling = CrNo,
    quat = quatIdentity,
    rotOffset = (Vector3 0.0 0.0 0.0 :: Vector3 GLfloat),
    rotAxis = (Vector3 0.0 0.0 0.0 :: Vector3 GLfloat),
    fallOffset = 0.0
  } where
    (x,y) = lStartPos level
    p = CubePos {x = x, y = y}


renderCube :: Cube -> IO ()
renderCube cubex = preservingMatrix $ do
  let xx = fromIntegral ((x $ pos1 cubex) + (x $ pos2 cubex))
  let yy = fromIntegral ((y $ pos1 cubex) + (y $ pos2 cubex))
  let zz = if orientation cubex == Standing then 2.0 else 1.0

  if (rolling cubex == CrFalling) || (rolling cubex == CrWinning)
    then translate (Vector3 0 (fallOffset cubex) 0 :: Vector3 GLfloat)
    else return ()

  translate $ (Vector3 xx 0 yy :: Vector3 GLfloat)
  if rolling cubex /= CrNo
    then do
      translate $ vecNeg $ rotOffset cubex
      rotate (angle cubex) (rotAxis cubex)
      translate $ rotOffset cubex
    else
      return ()

  translate $ (Vector3 0.0 zz 0.0 :: Vector3 GLfloat)
  m <- quat2Matrix $ quat cubex
  multMatrix m
  scale (1.0 :: GLfloat) 2.0 1.0
  cube


upCube :: Cube -> Cube
upCube cube =
  if rolling cube == CrNo
    then 
      cube { rolling = CrUp
           , rotAxis = Vector3 (-1.0) 0.0 0.0
           , rotOffset = Vector3 0.0 0.0 ro
           }
    else
      cube
    where
      ro = if orientation cube == Vertical
             then 2.0
             else 1.0


downCube :: Cube -> Cube
downCube cube =
  if rolling cube == CrNo
    then
      cube { rolling = CrDown
           , rotAxis = Vector3 1.0 0.0 0.0
           , rotOffset = Vector3 0.0 0.0 ro
           }
    else
      cube
  where
    ro = if orientation cube == Vertical
           then (-2.0)
           else (-1.0)


leftCube :: Cube -> Cube
leftCube cube =
  if rolling cube == CrNo
    then
      cube { rolling = CrLeft
           , rotAxis = Vector3 0.0 0.0 1.0
           , rotOffset = Vector3 ro 0.0 0.0
           }
    else
      cube
  where
    ro = if orientation cube == Horizontal
           then 2.0
           else 1.0


rightCube :: Cube -> Cube
rightCube cube =
  if rolling cube == CrNo
    then
      cube { rolling = CrRight
           , rotAxis = Vector3 0.0 0.0 (-1.0)
           , rotOffset = Vector3 ro 0.0 0.0
           }
    else
      cube
  where
    ro = if orientation cube == Horizontal
           then (-2.0)
           else (-1.0)


fallCube :: Cube -> Cube
fallCube cube = cube { rolling = CrFalling
                     , fallOffset = 0.0
                     }


winCube :: Cube -> Cube
winCube cube = cube { rolling = CrWinning
                    , fallOffset = 0.0
                    }


orientation :: Cube -> CubeOrientation
orientation cube =
  if x1 == x2 && y1 == y2
    then Standing
    else
      if x1 < x2 && y1 == y2
        then Horizontal
        else Vertical
  where
    x1 = x $ pos1 cube
    y1 = y $ pos1 cube
    x2 = x $ pos2 cube
    y2 = y $ pos2 cube


updateCube :: GLfloat -> Cube -> Cube
updateCube dt cube =
  if (angle cube > 90.0) && (rolling cube /= CrFalling) && (rolling cube /= CrWinning)
    then
      cube { pos1 = p1
           , pos2 = p2
           , quat = q
           , angle = 0
           , rolling = CrNo
           }
    else
      if rolling cube /= CrNo
        then cube { angle = a
                  , fallOffset = fo 
                  }
        else cube
  where
    (p1,p2,q) = updatePosition cube
    fo = if (rolling cube == CrFalling) || (rolling cube == CrWinning)
           then fallOffset cube - 10.0 * dt
           else 0.0
    a = if rolling cube == CrWinning
          then 0.0
          else angle cube + cubeRollingSpeed * dt


updatePosition :: Cube -> (CubePos, CubePos, Quaternion)
updatePosition cube =
  case rolling cube of
    CrUp -> case orientation cube of
            Standing   -> ( CubePos  xx      (yy - 2)
                          , CubePos  xx      (yy - 1)
                          , quatMul q qq
                          )
            Horizontal -> ( CubePos  xx      (yy - 1)
                          , CubePos (xx + 1) (yy - 1)
                          , quatMul q qq
                          )
            Vertical   -> ( CubePos  xx      (yy - 1)
                          , CubePos  xx      (yy - 1)
                          , quatMul q qq
                          )
            where q = quatFromAngleAxis piPer2 (-1.0) 0.0 0.0
    CrDown -> case orientation cube of
            Standing   -> ( CubePos  xx      (yy + 1)
                          , CubePos  xx      (yy + 2)
                          , quatMul q qq
                          )
            Horizontal -> ( CubePos  xx      (yy + 1)
                          , CubePos (xx + 1) (yy + 1)
                          , quatMul q qq
                          )
            Vertical   -> ( CubePos  xx      (yy + 2)
                          , CubePos  xx      (yy + 2)
                          , quatMul q qq
                          )
            where q = quatFromAngleAxis piPer2 1.0 0.0 0.0
    CrLeft -> case orientation cube of
            Standing   -> ( CubePos (xx - 2)  yy
                          , CubePos (xx - 1)  yy
                          , quatMul q qq
                          )
            Horizontal -> ( CubePos (xx - 1)  yy
                          , CubePos (xx - 1)  yy
                          , quatMul q qq
                          )
            Vertical   -> ( CubePos (xx - 1)  yy
                          , CubePos (xx - 1) (yy + 1)
                          , quatMul q qq
                          )
            where q = quatFromAngleAxis piPer2 0.0 0.0 1.0
    CrRight -> case orientation cube of
            Standing   -> ( CubePos (xx + 1)  yy
                          , CubePos (xx + 2)  yy
                          , quatMul q qq
                          )
            Horizontal -> ( CubePos (xx + 2)  yy
                          , CubePos (xx + 2)  yy
                          , quatMul q qq
                          )
            Vertical   -> ( CubePos (xx + 1)  yy
                          , CubePos (xx + 1) (yy + 1)
                          , quatMul q qq
                          )
            where q = quatFromAngleAxis piPer2 0.0 0.0 (-1.0)
    -- otherwise just return the old value
    _ -> (pos1 cube, pos2 cube, quat cube)
  where
    xx = x $ pos1 cube
    yy = y $ pos1 cube
    qq = quat cube


isCubeUpdated :: Cube -> Cube -> Bool
isCubeUpdated cube0 cube1 = (angle cube0 > 90.0) && (angle cube1 == 0.0)