mcpi-0.0.1.1: examples/Shapes.hs
{-# LANGUAGE RecordWildCards #-}
{-
License:
This code is placed in the Public Domain.
Author:
Douglas Burke (dburke.gw@gmail.com)
Usage:
./shapes
Every second, report the block type that the user is standing on
if it is different than the last report.
-}
module Main where
import Control.Concurrent (threadDelay)
import Control.Monad (forM_)
import Control.Monad.IO.Class (liftIO)
import Data.MineCraft.Pi.Block
import Data.MineCraft.Pi.Player
import Data.MineCraft.Pi.Types
import Data.MineCraft.Pi.Other
-- import Network.MineCraft.Pi.Client (MCPI, runMCPI)
import Network.MineCraft.Pi.Client.Internal (MCPI, runMCPI')
import System.Environment (getProgName)
import System.Exit (exitFailure)
import System.IO
-- | Could also store the bounding box for each option.
data Shape =
BlockList BlockType [IPos]
| BlockCuboid BlockType IPos IPos
type Shapes = [Shape]
-- | Given a center and width, return the coordinates
-- of the first and last item.
limits :: Int -> Int -> (Int, Int)
limits c w =
let (hw, d) = w `divMod` 2
in (c - hw, c + hw + d)
-- | If the width is even, the cube will extend from
-- @center - width/2@ to @center + width/2@.
makeRectangle ::
BlockType -- ^ block type
-> IPos -- ^ center of rectangle
-> Int -- ^ width in x
-> Int -- ^ width in y
-> Int -- ^ width in z
-> Shape
makeRectangle bt Pos {..} wx wy wz =
let (x1,x2) = limits _x wx
(y1,y2) = limits _y wy
(z1,z2) = limits _z wz
p1 = Pos x1 y1 z1
p2 = Pos x2 y2 z2
in BlockCuboid bt p1 p2
-- | If the width is even, the cube will extend from
-- @center - width/2@ to @center + width/2@.
makeCube ::
BlockType -- ^ block type
-> IPos -- ^ center of cube
-> Int -- ^ width of cube
-> Shape
makeCube bt pos w = makeRectangle bt pos w w w
-- | Note that we convert all the other blocks in the
-- cube to air.
makePyramid ::
BlockType -- ^ block type
-> IPos -- ^ center of the base
-> Int -- ^ width of base (also the height)
-> Shapes
makePyramid bt Pos {..} w =
let (hw,_) = w `divMod` 2
pMid = Pos _x _y (_z + hw)
toP l = makeRectangle bt (Pos _x _y (_z+l)) (w-2*l) (w-2*l) 0
pyramid = map toP [0..hw]
in makeCube air pMid w : pyramid
renderShape :: Shapes -> MCPI ()
renderShape =
let rs (BlockList bt pos) = mapM_ (`setBlock` bt) pos
rs (BlockCuboid bt p1 p2) = setBlocks p1 p2 bt
in mapM_ rs
frob :: BlockType -> MCPI ()
frob bt = do
pos@Pos {..} <- getPlayerTile
let shape = makePyramid bt pos 12
setPlayerTile $ Pos (_x+20) (_y+20) _z
renderShape shape
setPlayerTile $ Pos _x _y (_z+40)
usage :: IO ()
usage = do
progName <- getProgName
hPutStrLn stderr $ "Usage: " ++ progName
exitFailure
-- | Move the player by the given coordinates, which
-- are relative to the current location. The return
-- value is the new coordinate of the player, if
-- that position contains air, otherwise the player
-- is not moved.
--
movePlayer ::
Int -- ^ Change in X
-> Int -- ^ Change in Y
-> Int -- ^ Change in Z
-> MCPI (Maybe (Pos Int))
movePlayer dx dy dz = do
Pos {..} <- getPlayerTile
let nPos = Pos (_x + dx) (_y + dy) (_z + dz)
btype <- getBlock nPos
if btype == air
then setPlayerTile nPos >> return (Just nPos)
else return Nothing
showBlocksAsWall :: MCPI ()
showBlocksAsWall = do
Pos {..} <- getPlayerTile
let x1 = _x - 128
y1 = _y + 20
setBlocks (Pos x1 y1 (_z-1)) (Pos (x1+255) (y1+4) (_z+1)) sandstone
forM_ [0..255] $ \ctr ->
setBlocks
(Pos (x1+ctr) y1 _z)
(Pos (x1+ctr) (y1+4) (_z+16))
$ BlockType $ fromIntegral ctr
showBlocksAsFloor :: MCPI ()
showBlocksAsFloor = do
Pos {..} <- getPlayerTile
let w = 4
hw = 8 * w
x1 = _x - hw
y1 = _y - hw
x2 = x1 + 2 * hw
y2 = y1 + 2 * hw
z = _z - 1
setBlocks (Pos x1 y1 (z-1)) (Pos x2 y2 (z-1)) sandstone
forM_ [0..15] $ \j ->
let ys = y1 + j * w
ye = ys + w - 1
ctrBase = j * 16
in forM_ [0..15] $ \i ->
let xs = x1 + i * w
xe = xs + w - 1
ctr = fromIntegral $ i + ctrBase
in setBlocks
(Pos xs ys z)
(Pos xe ye z)
$ BlockType ctr
reportBlock :: MCPI ()
reportBlock =
let go oPos = do
liftIO $ threadDelay 1000000
nPos <- getPlayerTile
if nPos == oPos
then go oPos
else do
let bPos = Pos { _x = _x nPos
, _y = _y bPos
, _z = _z nPos - 1
}
(bType, bData) <- getBlockData bPos
chatPost $ "Block: " ++ showBlock bType
chatPost $ " : " ++ show bData
go nPos
in getPlayerTile >>= go
main :: IO ()
main = runMCPI' True reportBlock