Added basic parser functionality & tests for these functionalites. Split tests in several files
This commit is contained in:
parent
4c1f25e49d
commit
83659e69b4
9 changed files with 504 additions and 12 deletions
249
README.md
249
README.md
|
@ -1,3 +1,250 @@
|
|||
# RPG-Engine
|
||||
|
||||
Schrijf een game-engine voor een rollenspel
|
||||
Schrijf een game-engine voor een rollenspel
|
||||
|
||||
https://pixel-poem.itch.io/dungeon-assetpuck
|
||||
https://kyrise.itch.io/kyrises-free-16x16-rpg-icon-pack
|
||||
|
||||
# RPG Engine requirements
|
||||
|
||||
## 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
|
||||
|
||||
- [ ] 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 enimies, open doors, ...).
|
||||
- [ ] Player can go to the next level.
|
||||
|
||||
## Not-functional requirements
|
||||
|
||||
- [ ] Use Parsing.
|
||||
- [ ] Use at least one (1) monad transformer.
|
||||
- [ ] Write good and plenty of documentation.:w
|
||||
|
||||
- [ ] Write tests (for example, using HSpec).
|
||||
|
||||
---
|
||||
|
||||
# Plaats om dingen neer te jotten
|
||||
|
||||
```
|
||||
Play <--- HandleInput
|
||||
|
|
||||
|
|
||||
v
|
||||
Level <--- LoadLevel <--- Parse
|
||||
|
|
||||
|
|
||||
v
|
||||
RenderLevel
|
||||
```
|
||||
|
||||
- [ ] State paradigma gebruiken om van startscherm naar playscherm naar pause scherm naar endscherm te gaan
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
# RPG-Engine Documentation
|
||||
|
||||
## Playing the game
|
||||
|
||||
TODO
|
||||
|
||||
- Input commands etc
|
||||
- An example playthrough
|
||||
|
||||
## Writing your own stages
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
| Element | Short description |
|
||||
| --------------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| `Block` | optionally surrounded by `{ ... }`, consists of several `Entry`'s, optionally separated by commas `,` |
|
||||
| `Entry` | is a `Key` - `Value` pair, optionally separated by a colon `:` |
|
||||
| `Key` | is a unique, predefined `String` describing `Value` |
|
||||
| `Value` | is either a `Block` or a `BlockList` or a traditional value, such as `String` or `Int` |
|
||||
| `BlockList` | is a number of `Block`'s, surrounded by `[ ... ]`, separated by commas, can be empty |
|
||||
|
||||
We'll look at the following example to explain these concepts.
|
||||
|
||||
```javascript
|
||||
player: {
|
||||
hp: 50,
|
||||
inventory: [
|
||||
{
|
||||
id: "dagger",
|
||||
x: 0,
|
||||
y: 0,
|
||||
name: "Dagger",
|
||||
description: "Basic dagger you found somewhere",
|
||||
useTimes: infinite,
|
||||
value: 10,
|
||||
|
||||
actions: {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
levels: [
|
||||
{
|
||||
layout: {
|
||||
| * * * * * *
|
||||
| * s . . e *
|
||||
| * * * * * *
|
||||
},
|
||||
items: [],
|
||||
entities: []
|
||||
},
|
||||
{
|
||||
layout: {
|
||||
| * * * * * * * *
|
||||
| * s . . . . e *
|
||||
| * * * * * * * *
|
||||
},
|
||||
items: [
|
||||
{
|
||||
id: "key",
|
||||
x: 3,
|
||||
y: 1,
|
||||
name: "Doorkey",
|
||||
description: "Unlocks 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 secret door can only be opened with a key",
|
||||
direction: left,
|
||||
actions: {
|
||||
[inventoryContains(key)] useItem(key),
|
||||
[] leave()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
This stage description file consists of a single `Block`. A stage 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
|
||||
`inventory` (a `BlockList` of several other `Block`s). They are both separated by commas this time. It is possible for
|
||||
the inventory to be an empty list `[]`.
|
||||
|
||||
`levels` is a `BlockList` that contains all the information to construct your game.
|
||||
|
||||
### `layout` syntax
|
||||
|
||||
If `Key` has the value `layout`, `Value` is none of the types discussed so far. Instead `Layout` is specifically made
|
||||
to describe the layout of a level. This object is surrounded by `{ ... }` and consists of multiple lines, starting with
|
||||
a vertical line `|` and several characters of the following:
|
||||
|
||||
- `x` is an empty tile a.k.a. void.
|
||||
- `.` is a tile walkable by the player.
|
||||
- `*` is a tile not walkable by the player.
|
||||
- `s` is the starting position of the player.
|
||||
- `e` is the exit.
|
||||
|
||||
All characters are interspersed with spaces.
|
||||
|
||||
### `actions` syntax
|
||||
|
||||
If `Key` has the value `actions`, the following changes are important for its `Value`, which in this case is a `Block`
|
||||
with zero or more `Entry`s like so:
|
||||
|
||||
- `Key` has type `ConditionList`.
|
||||
|
||||
A `ConditionList` consists of several `Condition`s, surrounded by `[ ... ]`, separated by commas. A `ConditionList`
|
||||
can be empty. If so, the conditional is always fulfilled.
|
||||
|
||||
A `Condition` is one of the following:
|
||||
|
||||
- `inventoryFull()`: the players inventory is full.
|
||||
- `inventoryContains(objectId)`: the players inventory contains an object with id `objectId`.
|
||||
- `not(condition)`: logical negation of `condition`.
|
||||
|
||||
- `Value` is an `Action`.
|
||||
|
||||
An `Action` is one of the following:
|
||||
|
||||
- `leave()`
|
||||
- `retrieveItem(objectId)`
|
||||
- `useItem(objectId)`
|
||||
- `decreaseHp(entityId, objectId)`
|
||||
- `increasePlayerHp(objectId)`
|
||||
|
||||
### Back to the example
|
||||
|
||||
If we look at the example, all the objects are
|
||||
|
||||
```
|
||||
>Block<
|
||||
Entry = Key ('player') + >Block<
|
||||
Entry = Key ('hp') + Value (50)
|
||||
Entry = Key ('inventory') + >BlockList<
|
||||
length = 1
|
||||
Block
|
||||
Entry = Key ('id') + Value ("dagger")
|
||||
... <several traditional entries like this>
|
||||
Entry = Key ('actions') + empty Block
|
||||
Entry = Key ('levels') + >BlockList<
|
||||
length = 2
|
||||
>Block<
|
||||
Entry = Key ('layout') + Layout
|
||||
<multiple lines that describe the layout>
|
||||
Entry = Key ('items') + empty BlockList
|
||||
Entry = Key ('entities') + empty BlockList
|
||||
>Block<
|
||||
Entry = Key ('layout') + Layout
|
||||
<multiple lines that describe the layout>
|
||||
Entry = Key ('items') + >BlockList<
|
||||
length = 1
|
||||
>Block<
|
||||
Entry = Key ('id') + Value ("key")
|
||||
... <several traditional entries like this>
|
||||
Entry = Key ('actions') + >Block<
|
||||
Entry = >ConditionList< + Action ('retrieveItem(key)')
|
||||
length = 1
|
||||
Condition ('not(inventoryFull())'))
|
||||
Entry = empty ConditionList + Action ('leave()')
|
||||
Entry = Key ('entities') + >BlockList<
|
||||
length = 1
|
||||
>Block<
|
||||
Entry = Key ('id') + Value ("door")
|
||||
... <several traditional entries like this>
|
||||
Entry = Key ('actions') + >Block<
|
||||
Entry = >ConditionList< + Action ('useItem(key)')
|
||||
length = 1
|
||||
Condition ('inventoryContains(key)')
|
||||
Entry = empty ConditionList + Action ('leave()')
|
||||
```
|
Reference in a new issue