I forgot to push first #27
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						|  | @ -12,3 +12,4 @@ extra/ | ||||||
| *.dll | *.dll | ||||||
| 
 | 
 | ||||||
| stack.yaml.lock | stack.yaml.lock | ||||||
|  | .vscode/settings.json | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								.vscode/tasks.json
									
										
									
									
										vendored
									
									
								
							
							
						
						|  | @ -49,9 +49,10 @@ | ||||||
|             "args": [ |             "args": [ | ||||||
|                 "-s", |                 "-s", | ||||||
|                 "-o", "verslag.pdf", |                 "-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", |                 "--pdf-engine", "lualatex", | ||||||
|                 "--template", "eisvogel", |                 "--template", "eisvogel", | ||||||
|  |                 "--dpi=300", | ||||||
|                 "header.yaml", |                 "header.yaml", | ||||||
|                 "README.md" |                 "README.md" | ||||||
|             ], |             ], | ||||||
|  |  | ||||||
							
								
								
									
										287
									
								
								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:  | Nuttige links:  | ||||||
| 
 | 
 | ||||||
| - https://jakewheat.github.io/intro_to_parsing/ | - https://jakewheat.github.io/intro_to_parsing/ | ||||||
| 
 | 
 | ||||||
| ``` | Config files cannot end with blank line | ||||||
| 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 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| --- |  | ||||||
| 
 | 
 | ||||||
| <div style="page-break-after: always;"></div> | <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). | 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 | ## 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    | | | Action         | Primary       | Secondary    | | ||||||
| | -------------- | ------------- | ----------- | | | -------------- | ------------- | ------------ | | ||||||
| | Move up        | `Arrow Up`    | `w`          | | | Move up        | `Arrow Up`    | `w`          | | ||||||
| | Move left      | `Arrow Left`  | `a`          | | | Move left      | `Arrow Left`  | `a`          | | ||||||
| | Move down      | `Arrow Down`  | `s`          | | | Move down      | `Arrow Down`  | `s`          | | ||||||
| | Move right     | `Arrow Right` | `d`          | | | Move right     | `Arrow Right` | `d`          | | ||||||
| | Interaction    | `Space`       | `f`         | | | Interaction    | `Space`       | `f`, `Enter` | | ||||||
| | Show inventory | `i`           | `Tab`        | | | Show inventory | `i`           | `Tab`        | | ||||||
| | Restart level  | `r`           |              | | | Restart level  | `r`           |              | | ||||||
| | Quit game      | `Esc`         |              | | | Quit game      | `Esc`         |              | | ||||||
| 
 | 
 | ||||||
| ### Example playthrough | ### 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> | <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 | <a id="1">[1]</a> [Kyrise's Free 16x16 RPG Icon Pack](https://kyrise.itch.io/kyrises-free-16x16-rpg-icon-pack) © 2018 | ||||||
|  everything inside a single stage of your game, including anything related to the player, the levels your game contains |  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. |  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 world description file consists of several elements. | ||||||
| 
 |  | ||||||
| A stage description file consists of several elements. |  | ||||||
| 
 | 
 | ||||||
| | Element         | Short description                                                                                         | | | Element         | Short description                                                                                         | | ||||||
| | --------------- | --------------------------------------------------------------------------------------------------------- | | | --------------- | --------------------------------------------------------------------------------------------------------- | | ||||||
|  | @ -159,7 +281,7 @@ levels: [ | ||||||
| ``` | ``` | ||||||
| </details> | </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.  |  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 | `player` describes a `Block` that represents the player of the game. Its `Entry`s are `hp` (a traditional value) and | ||||||
|  | @ -253,78 +375,3 @@ If we look at the example, all the objects are | ||||||
|                             Condition ('inventoryContains(key)') |                             Condition ('inventoryContains(key)') | ||||||
|                         Entry = empty ConditionList + Action ('leave()') |                         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 :: InputHandler Game | ||||||
| handleInputActionSelection = composeInputHandlers [ | handleInputActionSelection = composeInputHandlers [ | ||||||
|     handleKey (SpecialKey KeySpace) Down selectAction, |     handleKey (SpecialKey KeySpace) Down selectAction, | ||||||
|  |     handleKey (SpecialKey KeyEnter) Down selectAction, | ||||||
| 
 | 
 | ||||||
|     handleKey (SpecialKey KeyUp)    Down $ moveSelector North, |     handleKey (SpecialKey KeyUp)    Down $ moveSelector North, | ||||||
|     handleKey (SpecialKey KeyDown)  Down $ moveSelector South |     handleKey (SpecialKey KeyDown)  Down $ moveSelector South | ||||||
|  |  | ||||||
|  | @ -36,6 +36,7 @@ handleInputPlaying = composeInputHandlers [ | ||||||
| 
 | 
 | ||||||
|     -- Interaction with entities and items |     -- Interaction with entities and items | ||||||
|     handleKey (SpecialKey KeySpace) Down checkForInteraction, |     handleKey (SpecialKey KeySpace) Down checkForInteraction, | ||||||
|  |     handleKey (SpecialKey KeyEnter) Down checkForInteraction, | ||||||
|     handleKey (Char 'f')            Down checkForInteraction, |     handleKey (Char 'f')            Down checkForInteraction, | ||||||
| 
 | 
 | ||||||
|     handleKey (Char 'i')            Down $ toggleInventoryShown True, |     handleKey (Char 'i')            Down $ toggleInventoryShown True, | ||||||
|  |  | ||||||
|  | @ -42,8 +42,7 @@ focusPlayer _ = id | ||||||
| renderLevel :: Renderer Level | renderLevel :: Renderer Level | ||||||
| renderLevel Level{ layout = l, items = i, entities = e } = level | renderLevel Level{ layout = l, items = i, entities = e } = level | ||||||
|     where level    = pictures [void, layout, items, entities] |     where level    = pictures [void, layout, items, entities] | ||||||
|         --   void     = createVoid |           void     = createVoid | ||||||
|           void     = blank |  | ||||||
|           layout   = renderLayout l |           layout   = renderLayout l | ||||||
|           items    = renderItems i |           items    = renderItems i | ||||||
|           entities = renderEntities e |           entities = renderEntities e | ||||||
|  | @ -92,12 +91,12 @@ renderInventory :: Player -> Picture | ||||||
| renderInventory Player{ showInventory = False } = blank | renderInventory Player{ showInventory = False } = blank | ||||||
| renderInventory Player{ inventory = list } = pictures [overlay, title, items] | renderInventory Player{ inventory = list } = pictures [overlay, title, items] | ||||||
|     where title = translate 0 (offset (-1)) $ scale uizoom uizoom $ color white $ text "Inventory" |     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 |           move (i, pic) = translate 0 (offset i) pic | ||||||
|           offset i      = negate (zoom * resolution * fromIntegral i) |           offset i      = negate (zoom * resolution * fromIntegral i) | ||||||
| 
 | 
 | ||||||
| withHealthBar :: HP -> Picture -> Picture | withHealthBar :: HP -> Picture -> Picture | ||||||
| withHealthBar (Nothing) renderedEntity = renderedEntity | withHealthBar Nothing   renderedEntity = renderedEntity | ||||||
| withHealthBar (Just hp) renderedEntity = pictures [renderedEntity, positionedBar] | withHealthBar (Just hp) renderedEntity = pictures [renderedEntity, positionedBar] | ||||||
|     where positionedBar = scale smaller smaller $ translate left up renderedBar |     where positionedBar = scale smaller smaller $ translate left up renderedBar | ||||||
|           renderedBar   = pictures [heart, counter] |           renderedBar   = pictures [heart, counter] | ||||||
|  |  | ||||||