packages feed

HGamer3D-Wire-0.3.0: HGamer3D/Wire/ECSWire.hs

{-# LANGUAGE Arrows #-}

-- Some useful wires for game programming
--
-- (c) 2014 Peter Althainz
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

module HGamer3D.Wire.ECSWire where

import Control.Wire as W
import Control.Wire.Unsafe.Event as U

import Prelude hiding ((.), id)

import HGamer3D
import HGamer3D.Audio
import HGamer3D.InputSystem

import HGamer3D.Wire.Types
import HGamer3D.Wire.EntityComponentSystem

import Control.Monad.Trans.Maybe

-- ECS Wires

-- move position of Entity by a vector (per second)
move :: Entity -> Vec3 -> GameWire (Event a) (Event a)
move e vec = mkGen $ \s evt -> case evt of
    Event _ -> do
      let t = realToFrac (dtime s) 
      runMaybeT $ setLocation e (\pos -> pos &+ (vec &* t))
      return (Right evt, move e vec)
    _ -> return (Right evt, move e vec)
 
rotate :: Entity -> Vec3 -> Float -> GameWire (Event a) (Event a)
rotate e rv a = mkGen $ \s evt -> case evt of
  Event _ -> do
    let t = realToFrac (dtime s) 
    runMaybeT $ setOrientation e (\ori -> ori .*. (rotU rv (a*t))) 
    return (Right evt, rotate e rv a)
  _ -> return (Right evt, rotate e rv a)

accelerate :: Entity -> Vec3 -> GameWire (Event a) (Event a)
accelerate e vec = mkGen $ \s evt -> case evt of
  Event _ -> do
    runMaybeT $ setVelocity e (\vel -> vel &+ vec) 
    return $ (Right evt, accelerate e vec)  
  _ -> return $ (Right evt, accelerate e vec)


-- move position to target within specific time 
moveTo :: Entity -> Vec3 -> Float -> GameWire a ()
moveTo e target dt = let  
  startAction = do 
    runMaybeT (do
                  loc <- getLocation e
                  let vel = (target &- loc) &* (1.0 / dt)
                  setVelocity e (\_ -> vel)
                  return () ) 
    return $ Left ()
  endAction = do 
    runMaybeT (do
                  setVelocity e (\_ -> (Vec3 0.0 0.0 0.0))
                  setLocation e (\_ -> target)
                  return () ) 
    return $ Left ()
  in  (mkGen_ (\_ -> startAction)) --> for (realToFrac dt) . pure () --> (mkGen_ (\_ -> endAction)) 


-- USEFUL WIRES

-- sends the event one time and then inhibit !

sendOnce :: GameWire (W.Event a) (W.Event a)
sendOnce = proc inevt -> do
  rec
    devt <- delay (U.NoEvent) -< devt'
    devt' <- id -< inevt 
  case devt of
      U.NoEvent -> do
        returnA -< inevt
      U.Event evt -> do
        x <- pure U.NoEvent . mkEmpty -< ()
        returnA -< x
    
-- wire to play a sound
soundW :: AudioSource -> Wire s e IO (W.Event a) (W.Event a)
soundW aSource = onEventM (\aIn -> do
   playAudioSource aSource
   return aIn )
                 
-- samples generated keypress
keyW :: EnumKey -> Wire s e IO a (W.Event a)
keyW key = mkGen_ ( \inVal -> do
                       press <- isKeyPressed key
                       let evt = if press then U.Event inVal else U.NoEvent
                       return $ (Right evt) )

-- Cycles through a list, stop at end
cycleW :: [b] -> Wire s () IO (W.Event a) (W.Event b)
cycleW lin = if length lin > 0 then
               mkPureN (\evt -> case evt of
                        U.Event _ -> (Right (U.Event (head lin)), cycleW (tail lin))
                        _ -> (Right U.NoEvent, cycleW lin) )
               else
                 mkConst $ Left ()
               

printEvt :: Show a => GameWire (W.Event a) (W.Event a) 
printEvt = mkGen_ (\evt -> do case evt of 
                                U.Event x -> do
                                             print x
                                             return (Right evt)
                                _ -> return (Right evt))


-- generic switch, in event is two-fold, left side creates new wire of a -> b
-- right side is a output is b, runs sequentially, first do new wire switched in, then restart switch logic

-- different types of switching logic, eases up game programming
----------------------------------------------------------------

-- this wire creates a wire from a function which creates a wire from an input status
-- the resulting wire switches into the created wire, runs this until it inihibits and then
-- switches back to the switchIntoAndRun semantics

-- useful for providing game states, which run for a certain amount of time and then wait for a new
-- initializer, during running the created wire additional state input is ignored

switchIntoAndRunW :: (gst -> GameWire (Event a) (Event b)) -> GameWire (Event gst, Event a) (Event b)
switchIntoAndRunW inWireF = switch $ mkPure_ (\(gsEvt, aEvt) -> case gsEvt of
              Event gs -> let
                newWire = (inWireF gs) . mkPure_ (\(a, b) -> Right b) --> switchIntoAndRunW inWireF
                in Right (NoEvent, Event (newWire))
              _ -> Right (NoEvent, NoEvent) )

-- same logic, but does not need to process events during run phase
-- usefule if something needs to happen, but no input processing
switchIntoAndRunW' :: (gst -> GameWire () (Event b)) -> GameWire (Event gst) (Event b)
switchIntoAndRunW' inWireF = switch $ mkPure_ (\gsEvt -> case gsEvt of
              Event gs -> let
                newWire = (inWireF gs) . pure () --> switchIntoAndRunW' inWireF
                in Right (NoEvent, Event (newWire))
              _ -> Right (NoEvent, NoEvent) )