I forgot to push first #27
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						|  | @ -12,3 +12,4 @@ extra/ | |||
| *.dll | ||||
| 
 | ||||
| stack.yaml.lock | ||||
| .vscode/settings.json | ||||
|  |  | |||
							
								
								
									
										3
									
								
								.vscode/tasks.json
									
										
									
									
										vendored
									
									
								
							
							
						
						|  | @ -49,9 +49,10 @@ | |||
|             "args": [ | ||||
|                 "-s", | ||||
|                 "-o", "verslag.pdf", | ||||
|                 "-f", "markdown+smart+header_attributes+yaml_metadata_block+auto_identifiers", | ||||
|                 "-f", "markdown+smart+header_attributes+yaml_metadata_block+auto_identifiers+implicit_figures", | ||||
|                 "--pdf-engine", "lualatex", | ||||
|                 "--template", "eisvogel", | ||||
|                 "--dpi=300", | ||||
|                 "header.yaml", | ||||
|                 "README.md" | ||||
|             ], | ||||
|  |  | |||
							
								
								
									
										305
									
								
								README.md
									
										
									
									
									
								
							
							
						
						|  | @ -1,40 +1,9 @@ | |||
| <!-- | ||||
| ## Functional requirements | ||||
| 
 | ||||
| - [ ] Parsing of engine configuration file to game object | ||||
| - [ ] Rendering of all game objects (Levels, objects, entities, ...) | ||||
| - [ ] A start menu with the possibility of selecting a level | ||||
| - [ ] An end screen that shows wether or not a player won | ||||
| - [ ] Support for built-in engine functions | ||||
| 
 | ||||
| - [x] Player can move around in grid-world. | ||||
| - [ ] Player can pick up objects. | ||||
| - [ ] Player can use objects. | ||||
| - [ ] Player can loose and gain health points. | ||||
| - [ ] Player can interact with other entities (fight enemies, open doors, ...). | ||||
| - [ ] Player can go to the next level. | ||||
| 
 | ||||
| ## Not-functional requirements | ||||
| 
 | ||||
| - [x] Use Parsing. | ||||
| - [ ] Use at least one (1) monad transformer. | ||||
| - [ ] Write good and plenty of documentation.:w | ||||
| 
 | ||||
| - [x] Write tests (for example, using HSpec). | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| Nuttige links:  | ||||
| 
 | ||||
| - https://jakewheat.github.io/intro_to_parsing/ | ||||
| 
 | ||||
| ``` | ||||
| Jarne — Today at 22:44 | ||||
| Da kan hoor en had da eerst, me gloss eeft geen goede text dus... | ||||
| ListDirectory, en er was ook een fuctie takeBaseName | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| Config files cannot end with blank line | ||||
| 
 | ||||
| <div style="page-break-after: always;"></div> | ||||
| --> | ||||
|  | @ -45,40 +14,193 @@ RPG-Engine is a game engine for playing and creating your own RPG games. | |||
| 
 | ||||
| If you are interested in the development side of things, [development notes can be found here](#Development-notes). | ||||
| 
 | ||||
| This README serves as both documentation and project report, so excuse the details that might not be important for the average user. | ||||
| This README serves as both documentation and project report, so excuse the details that might not be important for the average reader. | ||||
| 
 | ||||
| ## Playing the game | ||||
| 
 | ||||
| These are the keybinds *in* the game. All other keybinds in the menus should be straightforward. | ||||
| These are the keybinds while *in* game. All other keybinds in menus etc. should be straightforward. | ||||
| 
 | ||||
| | Action         | Primary       | Secondary   | | ||||
| | -------------- | ------------- | ----------- | | ||||
| | Move up        | `Arrow Up`    | `w`         | | ||||
| | Move left      | `Arrow Left`  | `a`         | | ||||
| | Move down      | `Arrow Down`  | `s`         | | ||||
| | Move right     | `Arrow Right` | `d`         | | ||||
| | Interaction    | `Space`       | `f`         | | ||||
| | Show inventory | `i`           | `Tab`       | | ||||
| | Restart level  | `r`           |             | | ||||
| | Quit game      | `Esc`         |             | | ||||
| | Action         | Primary       | Secondary    | | ||||
| | -------------- | ------------- | ------------ | | ||||
| | Move up        | `Arrow Up`    | `w`          | | ||||
| | Move left      | `Arrow Left`  | `a`          | | ||||
| | Move down      | `Arrow Down`  | `s`          | | ||||
| | Move right     | `Arrow Right` | `d`          | | ||||
| | Interaction    | `Space`       | `f`, `Enter` | | ||||
| | Show inventory | `i`           | `Tab`        | | ||||
| | Restart level  | `r`           |              | | ||||
| | Quit game      | `Esc`         |              | | ||||
| 
 | ||||
| ### Example playthrough | ||||
| 
 | ||||
| TODO | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| { width=600 } | ||||
| 
 | ||||
| - An example playthrough, with pictures and explanations | ||||
| ## Development notes | ||||
| 
 | ||||
| ### Engine architecture | ||||
| 
 | ||||
| `RPGEngine` is the main module. It contains the `playRPGEngine` function which bootstraps the whole game. It is also | ||||
|  the game loop. From here, `RPGEngine` talks to its submodules. | ||||
| 
 | ||||
| These submodules are `Config`, `Data`, `Input`, `Parse` & `Render`. They are all responsible for their own part. However, | ||||
|  each of these submodules has their own submodules to divide the work. They are conveniently named after the state of | ||||
|  the game that they work with, e.g. the main menu has a module & when the game is playing is a different module. A special | ||||
|  one is `Core`, which is kind of like a library for every piece. It contains functions that are regularly used by the other modules. | ||||
| 
 | ||||
| - `Config`: Configuration values, should ultimately be moved into parsing from a file. | ||||
| - `Data`: Data containers and accessors of information. | ||||
| - `Input`: Anything that handles input or changes the state of the game. | ||||
| - `Parse`: Parsing | ||||
| - `Render`: Rendering of the game and everything below that. | ||||
| 
 | ||||
| #### Monads/Monad stack | ||||
| 
 | ||||
| Monads:  | ||||
| 
 | ||||
| - Extensive use of `Maybe` for integers or infinity and `do` in parser implementation. | ||||
| - `IO` to handle external information | ||||
| - ... | ||||
| 
 | ||||
| Monad transformer: ?? | ||||
| 
 | ||||
| I am afraid I did not write any monad transformers in this project. I think I could (and should) have focused more on | ||||
|  writing monads and monad transformers. In hindsight, I can see where I could and should have used them. I can think of | ||||
|  plenty of ways to make the current implementation simpler. This is unfortunate. However, I want to believe that my next | ||||
|  time writing a more complex Haskell program, I will remember using monad transformers. Sadly, I forgot this time. | ||||
| 
 | ||||
| An example of where I would use a monad transformer - in hindsight: | ||||
| 
 | ||||
| 1. Interactions in game: something along the lines of ... | ||||
| 
 | ||||
| ```haskell | ||||
| newtype StateT m a = StateT { runStateT ::  m a } | ||||
| 
 | ||||
| instance Monad m => Monad (StateT m) where | ||||
|     return = lift . return | ||||
|     x >>= f = StateT $ do | ||||
|         v <- runStateT x | ||||
|         case v of | ||||
|             Playing level    -> runStateT ( f level ) | ||||
|             Paused  continue -> runStateT ( continue >>= f ) | ||||
|             -- etc | ||||
| 
 | ||||
| class MonadTransformer r where | ||||
|     lift      :: Monad m => m a -> (r m) a | ||||
| instance MonadTransformer StateT where | ||||
|     lift      = StateT | ||||
| ``` | ||||
| 
 | ||||
| 2. Interaction with the outside world should also be done with Monad(transformers) instead of using `unsafePerformIO`. | ||||
| 
 | ||||
| ### Tests | ||||
| 
 | ||||
| Overall, only parsing is tested using Hspec. However, parsing is tested *thoroughly* and I am quite sure that there aren't | ||||
|  a lot of edge cases that I did not catch. This makes for a relaxing environment where you can quickly check if a change | ||||
|  you made breaks anything. | ||||
| 
 | ||||
| `Spec` is the main module. It does not contain any tests, but functions as the 'discover' module to find the other tests | ||||
|  in its folder. | ||||
| 
 | ||||
| `Parser.StructureSpec` tests functionality of `RPGEngine.Parse.TextToStructure`, `Parser.GameSpec` tests functionality | ||||
|  of `RPGEngine.Parse.StructureToGame`. | ||||
| 
 | ||||
| Known issues: | ||||
| 
 | ||||
| - [ ] Rendering is still not centered, I am sorry for those with small screens. | ||||
| - [ ] Config files cannot end with an empty line. I could not get that to work and I decided that it was more important | ||||
|  to implement other functionality first. Unfortunately, I was not able to get back to it yet. | ||||
| - [ ] The parser is unable to parse layouts with trailing whitespace. | ||||
| 
 | ||||
| ## Conclusion  | ||||
| 
 | ||||
| Parsing was way harder than I initially expected. I believe over half my time on this project was spent trying to write the | ||||
|  parser. I am still not absolutely sure that it will work with *everything*, but it gets the job done at the moment. I don't | ||||
|  know if parsing into a structure before transforming the structure into a game was a good move. It might have saved me some | ||||
|  time if I did it straight to `Game`. I want to say that I have a parser-to-structure module now, but even so, there are some | ||||
|  links between `TextToStructure` and `Game` that make it almost useless to any other project (without changing anything). | ||||
| 
 | ||||
| Player-object interaction was easier than previous projects. I believe this is both because I am getting used to it by now | ||||
|  and because I spent a lot of time beforehand structuring everything. I also like to think that structuring the project is | ||||
|  what I did right. There is a clear hierarchy and you can find what you are looking for fairly easy, without having to search | ||||
|  for a function in file contents or having to scavenge multiple different files before finding what you want. However, I | ||||
|  absolutely wasted a lot of time restructuring the project multiple times, mostly because I was running into dependency cycles. | ||||
| 
 | ||||
| Overall, I believe the project was a success. I am proud of the end result. Though, please note my comments on monad transformers. | ||||
| 
 | ||||
| ### Assets & dependencies | ||||
| 
 | ||||
| The following assets were used (and modified if specified): | ||||
| 
 | ||||
| - Kyrise's Free 16x16 RPG Icon Pack<sup>[[1]](#1)</sup> | ||||
| 
 | ||||
| - 2D Pixel Dungeon Asset Pack by Pixel_Poem<sup>[[2]](#2)</sup> | ||||
| 
 | ||||
|   Every needed asset was taken and put into its own `.png`, instead of in the overview. | ||||
| 
 | ||||
| RPG-Engine makes use of the following libraries: | ||||
| 
 | ||||
| - [directory](https://hackage.haskell.org/package/directory) for listing levels in a directory | ||||
| - [gloss](https://hackage.haskell.org/package/gloss) for game rendering | ||||
| - [gloss-juicy](https://hackage.haskell.org/package/gloss-juicy) for rendering images | ||||
| - [hspec](https://hackage.haskell.org/package/hspec) for testing | ||||
| - [hspec-discover](https://hackage.haskell.org/package/hspec-discover) for allowing to split test files in multiple files | ||||
| - [parsec](https://hackage.haskell.org/package/parsec) for parsing configuration files | ||||
| 
 | ||||
| <div style="page-break-after: always; visibility: hidden">\pagebreak</div> | ||||
| 
 | ||||
| ## Writing your own stages | ||||
| ## References | ||||
| 
 | ||||
| A stage description file, conventionally named `<stage_name>.txt` is a file with a JSON-like format. It is used to describe | ||||
|  everything inside a single stage of your game, including anything related to the player, the levels your game contains | ||||
| <a id="1">[1]</a> [Kyrise's Free 16x16 RPG Icon Pack](https://kyrise.itch.io/kyrises-free-16x16-rpg-icon-pack) © 2018 | ||||
|  by [Kyrise](https://kyrise.itch.io/) is licensed under [CC BY 4.0](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1) | ||||
| 
 | ||||
| <a id="2">[2]</a> [2D Pixel Dungeon Asset Pack](https://pixel-poem.itch.io/dungeon-assetpuck) by [Pixel_Poem](https://pixel-poem.itch.io/) | ||||
|  is not licensed | ||||
| 
 | ||||
| <div style="page-break-after: always; visibility: hidden">\pagebreak</div> | ||||
| 
 | ||||
| ## Appendix A: Future development ideas | ||||
| 
 | ||||
| The following ideas could (or should) be implemented in the future of this project. | ||||
| 
 | ||||
| - [ ] **Entity system:** With en ES, you can implement moving entities and repeated input. It also resembles the typical | ||||
|  game loop more closely which can make it easier to implement other ideas in the future. | ||||
| - [ ] **Game music:** Ambient game music and sound effects can improve the gaming experience I think. | ||||
| - [ ] **Expand configuration file:** Implement the same methods for parsing stage description files to a configuration file, | ||||
|  containing keybinds, dimension sizes, even window titles, making this a truly customizable engine. | ||||
| - [ ] **Camera follows player:** The camera should follow the player, making it always center. This allows for larger levels | ||||
|  increases the immersion of the game. | ||||
| 
 | ||||
| Changes in the backend: | ||||
| 
 | ||||
| - [ ] **Make inventory a state** At the moment, there is a boolean for inventory rendering. This should be turned into a state, | ||||
|  so it makes more sense to call it from other places as well. | ||||
| - [ ] **Direction of entities** Change the rendering based on the direction of an entity. | ||||
| - [ ] **Inventory with more details** The inventory should show more details of items, e.g. name, value, remaining use | ||||
|  times and description. | ||||
| 
 | ||||
| <div style="page-break-after: always; visibility: hidden">\pagebreak</div> | ||||
| 
 | ||||
| ## Appendix B: Writing your own worlds | ||||
| 
 | ||||
| A world description file, conventionally named `<world_name_or_level_x>.txt` is a file with a JSON-like format. It is used to describe | ||||
|  everything inside a single world of your game, including anything related to the player, the levels your game contains | ||||
|  and what happens in that level. It is essentially the raw representation of the initial state of a single game. | ||||
| 
 | ||||
| > Note: At the moment, every game has a single stage description file. Chaining several files together is not possible yet. | ||||
| 
 | ||||
| A stage description file consists of several elements. | ||||
| A world description file consists of several elements. | ||||
| 
 | ||||
| | Element         | Short description                                                                                         | | ||||
| | --------------- | --------------------------------------------------------------------------------------------------------- | | ||||
|  | @ -159,7 +281,7 @@ levels: [ | |||
| ``` | ||||
| </details> | ||||
| 
 | ||||
| This stage description file consists of a single `Block`. A stage description file always does. This top level `Block` | ||||
| This world description file consists of a single `Block`. A world description file always does. This top level `Block` | ||||
|  contains two `Value`s `player` and `levels`, not separated by commas.  | ||||
| 
 | ||||
| `player` describes a `Block` that represents the player of the game. Its `Entry`s are `hp` (a traditional value) and | ||||
|  | @ -252,79 +374,4 @@ If we look at the example, all the objects are | |||
|                             length = 1 | ||||
|                             Condition ('inventoryContains(key)') | ||||
|                         Entry = empty ConditionList + Action ('leave()') | ||||
| ``` | ||||
| 
 | ||||
| <div style="page-break-after: always; visibility: hidden">\pagebreak</div> | ||||
| 
 | ||||
| ## Development notes | ||||
| 
 | ||||
| ### Engine architecture | ||||
| 
 | ||||
| <mark>TODO</mark> | ||||
| 
 | ||||
| `RPGEngine` is the main module. It contains the `playRPGEngine` function which bootstraps the whole game. It is also | ||||
|  the game loop. From here, `RPGEngine` talks to its submodules. | ||||
| 
 | ||||
| These submodules are `Config`, `Data`, `Input`, `Parse` & `Render`. They are all responsible for their own part, either | ||||
|  containing the program configuration, data containers, everything needed to handle input, everything needed to parse a | ||||
|  source file & everything needed to render the game. However, each of these submodules has their own submodules to | ||||
|  divide the work. They are conveniently named after the state of the game that they work with, e.g. the main menu has a | ||||
|  module & when the game is playing is a different module. A special one is `Core`, which is kind of like a library for | ||||
|  every piece. It contains functions that are regularly used by the other modules. | ||||
| 
 | ||||
| #### Monads/Monad stack | ||||
| 
 | ||||
| <mark>TODO</mark> | ||||
| 
 | ||||
| ### Tests | ||||
| 
 | ||||
| <mark>TODO</mark> | ||||
| 
 | ||||
| ### Assets & dependencies | ||||
| 
 | ||||
| The following assets were used (and modified if specified): | ||||
| 
 | ||||
| - Kyrise's Free 16x16 RPG Icon Pack<sup>[[1]](#1)</sup> | ||||
| 
 | ||||
| - 2D Pixel Dungeon Asset Pack by Pixel_Poem<sup>[[2]](#2)</sup> | ||||
| 
 | ||||
|   Every needed asset was taken and put into its own `.png`, instead of in the overview. | ||||
| 
 | ||||
| RPG-Engine makes use of the following libraries: | ||||
| 
 | ||||
| - [directory](https://hackage.haskell.org/package/directory) for listing levels in a directory | ||||
| - [gloss](https://hackage.haskell.org/package/gloss) for game rendering | ||||
| - [gloss-juicy](https://hackage.haskell.org/package/gloss-juicy) for rendering images | ||||
| - [hspec](https://hackage.haskell.org/package/hspec) for testing | ||||
| - [hspec-discover](https://hackage.haskell.org/package/hspec-discover) for allowing to split test files in multiple files | ||||
| - [parsec](https://hackage.haskell.org/package/parsec) for parsing configuration files | ||||
| 
 | ||||
| ### Future development ideas | ||||
| 
 | ||||
| The following ideas could (or should) be implemented in the future of this project. | ||||
| 
 | ||||
| - [ ] **Entity system:** With en ES, you can implement moving entities and repeated input. It also resembles the typical | ||||
|  game loop more closely which can make it easier to implement other ideas in the future. | ||||
| - [ ] **Game music:** Ambient game music and sound effects can improve the gaming experience I think. | ||||
| - [ ] **Expand configuration file:** Implement the same methods for parsing stage description files to a configuration file, | ||||
|  containing keybinds, dimension sizes, even window titles, making this a truly customizable engine. | ||||
| - [ ] **Camera follows player:** The camera should follow the player, making it always center. This allows for larger levels | ||||
|  increases the immersion of the game. | ||||
| 
 | ||||
| <div style="page-break-after: always; visibility: hidden">\pagebreak</div> | ||||
| 
 | ||||
| ## Conclusion  | ||||
| 
 | ||||
| Parsing was way harder than I initially expected. About half of my time on this project was spent writing the parser. | ||||
| 
 | ||||
| <mark>TODO</mark> | ||||
| 
 | ||||
| <div style="page-break-after: always; visibility: hidden">\pagebreak</div> | ||||
| 
 | ||||
| ## References | ||||
| 
 | ||||
| <a id="1">[1]</a> [Kyrise's Free 16x16 RPG Icon Pack](https://kyrise.itch.io/kyrises-free-16x16-rpg-icon-pack) © 2018 | ||||
|  by [Kyrise](https://kyrise.itch.io/) is licensed under [CC BY 4.0](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1) | ||||
| 
 | ||||
| <a id="2">[2]</a> [2D Pixel Dungeon Asset Pack](https://pixel-poem.itch.io/dungeon-assetpuck) by [Pixel_Poem](https://pixel-poem.itch.io/) | ||||
|  is not licensed | ||||
| ``` | ||||
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 905 B | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-1-01.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-2-01.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-2-02.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-2-03.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-2-04.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-2-05.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-2-06.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-3-01.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-3-02.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 19 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-3-03.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 18 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-3-04.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-3-05.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 22 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-3-06.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-3-07.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-3-08.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/5-3-09.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/selection.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 8.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								extra/walkthrough/you-win.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.9 KiB | 
							
								
								
									
										144
									
								
								levels/level5.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,144 @@ | |||
| player: { | ||||
|     hp: 100, | ||||
|     inventory: [ | ||||
|         { | ||||
|             id: "dagger", | ||||
|             x: 0, | ||||
|             y: 0, | ||||
|             name: "Swiss army knife", | ||||
|             description: "Your trustworthy army knife will never let you down", | ||||
|             useTimes: infinite, | ||||
|             value: 5, | ||||
|             actions: {} | ||||
|         }, | ||||
|         { | ||||
|             id: "potion", | ||||
|             x: 0, | ||||
|             y: 0, | ||||
|             name: "Small healing potion", | ||||
|             description: "Will recover you from small injuries", | ||||
|             useTimes: 5, | ||||
|             value: 5, | ||||
|             actions: {} | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| levels: [ | ||||
|     { | ||||
|         layout: { | ||||
|             | * * * * * * * | ||||
|             | * s . . . e * | ||||
|             | * * * * * * * | ||||
|         }, | ||||
|         items: [], | ||||
|         entities: [] | ||||
|     }, | ||||
|     { | ||||
|         layout: { | ||||
|             | x x * * * x x x x | ||||
|             | x x * . * x x x x | ||||
|             | * * * . * * * * * | ||||
|             | * s . . . . . e * | ||||
|             | * * * * * * * * * | ||||
|         }, | ||||
|         items: [ | ||||
|             { | ||||
|                 id: "key", | ||||
|                 x: 3, | ||||
|                 y: 3, | ||||
|                 name: "Secret key", | ||||
|                 description: "What if this key opens a secret door?", | ||||
|                 useTimes: 1, | ||||
|                 value: 0, | ||||
|                 actions: { | ||||
|                     [not(inventoryFull())] retrieveItem(key), | ||||
|                     [] leave() | ||||
|                 } | ||||
|             } | ||||
|         ], | ||||
|         entities: [ | ||||
|             { | ||||
|                 id: "door", | ||||
|                 x: 4, | ||||
|                 y: 1, | ||||
|                 name: "Secret door", | ||||
|                 description: "This door can only be opened with a secret key", | ||||
|                 direction: left, | ||||
|                 actions: { | ||||
|                     [inventoryContains(key)] useItem(key), | ||||
|                     [] leave() | ||||
|                 } | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         layout: { | ||||
|             | * * * * * * * * * * * | ||||
|             | * . . . . . . . . . * | ||||
|             | * * * * * * * * * . * | ||||
|             | * e . . . . . . . s * | ||||
|             | * * * * * * * * * . * | ||||
|             | x x x x x x x x * . * | ||||
|             | * * * * * * * * * . * | ||||
|             | * . . . . . . . . . * | ||||
|             | * * * * * * * * * * * | ||||
|         }, | ||||
|         items: [ | ||||
|             { | ||||
|                 id: "key", | ||||
|                 x: 1, | ||||
|                 y: 1, | ||||
|                 name: "Key to sturdy door", | ||||
|                 description: "You have proven worthy", | ||||
|                 useTimes: 1, | ||||
|                 value: 0, | ||||
|                 actions: { | ||||
|                     [not(inventoryFull())] retrieveItem(key), | ||||
|                     [] leave() | ||||
|                 } | ||||
|             }, | ||||
|             { | ||||
|                 id: "sword", | ||||
|                 x: 1, | ||||
|                 y: 7, | ||||
|                 name: "Mighty sword", | ||||
|                 description: "Slayer of evil", | ||||
|                 useTimes: 3, | ||||
|                 value: 100, | ||||
|                 actions: { | ||||
|                     [not(inventoryFull())] retrieveItem(sword), | ||||
|                     [] leave() | ||||
|                 } | ||||
|             } | ||||
|         ], | ||||
|         entities: [ | ||||
|             { | ||||
|                 id: "door", | ||||
|                 x: 8, | ||||
|                 y: 5, | ||||
|                 name: "Sturdy door", | ||||
|                 description: "I wonder what's behind it?", | ||||
|                 direction: right, | ||||
|                 actions: { | ||||
|                     [inventoryContains(key)] useItem(key), | ||||
|                     [] leave() | ||||
|                 } | ||||
|             }, | ||||
|             { | ||||
|                 id: "devil", | ||||
|                 x: 6, | ||||
|                 y: 1, | ||||
|                 name: "Evil powers", | ||||
|                 description: "Certainly from hell", | ||||
|                 hp: 55, | ||||
|                 value: 10, | ||||
|                 actions: { | ||||
|                     [inventoryContains(dagger)] decreaseHp(devil, dagger), | ||||
|                     [inventoryContains(sword)] decreaseHp(devil, sword), | ||||
|                     [] leave() | ||||
|                 } | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
| ] | ||||
|  | @ -16,6 +16,7 @@ import Data.Foldable (find) | |||
| handleInputActionSelection :: InputHandler Game | ||||
| handleInputActionSelection = composeInputHandlers [ | ||||
|     handleKey (SpecialKey KeySpace) Down selectAction, | ||||
|     handleKey (SpecialKey KeyEnter) Down selectAction, | ||||
| 
 | ||||
|     handleKey (SpecialKey KeyUp)    Down $ moveSelector North, | ||||
|     handleKey (SpecialKey KeyDown)  Down $ moveSelector South | ||||
|  |  | |||
|  | @ -36,6 +36,7 @@ handleInputPlaying = composeInputHandlers [ | |||
| 
 | ||||
|     -- Interaction with entities and items | ||||
|     handleKey (SpecialKey KeySpace) Down checkForInteraction, | ||||
|     handleKey (SpecialKey KeyEnter) Down checkForInteraction, | ||||
|     handleKey (Char 'f')            Down checkForInteraction, | ||||
| 
 | ||||
|     handleKey (Char 'i')            Down $ toggleInventoryShown True, | ||||
|  |  | |||
|  | @ -42,8 +42,7 @@ focusPlayer _ = id | |||
| renderLevel :: Renderer Level | ||||
| renderLevel Level{ layout = l, items = i, entities = e } = level | ||||
|     where level    = pictures [void, layout, items, entities] | ||||
|         --   void     = createVoid | ||||
|           void     = blank | ||||
|           void     = createVoid | ||||
|           layout   = renderLayout l | ||||
|           items    = renderItems i | ||||
|           entities = renderEntities e | ||||
|  | @ -92,12 +91,12 @@ renderInventory :: Player -> Picture | |||
| renderInventory Player{ showInventory = False } = blank | ||||
| renderInventory Player{ inventory = list } = pictures [overlay, title, items] | ||||
|     where title = translate 0 (offset (-1)) $ scale uizoom uizoom $ color white $ text "Inventory" | ||||
|           items = pictures $ map move $ zip [0::Int ..] (map (getRender . itemId) list) | ||||
|           items = pictures $ zipWith (curry move) [0::Int ..] (map (getRender . itemId) list) | ||||
|           move (i, pic) = translate 0 (offset i) pic | ||||
|           offset i      = negate (zoom * resolution * fromIntegral i) | ||||
| 
 | ||||
| withHealthBar :: HP -> Picture -> Picture | ||||
| withHealthBar (Nothing) renderedEntity = renderedEntity | ||||
| withHealthBar Nothing   renderedEntity = renderedEntity | ||||
| withHealthBar (Just hp) renderedEntity = pictures [renderedEntity, positionedBar] | ||||
|     where positionedBar = scale smaller smaller $ translate left up renderedBar | ||||
|           renderedBar   = pictures [heart, counter] | ||||
|  |  | |||