Functional Banana Traveller - Timer and player independent Events
Asked Answered
S

1

7

I'm at the point in my Traveller I'm trying to handle player-independent updates to the game state. For reference, the project is here (the devel branch is the relevant one for this question).

Libraries/Universe/GameState.hs has a function, updateGS that handles all player updates to the game state. The EventNetwork looks like this right now.

makeNetworkDescription :: AddHandler PlayerCommand ->
                          AddHandler () ->
                          TChan GameState ->
                          IO EventNetwork
makeNetworkDescription addCommandEvent tickHandler gsChannel = compile $ do
    eInput <- fromAddHandler addCommandEvent
    eTick <- fromAddHandler tickHandler
    let bGameState = accumB initialGS $ updateGS <$> eInput
    eGameState <- changes bGameState
    reactimate $ (\n -> (atomically $ writeTChan gsChannel n)) <$> eGameState

The problem in implementing this timer is that all the examples I have looked at have a use case different from mine, physics simulation. There's no physics involved in this game. I'm trying to use a timer to have the game state evaluated independent of player actions. For now, the only thing I want to manage is hyperspace travel. When completely implemented, moving from one planet to another will change an Agent's location to Right Hyperspace. What needs to happen now, is that when a tick happens, distanceTraversed increments by one. Then if distanceTraversed equals totalDistance Agent's location becomes Left Planet.

So what would that look like from the EventNetwork's point of view?

let bHyperspace = accumB initialGS $ foo <$> eTick

now to combine behaviors

let bBaz = (++) <$> bGameState <*> bHyperspace

Is this the right track?

Statfarad answered 10/5, 2013 at 14:14 Comment(0)
C
1

The question is somewhat vague and not easy to answer, but I'll try my best.

First, looking at your code, I find it odd that you have "outsourced" the actual gameplay logic to the monolithic GameState type and the updateGS function. Now, this is not a bad thing to do, it's just that there is no benefit from using FRP in this style. You can remove the makeNetworkDescription function altogether and register an even thandler with addCommandEvent by hand instead.

The benefit of FRP is that you can model the game state as a network of behaviors and events. If the state is modular enough, then this will simplify the code significantly.


Second, concerning your question about modelling hyperspace travel.

Here is one way to do it:

-- indicates whether hyperspace travel is currently happening
bTravelling :: Behavior t Bool

-- increment travel distance, but only when travelling
bTravelDistance :: Behavior t Distance
bTravelDistance = accumB 0 $ (+1) <$> whenE bTravelling eTick

-- calculate player location from travel distance
bPlayerLocation :: Behavior t Location
bPlayerLocation =
    (\distance -> if distance > total then Left Planet else Right HyperSpace)
    <$> bTravelDistance

You probably want to repeat this process several times in a game, though. Unfortunately, reactive-banana currently doesn't offer a "first do this, then do that" kind of abstraction. There is dynamic event switching, but it may be a bit unwieldy.

Cease answered 18/5, 2013 at 8:33 Comment(1)
I had realized that updateGS was going to be a problem, but I was saving that question for another day.Statfarad

© 2022 - 2024 — McMap. All rights reserved.