Skip to content

Commit

Permalink
more game docs, ui playground
Browse files Browse the repository at this point in the history
  • Loading branch information
aghull committed Jan 20, 2024
1 parent a0a6350 commit b63db4f
Show file tree
Hide file tree
Showing 20 changed files with 2,384 additions and 214 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# Generated files
.docusaurus
.cache-loader
/docs
docs/api

# Misc
.DS_Store
Expand Down
2 changes: 1 addition & 1 deletion docs/game/_category_.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"label": "Game logic",
"label": "Building a game",
"position": 2,
"link": {
"type": "generated-index",
Expand Down
461 changes: 446 additions & 15 deletions docs/game/actions.md

Large diffs are not rendered by default.

243 changes: 243 additions & 0 deletions docs/game/board.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
---
sidebar_position: 3
---
# Board Structure

The board is a singleton class that is declared as the first step of creating a
game. Installing Boardzilla automatically creates this class and you can add
properties and methods to it as you progress. Your board extends the base
[Board](../api/classes/board) class.

Boards contains [Spaces](../api/classes/space) (fixed regions) and
[Pieces](../api/classes/piece) (movable game objects). This is essentially a
data tree, with Board as the root branch and the spaces and pieces within in as
branches. For example, a Board may have spaces for each player's tableau, and
inside those are spaces for the player's hand, and inside those are their
cards. All of these spaces and pieces are called the
[Elements](../api/classes/GameElement) of the game. Elements in Boardzilla
always have a parent-child relationship within the board.

```mermaid
graph TD;
Board["`**Board**`"]-->mat1["`**Space**
Player 1 Tableau
`"];
Board-->mat2["`**Space**
Player 2 Tableau
`"];
mat1-->coins["`**Space**
Player 1 Coins
`"];
mat1-->hand1["`**Space**
Player 1 Hand
`"];
hand1-->card1["`**Piece**
Ace of hearts
`"];
hand1-->card2["`**Piece**
Two of clubs
`"];
```

Spaces can contain other spaces and pieces. Pieces can even contain other
pieces, e.g. as when placing tokens onto a card.

## Subclassing

Typically, a game will declare a few classes of game pieces, e.g. Cards, Tokens
and the like. Each of these will be a subclass of Piece. These subclasses can
add properties and methods that you can use in the rules of game. E.g. a `Card`
class, that might have `suit` and `number` properties, and special methods like
`isTrump()`.

```ts title="Example Card class"
export class Card extends Piece {
suit: 'S' | 'H' | 'D' | 'C';
number: number;

isTrump() {
return this.suit === this.game.trump;
}
}
```

Spaces can be subclassed as well. This is less common, but helpful if you have
several spaces of a particular type that have special properties or behaviour.

Defining subclasses like this also makes it easy to customize their appearance
later and give the different classes of Pieces entirely different visuals.

## Querying

Accessing parts of the board is done using the Query API on the board and the
spaces and pieces you add. The two most important methods are
[`all`](../api/classes/GameElement#all) and
[`first`](../api/classes/GameElement#first).
- `all` Search the tree recursively and return *all* matches
- `first` Search the tree recursively and return only the *first* match

In the example tree above, calling `board.all(Piece)` would return the two cards
at the bottom of the tree. If we used the Card class above, we could also have
used `board.all(Card)` to return the same thing but typed correctly to the Card
class. We can then also search by name, e.g. `board.first(Card, '2C')` to return
the Card named '2C', or add properties to the search, e.g. `board.first(Card, {
number: 1 })` to return the first ace in the game.

Any methods that return lists of elements, like `all`, actually return an
[ElementCollection](../api/classes/ElementCollection). This is an Array-like
class that can be treated like an array but also contains many other methods.

:::warning first can return undefined
Note that `first` can return `undefined` if matching element is found. When using
`first`, you will frequently add `!` or `?` depending on the situation, which is
a good reminder to not assume that a piece is always where you expect, e.g.

```ts
// flip the top Card of the deck, if there are any
$.deck.first(Card)?.showToAll();
```
:::

For convenience, all uniquely named spaces are also accessible from a global `$`
object that contains all spaces by name, e.g. `$.deck`.

There are many more methods and options for finding particular game
elements. See the [API documentation](../api/classes/GameElement#queries) for
more.

## Creation

Spaces and pieces are created using the
[create](../api/classes/GameElement#create) method. All Game Elements have a
class and a name. The Class can be one of the based classes or one of the
subclasses you've declared. The name can be any string. It is used for searches,
determining uniqueness, and also appears in the HTML for CSS targetting. e.g.:

```ts
const tableau = board.create(Space, 'tableau');
const hand = tableau.create(Space, 'hand');
hand.create(Card, '2C');
```

You can also specify the properties during their creation with a 3rd argument:

```ts
hand.create(Card, '2C', { suit: 'C', number: 2 });
hand.create(Card, 'JS', { suit: 'S', number: 1 1});
```

## Ownership
All Game Elements also have an optional `player` property built-in. Setting this
property assigns the element to a [Player](../api/classes/Player). This is
useful for pieces and spaces that permanently belong to them, like their player
mat, or their unique player token. These elements can be created and queried
using the `player` property.

```ts
// create 2 tableaus for each player
board.create(Space, 'tableau', { player: game.players[0] });
board.create(Space, 'tableau', { player: game.players[1] });

// get player 1's tableau
board.first(Space, 'tableau', { player: game.players[0] });
```

Any elements that are contained within an element assigned to a player are also
considered to be "owned" by that player, e.g. a card in their hand. These
elements can be queried using the `owner` property.

```ts
// get player 1's cards
board.all(Card { player: game.players[0] });
```

:::warning player vs owner
Rememeber the difference between `player` and `owner`. They are related but distinct.
- `player` is a property you set that assigns a game element to that player, and is usually permanent. Think of it like a name tag, or the color of the element.
- `owner` is a read-only property that indicates where the piece currently resides. A Card might be owned by a player while they hold it, but the card "doesn't have their name on it" so to speak, and may change hands.
:::

The Player object also conveniently has methods for retrieving these elements:
[my](../api/classes/Player#my) and [allMy](../api/classes/Player#allmy) for
retrieving one or many elements respectively

```ts
// get player 1's tableau
game.players[0].my('tableau');

// get player 1's cards
game.players[0].allMy(Card);
```

## Visibility
All elements are visible to all players by default. Often a game will require
that pieces are visible only to some players and hidden from others. In
Boardzilla, "hiding" a Game Element means that the properties of that element
are no longer visible. For example if one of our example `Card` instances was
flipped over, the player would be able to see that it was an instance of the
`Card` class, but `card.suit` and `card.number` would be `undefined`.

This can be accomplished in a number of ways, the simplest being
[`hideFromAll`](../api/classes/GameElement#hidefromall). There are many [other
methods](../api/classes/GameElement#visibility) for managing visibilty. It may
also be that some properties should be visible even when the element is hidden,
e.g. a Card may belong to different decks, and this is represented by
`Card#deck`. If the card back art indicates what deck it belongs to, then this
property should be revealed even if the card is hidden. You can set these
properties with the static method
[`revealWhenHidden`](../api/classes/GameElement#revealwhenhidden), e.g.:

```ts
Card.revealWhenHidden('deck');
```

## Movement
Pieces can be created in a particular place on the board, but will move around
as players take their actions. There are several ways to do this but the
simplest is [`putInto`](../api/classes/Piece#putinto).

```ts
// discard a card
card.putInto($.discard);

// draw the top card of the deck into the field
$.deck.first(Card).putInto($.field);
```

When cards move from space to space, you may want to change their
properties. These can be done automatically by adding event handlers to spaces. The most common type is to have spaces that change the visibility of their elements. E.g. when a card enters the deck, it should automatically be turned face down. When it enters a player's hand, it becomes visibile only to that player. This can be done with [`onEnter`](../api/classes/Space#onenter):

```ts
// the deck's cards are always face down
$.deck.onEnter(Card, card => card.hideFromAll();

// the player's hand always reveals their cards to `player`
const hand = board.create(Space, 'hand', { player });
hand.onEnter(Card, card => card.showTo(player));
```
There is also a corresponding [`onExit`](../api/classes/Space#onexit) handler.
## The pile and removing pieces
There is a special invisible region of the board called the "pile" available at
[`board.pile`](../api/classes/Board#pile). This is the holding area for any
pieces that are not in use. The pile is never rendered, but is always available
to the API for querying. Pieces are never created or destroyed once the game has
started, and instead are simply moved to or retrieved from the pile.
Remove a piece (move it to the pile) simply by calling it's
[`remove`](../api/classes/GameElement#remove) method, or for a list of items,
the [ElementCollection#remove](../api/classes/ElementCollection#remove) can be
used. For example, to remove all cards from the deck that are lower than 5, we
can say:
```ts
$.deck.all(Card, card => card.number < 5).remove();
```
to put all the unused cards from the pile into the deck, we would say:
```ts
board.pile.all(Card).putInto($.deck);
```
68 changes: 68 additions & 0 deletions docs/game/core-concepts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
sidebar_position: 1
---
# Core concepts

These are some of the basic core concepts of Boardzilla that will be refered to
throughout the documentation.

## Board
The Board is the class that defines the overall layout of the game. It is
composed of **Spaces** and **Pieces**. It itself is a Space but with special
behaviour.

### Space
Spaces are regions of the Board. They are **stable** and never change once the
game starts. They can be nested within each other.

### Piece
Pieces are **movable** objects in the game. They can freely move around to spaces,
and can also be placed inside other Pieces, such as when tokens are placed onto
a card.

Learn more in [Board Structure](./board).

## Player
The player is a core class that represents a user playing the game. Each game
will have a player class. Players can have pieces and spaces assigned to them,
and any piece that enters these becomes "owned" by the player.

Learn more in [Players](./player).

## Action
An Action is a discrete unit of choice for a player. An action has a **name**,
any possible **selections** for the action, and **behaviour**. In chess an
action might be called "move". It has two choices: the piece being moved, and
space it's moved to. It's behaviour would be to move the piece to its
destination and a message to the game log.

Learn more in [Actions](./actions).

## Flow
The Flow of your game is how the game runs from beginning to end. This describes
the phases, rounds and turns of the game, and what actions are avaiable to
players at which point in the Flow.

Learn more in [Flow](./flow).

## UI
Boardzilla renders and animated the board on the player's browser according to
the rules of your game. By default everything appears in a very raw but usable
format. You can gradually customize how your game appears.

### Layout
The layout of the game is the definition of where the Spaces and Pieces appear
in the player's browser, and how they change as Pieces are added into an area
and start to fill it up.

### Appearance
The appearance of each visible space and piece can be customized using JSX that
you provide and you can apply your own CSS to these. You can also supply special
effects that will be applied when things happen to these elements.

### Controls
Controls are what we call any floating boxes above the game board. These include:
- Prompts
- Button choices
- Inputs for text or numbers
- Confirmation text and buttons
34 changes: 0 additions & 34 deletions docs/game/create-a-blog-post.md

This file was deleted.

8 changes: 0 additions & 8 deletions docs/game/create-a-document.md

This file was deleted.

Loading

0 comments on commit b63db4f

Please sign in to comment.