├── .gitignore
├── src
├── site
│ ├── style.css
│ ├── images
│ │ ├── frog.png
│ │ ├── leaf.png
│ │ ├── next.png
│ │ ├── level.png
│ │ ├── loading.gif
│ │ ├── restart.png
│ │ ├── water.jpg
│ │ ├── completed.svg
│ │ ├── message
│ │ │ ├── 90.svg
│ │ │ ├── 180.svg
│ │ │ ├── 0.svg
│ │ │ └── 270.svg
│ │ ├── key.svg
│ │ └── arrows
│ │ │ ├── 1
│ │ │ ├── 180.svg
│ │ │ └── 0.svg
│ │ │ └── 2
│ │ │ ├── 180.svg
│ │ │ ├── 0.svg
│ │ │ ├── 90.svg
│ │ │ └── 270.svg
│ ├── index.html
│ └── script.js
└── elm
│ └── Froggy
│ ├── TransitionUtil.elm
│ ├── Grid.elm
│ ├── Util.elm
│ ├── Main.elm
│ ├── Commands.elm
│ ├── Model.elm
│ ├── State.elm
│ └── View.elm
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | build/cache
2 | build/elm_dependencies
3 | build/node_modules
4 | target
--------------------------------------------------------------------------------
/src/site/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-image: url("images/water.jpg");
3 | }
--------------------------------------------------------------------------------
/src/site/images/frog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thSoft/froggy/HEAD/src/site/images/frog.png
--------------------------------------------------------------------------------
/src/site/images/leaf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thSoft/froggy/HEAD/src/site/images/leaf.png
--------------------------------------------------------------------------------
/src/site/images/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thSoft/froggy/HEAD/src/site/images/next.png
--------------------------------------------------------------------------------
/src/site/images/level.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thSoft/froggy/HEAD/src/site/images/level.png
--------------------------------------------------------------------------------
/src/site/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thSoft/froggy/HEAD/src/site/images/loading.gif
--------------------------------------------------------------------------------
/src/site/images/restart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thSoft/froggy/HEAD/src/site/images/restart.png
--------------------------------------------------------------------------------
/src/site/images/water.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thSoft/froggy/HEAD/src/site/images/water.jpg
--------------------------------------------------------------------------------
/src/elm/Froggy/TransitionUtil.elm:
--------------------------------------------------------------------------------
1 | module Froggy.TransitionUtil where
2 |
3 | type TransitionInfo a = {
4 | oldValue: a,
5 | startTime: Time
6 | }
7 |
8 | time : Signal Time
9 | time = fps 30 |> timestamp |> lift fst
--------------------------------------------------------------------------------
/src/elm/Froggy/Grid.elm:
--------------------------------------------------------------------------------
1 | module Froggy.Grid where
2 |
3 | type Position = {
4 | x: Int,
5 | y: Int
6 | }
7 |
8 | equals : Position -> Position -> Bool
9 | equals a b = (a.x == b.x) && (a.y == b.y)
10 |
11 | translate : Position -> Position -> Position
12 | translate a b =
13 | {
14 | x = a.x + b.x,
15 | y = a.y + b.y
16 | }
--------------------------------------------------------------------------------
/src/elm/Froggy/Util.elm:
--------------------------------------------------------------------------------
1 | module Froggy.Util where
2 |
3 | import Maybe (..)
4 |
5 | remove : [a] -> a -> [a]
6 | remove xs x = xs |> filter (\element -> element /= x)
7 |
8 | getOrElse : a -> Maybe a -> a
9 | getOrElse defaultValue maybeValue = maybeValue |> maybe defaultValue identity
10 |
11 | distance : Int -> Int -> Int
12 | distance a b = abs(a - b)
--------------------------------------------------------------------------------
/src/elm/Froggy/Main.elm:
--------------------------------------------------------------------------------
1 | module Froggy.Main where
2 |
3 | import Window
4 | import Froggy.Model (..)
5 | import Froggy.State (..)
6 | import Froggy.View (..)
7 | import Froggy.TransitionUtil (..)
8 |
9 | main = lift3 (view fontName) Window.dimensions time mainState
10 |
11 | mainState = game loadedGame
12 |
13 | port loadedGame : Signal (Maybe Game)
14 |
15 | port savedGame : Signal Game
16 | port savedGame = mainState
17 |
18 | port fontName : String
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Play
2 |
3 | [Have fun!](http://thsoft.github.io/froggy)
4 |
5 | ## Design
6 |
7 | If you are a graphic designer and this game inspires you, I'd be very grateful if you created the graphics for it. Please [contact me](http://thsoft.hu/en/Contact)!
8 |
9 | ## Develop
10 |
11 | ### Install
12 | * [node.js](http://nodejs.org/download/)
13 | * [Elm 0.13](http://elm-lang.org/Install.elm)
14 |
15 | ### Check out
16 | git clone https://github.com/thSoft/froggy.git
17 | cd froggy
18 | build/init.sh
19 |
20 | ### Build
21 | build/build.js
22 | * Open `target/index.html`
23 |
24 | ### Release
25 | build/publish.js
--------------------------------------------------------------------------------
/src/site/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Froggy
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
--------------------------------------------------------------------------------
/src/elm/Froggy/Commands.elm:
--------------------------------------------------------------------------------
1 | module Froggy.Commands where
2 |
3 | import Keyboard
4 | import Mouse
5 | import Graphics.Input (..)
6 | import Froggy.Grid as Grid
7 | import Froggy.Model (..)
8 |
9 | data Command =
10 | Nop |
11 | MoveBy Grid.Position |
12 | MoveTo Leaf |
13 | Continue |
14 | RestartLevel |
15 | Start (Maybe Game)
16 |
17 | commands : Signal (Maybe Game) -> Signal (Time, Command)
18 | commands loadedGame =
19 | let moveBy = lift2 makeMoveBy Keyboard.shift Keyboard.arrows
20 | continue = lift (makeCommand Continue) (merge Keyboard.enter Mouse.isDown)
21 | restartLevel = lift (makeCommand RestartLevel) (Keyboard.isDown 27)
22 | start = makeStart loadedGame
23 | in merges [moveBy, moveTo.signal, continue, restartLevel, start] |> timestamp
24 |
25 | makeMoveBy : Bool -> Grid.Position -> Command
26 | makeMoveBy shift arrows =
27 | let multiplier = if shift then 2 else 1
28 | in MoveBy {
29 | x = arrows.x * multiplier,
30 | y = -arrows.y * multiplier
31 | }
32 |
33 | moveTo : Input Command
34 | moveTo = input Nop
35 |
36 | makeCommand : Command -> Bool -> Command
37 | makeCommand command pressed = if pressed then command else Nop
38 |
39 | makeStart : Signal (Maybe Game) -> Signal Command
40 | makeStart loadedGame = foldp updateStart Nop loadedGame |> dropRepeats
41 |
42 | updateStart : Maybe Game -> Command -> Command
43 | updateStart loadedGame startGame =
44 | case startGame of
45 | Nop -> Start loadedGame
46 | _ -> startGame
--------------------------------------------------------------------------------
/src/elm/Froggy/Model.elm:
--------------------------------------------------------------------------------
1 | module Froggy.Model where
2 |
3 | import Maybe (..)
4 | import Froggy.Util (..)
5 | import Froggy.TransitionUtil (..)
6 | import Froggy.Grid as Grid
7 | import Froggy.Levels (..)
8 |
9 | type Game = {
10 | scene: Scene,
11 | usingKeyboard: Bool,
12 | lastSceneChange: Maybe (TransitionInfo (Maybe Scene))
13 | }
14 |
15 | type Scene = {
16 | frog: Frog,
17 | leaves: [Leaf],
18 | levelNumber: Int
19 | }
20 |
21 | type Frog = {
22 | leaf: Leaf,
23 | lastMove: Maybe (TransitionInfo Leaf)
24 | }
25 |
26 | type Leaf = {
27 | position: Grid.Position
28 | }
29 |
30 | levelCompleted : Scene -> Bool
31 | levelCompleted scene = (scene.leaves |> length) == 1
32 |
33 | stuck : Scene -> Bool
34 | stuck scene = not (scene.leaves |> any (reachableBy scene.frog))
35 |
36 | reachableBy : Frog -> Leaf -> Bool
37 | reachableBy frog leaf = (frog.leaf `angleBetween` leaf) |> isJust
38 |
39 | angleBetween : Leaf -> Leaf -> Maybe Int
40 | angleBetween sourceLeaf targetLeaf =
41 | let sourceX = sourceLeaf.position.x
42 | sourceY = sourceLeaf.position.y
43 | targetX = targetLeaf.position.x
44 | targetY = targetLeaf.position.y
45 | in if | (sourceX == targetX) && (sourceY > targetY) && (sourceY `near` targetY) -> Just 90
46 | | (sourceY == targetY) && (sourceX < targetX) && (sourceX `near` targetX) -> Just 0
47 | | (sourceX == targetX) && (sourceY < targetY) && (sourceY `near` targetY) -> Just 270
48 | | (sourceY == targetY) && (sourceX > targetX) && (sourceX `near` targetX) -> Just 180
49 | | otherwise -> Nothing
50 |
51 | near : Int -> Int -> Bool
52 | near a b = (distance a b) <= 2
53 |
54 | onlyDoubleJump : Scene -> Bool
55 | onlyDoubleJump scene =
56 | let distanceX leaf = distance scene.frog.leaf.position.x leaf.position.x
57 | distanceY leaf = distance scene.frog.leaf.position.y leaf.position.y
58 | leafOnlyDoubleJump leaf = ((distanceX leaf) == 2) || ((distanceY leaf) == 2)
59 | in scene.leaves |> filter (reachableBy scene.frog) |> all leafOnlyDoubleJump
60 |
61 | angleOf : Frog -> Int
62 | angleOf frog =
63 | case frog.lastMove of
64 | Just { oldValue } -> oldValue `angleBetween` frog.leaf |> getOrElse 0
65 | Nothing -> 90
--------------------------------------------------------------------------------
/src/site/script.js:
--------------------------------------------------------------------------------
1 | function translateTouchEventsToMouseEvents() {
2 | function touchHandler(event) {
3 | var touches = event.changedTouches;
4 | var first = touches[0];
5 | var type;
6 | switch (event.type) {
7 | case "touchstart":
8 | type = "mousedown";
9 | break;
10 | case "touchmove":
11 | type = "mousemove";
12 | break;
13 | case "touchend":
14 | type = "mouseup";
15 | break;
16 | default:
17 | return;
18 | }
19 | var simulatedEvent = document.createEvent("MouseEvent");
20 | simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0, null);
21 | first.target.dispatchEvent(simulatedEvent);
22 | }
23 |
24 | document.addEventListener("touchstart", touchHandler, true);
25 | document.addEventListener("touchmove", touchHandler, true);
26 | document.addEventListener("touchend", touchHandler, true);
27 | document.addEventListener("touchcancel", touchHandler, true);
28 | }
29 |
30 | function initializeElm() {
31 | var module = Elm.Froggy.Main;
32 | return Elm.fullscreen(module, {
33 | loadedGame: null,
34 | fontName: fontName
35 | });
36 | }
37 |
38 | var fontName = "Boogaloo";
39 |
40 | function loadFonts() {
41 | WebFont.load({
42 | google: {
43 | families: [fontName]
44 | }
45 | });
46 | }
47 |
48 | function loadImages(callback) {
49 | var loadedImagesCount = 0;
50 | images.forEach(function(imagePath) {
51 | var image = new Image();
52 | image.src = imagePath;
53 | image.onload = function() {
54 | loadedImagesCount++;
55 | if (loadedImagesCount >= images.length) {
56 | callback();
57 | }
58 | }
59 | });
60 | }
61 |
62 | function loadGame(component) {
63 | var storageKey = 'froggy';
64 | var loadedJson = localStorage.getItem(storageKey);
65 | var loadedState = loadedJson ? JSON.parse(loadedJson) : null;
66 | try {
67 | component.ports.loadedGame.send(loadedState);
68 | } catch (e) {
69 | console.log("The game state can't be loaded because the internal model changed. Trying to start a new game. " + e);
70 | component.ports.loadedGame.send(null);
71 | }
72 | component.ports.savedGame.subscribe(function(state) {
73 | localStorage.setItem(storageKey, JSON.stringify(state));
74 | });
75 | }
--------------------------------------------------------------------------------
/src/elm/Froggy/State.elm:
--------------------------------------------------------------------------------
1 | module Froggy.State where
2 |
3 | import Array
4 | import Froggy.Util (..)
5 | import Froggy.Grid as Grid
6 | import Froggy.Grid (..)
7 | import Froggy.TransitionUtil (..)
8 | import Froggy.Levels (..)
9 | import Froggy.Model (..)
10 | import Froggy.Commands (..)
11 |
12 | game : Signal (Maybe Game) -> Signal Game
13 | game loadedGame = foldp update initialGame (commands loadedGame)
14 |
15 | update : (Time, Command) -> Game -> Game
16 | update (time, command) game =
17 | case command of
18 | Nop -> game
19 | MoveBy positionDelta -> game |> moveBy positionDelta time
20 | MoveTo leaf -> game |> moveTo leaf time
21 | Continue -> game |> continue time
22 | RestartLevel -> game |> restartLevel time
23 | Start loadedGame -> start loadedGame time
24 |
25 | useKeyboard : Bool -> Game -> Game
26 | useKeyboard keyboard game = { game | usingKeyboard <- keyboard }
27 |
28 | moveBy : Grid.Position -> Time -> Game -> Game
29 | moveBy positionDelta time game =
30 | let newGame =
31 | if (positionDelta.x == 0) && (positionDelta.y == 0) then game
32 | else
33 | let leafPosition = game.scene.frog.leaf.position `translate` positionDelta
34 | maybeLeaf = game.scene.leaves |> findLeaf leafPosition
35 | in case maybeLeaf of
36 | Nothing -> game
37 | Just leaf -> game |> moveTo leaf time
38 | in newGame |> useKeyboard True
39 |
40 | findLeaf : Grid.Position -> [Leaf] -> Maybe Leaf
41 | findLeaf position leaves =
42 | let hasPosition leaf = leaf.position `equals` position
43 | in leaves |> filter hasPosition |> Array.fromList |> Array.get 0
44 |
45 | moveTo : Leaf -> Time -> Game -> Game
46 | moveTo leaf time game =
47 | let reachable = leaf |> reachableBy game.scene.frog
48 | scene = game.scene
49 | newGame =
50 | if reachable then
51 | { game |
52 | scene <- { scene |
53 | frog <- {
54 | leaf = leaf,
55 | lastMove = Just {
56 | oldValue = game.scene.frog.leaf,
57 | startTime = time
58 | }
59 | },
60 | leaves <- remove game.scene.leaves game.scene.frog.leaf
61 | }
62 | }
63 | else game
64 | in newGame |> useKeyboard False
65 |
66 | loadLevel : Time -> Maybe Scene -> Int -> Game
67 | loadLevel time oldScene levelNumber =
68 | let actualLevelNumber = if (levelNumber >= numberOfLevels) || (levelNumber < 0) then 0 else levelNumber
69 | level = getLevel actualLevelNumber
70 | leaves = loadLeafMatrix level.leafMatrix
71 | maybeLeaf = leaves |> findLeaf level.frogPosition
72 | leaf = maybeLeaf |> getOrElse (leaves |> head)
73 | in {
74 | scene = {
75 | frog = {
76 | leaf = leaf,
77 | lastMove = Nothing
78 | },
79 | leaves = leaves,
80 | levelNumber = actualLevelNumber
81 | },
82 | usingKeyboard = False,
83 | lastSceneChange = Just {
84 | oldValue = oldScene,
85 | startTime = time
86 | }
87 | }
88 |
89 | loadLeafMatrix : LeafMatrix -> [Leaf]
90 | loadLeafMatrix leafMatrix = leafMatrix |> indexedMap loadLeafRow |> concat
91 |
92 | loadLeafRow : Int -> [Bool] -> [Leaf]
93 | loadLeafRow y row =
94 | let make x value = {
95 | position = {
96 | x = x,
97 | y = y
98 | },
99 | value = value
100 | }
101 | isThere { value } = value
102 | removeValue leaf = { leaf - value }
103 | in row |> indexedMap make |> filter isThere |> map removeValue
104 |
105 | continue : Time -> Game -> Game
106 | continue time game =
107 | if | game.scene |> levelCompleted -> game |> nextLevel time
108 | | game.scene |> stuck -> game |> restartLevel time
109 | | otherwise -> game
110 |
111 | nextLevel : Time -> Game -> Game
112 | nextLevel time game = loadLevel time (Just game.scene) (game.scene.levelNumber + 1)
113 |
114 | restartLevel : Time -> Game -> Game
115 | restartLevel time game = loadLevel time (Just game.scene) game.scene.levelNumber
116 |
117 | initialGame : Game
118 | initialGame = newGame 0 Nothing
119 |
120 | newGame : Time -> Maybe (TransitionInfo (Maybe Scene)) -> Game
121 | newGame time lastSceneChange =
122 | let level0 = loadLevel time Nothing 0
123 | in { level0 |
124 | lastSceneChange <- lastSceneChange
125 | }
126 |
127 | start : Maybe Game -> Time -> Game
128 | start loadedGame time =
129 | let lastSceneChange = Just {
130 | oldValue = Nothing,
131 | startTime = time
132 | }
133 | in case loadedGame of
134 | Nothing -> newGame time lastSceneChange
135 | Just startedGame -> { startedGame |
136 | lastSceneChange <- lastSceneChange
137 | }
--------------------------------------------------------------------------------
/src/site/images/completed.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
164 |
--------------------------------------------------------------------------------
/src/site/images/message/90.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
130 |
--------------------------------------------------------------------------------
/src/site/images/message/180.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
130 |
--------------------------------------------------------------------------------
/src/site/images/message/0.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
130 |
--------------------------------------------------------------------------------
/src/site/images/message/270.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
130 |
--------------------------------------------------------------------------------
/src/elm/Froggy/View.elm:
--------------------------------------------------------------------------------
1 | module Froggy.View where
2 |
3 | import Maybe
4 | import Text
5 | import Graphics.Element as Element
6 | import Graphics.Input (..)
7 | import Easing
8 | import Easing (..)
9 | import Froggy.Util (..)
10 | import Froggy.TransitionUtil (..)
11 | import Froggy.Grid as Grid
12 | import Froggy.Grid (..)
13 | import Froggy.Levels (..)
14 | import Froggy.Model (..)
15 | import Froggy.Commands (..)
16 |
17 | view : String -> (Int, Int) -> Time -> Game -> Element
18 | view fontName (windowWidth, windowHeight) time game =
19 | case game.lastSceneChange of
20 | Just lastSceneChange ->
21 | let viewSize = min windowWidth windowHeight
22 | scene = game.scene |> viewScene fontName viewSize time lastSceneChange
23 | keyboardHint = game |> viewKeyboardHint fontName viewSize time |> collage viewSize viewSize
24 | cover = lastSceneChange |> viewCover (windowWidth, windowHeight) time
25 | in [scene, keyboardHint, cover] |> map (container windowWidth windowHeight middle) |> layers
26 | Nothing ->
27 | let blackRectangle = spacer windowWidth windowHeight |> Element.color black
28 | loadingImage = image 64 64 (imagePath "loading.gif") |> container windowWidth windowHeight middle
29 | in layers [blackRectangle, loadingImage]
30 |
31 | viewScene : String -> Int -> Time -> TransitionInfo (Maybe Scene) -> Scene -> Element
32 | viewScene fontName viewSize time lastSceneChange scene =
33 | let actualScene = case lastSceneChange.oldValue of
34 | Just oldScene -> if (time - lastSceneChange.startTime) < (sceneChangeDuration / 2) then oldScene else scene
35 | Nothing -> scene
36 | tileSize = viewSize |> getTileSize
37 | frog = actualScene.frog |> viewFrog tileSize time
38 | leaves = actualScene.leaves |> map (viewLeaf tileSize)
39 | targets = actualScene.leaves |> viewTargets actualScene.frog tileSize
40 | level = actualScene.levelNumber |> viewLevelNumber fontName tileSize
41 | message = actualScene |> viewMessage fontName tileSize time
42 | in (leaves ++ targets ++ frog ++ level ++ message) |> collage viewSize viewSize
43 |
44 | getTileSize : Int -> Float
45 | getTileSize viewSize = (viewSize |> toFloat) / mapSize
46 |
47 | mapSize = 8
48 |
49 | viewFrog : Float -> Time -> Frog -> [Form]
50 | viewFrog tileSize time frog =
51 | let newWorldPosition = frog.leaf.position |> toWorld tileSize
52 | worldPosition = case frog.lastMove of
53 | Just { oldValue, startTime } ->
54 | let oldWorldPosition = oldValue.position |> toWorld tileSize
55 | in ease easeInOutQuint (pair float) oldWorldPosition newWorldPosition moveDuration (time - startTime)
56 | Nothing -> newWorldPosition
57 | lastLeaf = case frog.lastMove of
58 | Nothing -> []
59 | Just { oldValue, startTime } ->
60 | let alphaValue = ease easeInCubic float 1 0 moveDuration (time - startTime)
61 | in [viewLeaf tileSize oldValue |> alpha alphaValue]
62 | size = case frog.lastMove of
63 | Nothing -> 1
64 | Just { startTime } -> ease (easeInQuad |> retour) float 1 1.2 moveDuration (time - startTime)
65 | frogSprite = sprite worldPosition tileSize (imagePath "frog.png") |> rotate (angleOf frog |> toFloat |> degrees) |> scale size
66 | in lastLeaf ++ [frogSprite]
67 |
68 | moveDuration : Time
69 | moveDuration = 250 * millisecond
70 |
71 | sprite : (Float, Float) -> Float -> String -> Form
72 | sprite = customSprite identity
73 |
74 | customSprite : (Element -> Element) -> (Float, Float) -> Float -> String -> Form
75 | customSprite transform worldPosition tileSize url =
76 | let element = image (round tileSize) (round tileSize) url
77 | in element |> transform |> makeForm worldPosition
78 |
79 | makeForm : (Float, Float) -> Element -> Form
80 | makeForm worldPosition element = element |> toForm |> move worldPosition
81 |
82 | toWorld : Float -> Grid.Position -> (Float, Float)
83 | toWorld tileSize position =
84 | let transform coordinate = ((coordinate |> toFloat) - mapSize / 2 + 0.5) * tileSize
85 | in (transform position.x, -(transform position.y))
86 |
87 | viewLeaf : Float -> Leaf -> Form
88 | viewLeaf tileSize leaf =
89 | let worldPosition = leaf.position |> toWorld tileSize
90 | in sprite worldPosition tileSize (imagePath "leaf.png")
91 |
92 | viewTargets : Frog -> Float -> [Leaf] -> [Form]
93 | viewTargets frog tileSize leaves =
94 | let targets = leaves |> filter (reachableBy frog)
95 | toClickable target = clickable moveTo.handle (MoveTo target)
96 | distanceOf target = (distance target.position.x frog.leaf.position.x) + (distance target.position.y frog.leaf.position.y)
97 | angleOf target = frog.leaf `angleBetween` target |> getOrElse 0
98 | filename target = "arrows/" ++ (distanceOf target |> show) ++ "/" ++ (angleOf target |> show) ++ ".svg" |> imagePath
99 | worldPosition target = target.position |> toWorld tileSize
100 | viewTarget target = customSprite (toClickable target) (worldPosition target) tileSize (filename target)
101 | in targets |> map viewTarget
102 |
103 | viewLevelNumber : String -> Float -> Int -> [Form]
104 | viewLevelNumber fontName tileSize levelNumber =
105 | let position = (getLevel levelNumber) |> .levelPosition
106 | worldPosition = position |> toWorld tileSize
107 | background = sprite worldPosition tileSize (imagePath "level.png")
108 | indicator = textSprite fontName position tileSize ("Level\n" ++ show levelNumber ++ "/" ++ show (numberOfLevels - 1)) |> rotate (-1 |> degrees)
109 | in [background, indicator]
110 |
111 | textSprite : String -> Grid.Position -> Float -> String -> Form
112 | textSprite fontName position tileSize string =
113 | let textSize = tileSize / 5
114 | worldPosition = position |> toWorld tileSize
115 | in gameText fontName textSize string |> makeForm worldPosition
116 |
117 | gameText : String -> Float -> String -> Element
118 | gameText fontName height string = toText string |> Text.style {
119 | typeface = [fontName],
120 | height = Just height,
121 | color = red,
122 | bold = True,
123 | italic = False,
124 | line = Nothing
125 | } |> centered
126 |
127 | viewMessage : String -> Float -> Time -> Scene -> [Form]
128 | viewMessage fontName tileSize time scene =
129 | let inverseAngle = ((angleOf scene.frog + 180) % 360) |> toFloat
130 | deltaX = cos (inverseAngle |> degrees) |> round
131 | deltaY = (-1 * sin (inverseAngle |> degrees)) |> round
132 | position = scene.frog.leaf.position `translate` { x = deltaX, y = deltaY }
133 | worldPosition = toWorld tileSize position
134 | filename = "message/" ++ (inverseAngle |> show) ++ ".svg" |> imagePath
135 | background = sprite worldPosition tileSize filename
136 | iconSize = tileSize / 2.5
137 | forms =
138 | if | scene |> levelCompleted ->
139 | let lastLevel = scene.levelNumber == numberOfLevels - 1
140 | in if lastLevel then
141 | [background, sprite worldPosition iconSize (imagePath "completed.svg")]
142 | else
143 | [background, sprite worldPosition iconSize (imagePath "next.png")]
144 | | scene |> stuck -> [background, sprite worldPosition iconSize (imagePath "restart.png")]
145 | | otherwise -> []
146 | scaleFactor = case scene.frog.lastMove of
147 | Just { startTime } -> ease easeInOutBack float 0 1 (moveDuration * 2) (time - startTime)
148 | Nothing -> 1
149 | in forms |> map (scale scaleFactor)
150 |
151 | imagePath : String -> String
152 | imagePath filename = "images/" ++ filename
153 |
154 | viewKeyboardHint : String -> Int -> Time -> Game -> [Form]
155 | viewKeyboardHint fontName viewSize time game =
156 | if game.usingKeyboard then
157 | let tileSize = viewSize |> getTileSize
158 | gridPosition = game.scene.levelNumber |> getLevel |> .keyboardHintPosition
159 | worldPosition = gridPosition |> toWorld tileSize
160 | background = sprite worldPosition tileSize (imagePath "key.svg")
161 | text string = string |> textSprite fontName gridPosition tileSize
162 | forms = if | (game.scene |> levelCompleted) && (game.scene.levelNumber == 0) -> [background, text "Enter"]
163 | | (game.scene |> stuck) && not (game.scene |> levelCompleted) -> [background, text "Esc"]
164 | | (game.scene |> onlyDoubleJump) && (game.scene.levelNumber == 0) -> [background, text "Shift"]
165 | | otherwise -> []
166 | scaleFactor = case game.scene.frog.lastMove of
167 | Just { startTime } -> ease easeInOutQuad float 0 1 moveDuration (time - startTime)
168 | Nothing -> 1
169 | in forms |> map (scale scaleFactor)
170 | else []
171 |
172 | viewCover : (Int, Int) -> Time -> TransitionInfo (Maybe Scene) -> Element
173 | viewCover (windowWidth, windowHeight) time lastSceneChange =
174 | let easingFunction = if lastSceneChange.oldValue |> Maybe.isJust then retour Easing.linear else Easing.flip Easing.linear
175 | factor = ease easingFunction float 0 1 sceneChangeDuration (time - lastSceneChange.startTime)
176 | in spacer windowWidth windowHeight |> Element.color black |> opacity factor
177 |
178 | sceneChangeDuration : Time
179 | sceneChangeDuration = 300 * millisecond
--------------------------------------------------------------------------------
/src/site/images/key.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
301 |
--------------------------------------------------------------------------------
/src/site/images/arrows/2/180.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
616 |
--------------------------------------------------------------------------------
/src/site/images/arrows/1/180.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
647 |
--------------------------------------------------------------------------------
/src/site/images/arrows/2/0.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
699 |
--------------------------------------------------------------------------------
/src/site/images/arrows/1/0.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
710 |
--------------------------------------------------------------------------------
/src/site/images/arrows/2/90.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
719 |
--------------------------------------------------------------------------------
/src/site/images/arrows/2/270.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
719 |
--------------------------------------------------------------------------------