definition module StdGameDef

//	********************************************************************************
//	Clean Standard Game library, version 1.2.2
//	
//	StdGameDef contains all the type definitions needed to specify a game.
//	Author:   Mike Wiering
//	Modified: 7 Sept 2001 for Clean 2.0 (Peter Achten)
//	********************************************************************************

import	StdString
from	StdFunc			import :: St
from	StdOverloaded	import class zero
from	StdIOBasic		import :: IdFun, :: Size(..), :: Point2(..)
from	StdMaybe		import :: Maybe(..)
from	StdPictureDef	import :: Colour(..), ::RGBColour(..)
import	StdGSt


:: GameAttribute gs
   = ScreenSize Size     // screen resolution, default 320x240
   | ColorDepth Int      // screen color depth, default 8 (256 colors)

:: Game gs
   = { levels        :: [Level (GSt gs)]        // levels
     , quitlevel     :: St (GSt gs) Bool        // True quits the game
     , nextlevel     :: St (GSt gs) Int         // new level if >0 (0 quits)
     , textitems     :: St (GSt gs) [GameText]  // all game text items
     }

:: Level state
   = { boundmap      :: !BoundMap               // static bounds map in a level
     , initpos       :: !Point2                 // center of screen in boundmap
     , layers        :: ![Layer]                // all layers [back..front]
     , objects       :: ![GameObjectLS state]   // all other objects in the level
     , music         :: !Maybe Music            // background music
     , soundsamples  :: ![SoundSample]          // list of sound samples
     , leveloptions  :: !LevelOptions           // level options
     }

:: LevelOptions
   = { fillbackground:: !Maybe Colour    // fill the screen before drawing layers
     , escquit       :: !Bool            // quit the level when pressing Esc key
     , debugscroll   :: !Bool            // enable scrolling with arrow keys
     , fadein        :: !Bool            // fade in from black
     , fadeout       :: !Bool            // fade out to black
     }

:: Music
   = { musicfile     :: !String          // MIDI file
     , restart       :: !Bool            // restart the music when it ends?
     , continue      :: !Bool            // music continues after end of level
     }

:: SoundSample
   = { soundid       :: !SoundID         // id for the sample (any number)
     , soundfile     :: !String          // WAV file
     , soundbuffers  :: !Int             // max times sample can be played together
     }

:: SoundID
   :== Int

:: BoundMap
   = { map        :: ![{#Int}] // map of all static bounds
     , blocksize  :: !Size     // size of the map units (in pixels)
     , objstart   :: !Int      // min. value for objects (lower values are ignored)
     , startobjx  :: !Int      // X-distance from screen when to initialise objects
     , startobjy  :: !Int      // Y-distance from screen when to initialise objects
     }

:: GameRegion
   :== [Int]

:: Bounds
   :== Int

:: DirectionSet
   = { top        :: !Bool
     , left       :: !Bool
     , bottom     :: !Bool
     , right      :: !Bool
     }

:: Layer
   = { bmp        :: !GameBitmap     // bitmap that contains all the tiles
     , layermap   :: !LayerMap       // map of the tiles in the level
     , sequences  :: ![TileSequence] // tiles that change repeatedly
     , movement   :: !Movement       // function to scroll the layer
     }

:: GameTime
   :== Int                          // time in frames

:: GameBitmap
   = { bitmapname :: !String        // bitmap that contains smaller blocks
     , unitsize   :: !Size          // size of these blocks (width, height)
     , dimensions :: !(!Int,!Int)   // number of blocks (horizontal, vertical)
     , transparent:: !Maybe Point2  // position of a transparent pixel
     }

:: LayerMap
   :== [{#Int}]  // map of block numbers in a bitmap
                 // 0: empty; 1..n: block number; n+1..2n: mirror block;
                 // 2n+1..3n: upside down; 3n+1..4n: mirror and upsidedown;
                 // -1..-m: block sequence number
                 // (n = # blocks in gamebitmap; m = # sequences)

:: TileSequence
   :== (!Int, Sequence)   // block sequence number, Sequence

:: Sequence
   :== [(Int, Int)]   // tile number, duration

:: Movement
   :== Point2 GameTime -> Point2    // calculate layer's position from game position

:: SpriteID
   :== Int

:: Sprite
   = { bitmap   :: !GameBitmap      // sprites may have their own bitmap
     , sequence :: !Sequence        // seqence of blocks
     , loop     :: !Bool            // if FALSE, callback animation function
     }

:: GameObjectLS gs
   = E. state: GameObjectLS (GameObject state gs)
:: GameObject state gs
// = E. state:
   = { objectcode :: !ObjectCode    // code for object type (0 AutoInitObject)
     , sprites    :: ![Sprite]      // sprite 1..n
     , init       :: !SubCode Point2 GameTime gs -> GameObjectState state gs
     , done       :: !(GameObjectState state gs)    -> gs
     , move       :: !                                         ObjectFun state gs
     , animation  :: !                                         ObjectFun state gs
     , touchbound :: !DirectionSet MapCode                  -> ObjectFun state gs
     , collide    :: !DirectionSet ObjectCode GameObjectRec -> ObjectFun state gs
     , frametimer :: !                                         ObjectFun state gs
     , keydown    :: !KeyCode                               -> ObjectFun state gs
     , keyup      :: !KeyCode                               -> ObjectFun state gs
     , userevent  :: !EventCode EventPar EventPar           -> ObjectFun state gs
     }

:: *GameObjectState state gs
   = { st :: state             // object state
     , or :: GameObjectRec     // object record
     , gs :: gs                // game state
     }

:: ObjectFun state gs
   :== IdFun (GameObjectState state gs)

:: ObjectCode
   :== Int

:: SubCode
   :== Int

:: MapCode
   :== Int

:: KeyCode
   :== Int

:: EventCode
   :== Int

:: EventPar
   :== Int

:: FVXY
   = { fvx  :: !FV
     , fvy  :: !FV
     }

:: FV
   = Factor !Real
   | Value  !Real

:: RealXY
   = { rx :: !Real
     , ry :: !Real
     }

:: GameObjectRec
   = { active              :: !Bool            // move and check collisions?
     , subcode             :: !SubCode         // object's sub-code
     , size                :: !Size            // the actual size
     , pos                 :: !Point2          // current position
     , offset              :: !Point2          // relative offset for sprite
     , currentsprite       :: !Int             // current animation sequence
     , displayoptions      :: !DisplayOptions  // invisible/mirror etc.
     , ownbounds           :: !Bounds          // bound(s) of the object (bits)
     , bouncebounds        :: !Bounds          // just bounce against these bounds
     , collidebounds       :: !Bounds          // call collide func for these bounds
     , forgetdistance      :: !Point2          // make object inactive at distance
     , framecounter        :: !GameTime        // frame counter
     , layer               :: !LayerPosition   // layer the object moves in front of
     , acceleration        :: !RealXY          // x/y acceleration
     , speed               :: !RealXY          // object's x/y speed
     , bounce              :: !FVXY            // x/y bounce at static bounds * 256
     , maxspeed            :: !RealXY          // x/y maximum speed
     , slowdown            :: !FVXY            // x/y slow down
     , skipmove            :: !Int             // acceleration delay
     , options             :: !ObjectOptions   // object options
     }

:: DisplayOptions
   = { blink               :: !Bool            // object blinks
     , stretch             :: !Bool            // stretch sprite to fit in size
     , mirrorleftright     :: !Bool            // mirror the sprite
     , mirrorupdown        :: !Bool            // draw sprite up side down
     , rotation            :: !Rotation        // rotation
     }

:: Rotation
   = NoRotation | Rotate90 | Rotate180 | Rotate270

:: ObjectOptions
   = { ignorelevelbounds   :: !Bool         // object can move out of the level
     , checkkeyboard       :: !Bool         // allow key down event for this object
     , allowkeyboardrepeat :: !Bool         // allow pressed key to repeat
     , static              :: !Bool         // object always moves with screen
     , hdirection          :: !HDirection   // horizontal direction of the object
     , vdirection          :: !VDirection   // vertical direction of the object
     , automirrorleftright :: !Bool         // mirror if horizontal direction change
     , automirrorupdown    :: !Bool         // mirror if vertical direction change
     , freeze              :: !Bool         // no movement until framecounter is 0
     , removemapcode       :: !Bool         // remove the object from the map
     }

:: HDirection
   = DirLeft | DirRight

:: VDirection
   = DirUp | DirDown

:: LayerPosition
   = InFront | AtLayer Int

:: GameText
   = { format    :: !String        // text to display or format string for value
     , value     :: Maybe Int      // value to display
     , position  :: !Point2        // position on screen
     , style     :: !Style         // style to write the text in
     , color     :: Colour         // color for the text
     , shadow    :: Maybe Shadow   // shadow
     , alignment :: TextAlignment  // text alignment
     }

:: TextAlignment
   = { xyfromscreencenter :: !(!Bool,!Bool) // center position on the screen
     , xycentered         :: !(!Bool,!Bool) // center text around position
     }

:: Style
   = { fontname :: !String                  // any font name
     , fontsize :: !Int                     // size of the text
     , bold     :: !Bool                    // bold
     , italic   :: !Bool                    // italic
     }

:: Shadow
   = { shadowpos   :: !Point2               // relative position of text shadow
     , shadowcolor :: !Colour               // color of the shadow
     }

instance zero RealXY
instance zero TextAlignment
