#11 Write report
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]
|
||||
|
|