Skip to content

Commit 0568857

Browse files
committed
change phases syntax
This is in preparation for an upcoming feature to quickly switch to a temporary phase (and back after it is done). OLD: phases: [ { name: 'A', ... } { name: 'B', ... } ] NEW: phases: { A: { ... }, B: { ... }, } This allows creation of different phase orderings that aren't simple lists. Each phase can now point to a `next` phase: phases: { A: { next: 'B' }, } A 'default' phase is the catch-all next phase for phases that don't specify `next`. In order to emulate the old behavior, you can do something like: phases: { A: { next: 'B' }, B: { next: 'C' }, C: { next: 'A' }, }
1 parent bffc19e commit 0568857

20 files changed

Lines changed: 449 additions & 348 deletions

docs/CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,37 @@
1+
#### Breaking Changes
2+
3+
1. The syntax for phases has changed:
4+
5+
```
6+
// old
7+
phases: [
8+
{ name: 'A', ...opts },
9+
{ name: 'B', ...opts },
10+
]
11+
12+
// new
13+
phases: {
14+
'A': { ...opts },
15+
'B': { ...opts },
16+
}
17+
```
18+
19+
2. There is no implicit ordering of phases. You can specify an
20+
explicit order via `next`:
21+
22+
```
23+
// new
24+
phases: {
25+
'A': { next: 'B' },
26+
'B': { next: 'A' },
27+
}
28+
```
29+
30+
3. A phase called `default` is always created. This is the phase
31+
that the game begins with. This is also the phase that the
32+
game reverts to in case it detects an infinite loop of
33+
`endPhase` events caused by a cycle.
34+
135
## v0.26.3
236

337
#### Features

docs/api/Game.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ game state and the moves. The moves are converted to a
3333
of moves have been made.
3434
* `flow.undoableMoves` (_array_): Enables undo and redo of listed moves.
3535
Leave `undefined` if all moves should be undoable.
36-
* `flow.phases` (_array_): Optional list of game phases. See
36+
* `flow.phases` (_object_): Optional spec of game phases. See
3737
[Phases](/phases) for more information.
3838

3939
### Returns
@@ -110,9 +110,8 @@ const game = Game({
110110
},
111111

112112
flow: {
113-
phases: [
114-
{
115-
name: 'A',
113+
phases: {
114+
A: {
116115
endGameIf: ...
117116
endTurnIf: ...
118117
onTurnBegin: ...
@@ -122,8 +121,7 @@ const game = Game({
122121
allowedMoves: ...
123122
...
124123
},
125-
{
126-
name: 'B',
124+
B: {
127125
endGameIf: ...
128126
endTurnIf: ...
129127
onTurnBegin: ...
@@ -133,7 +131,7 @@ const game = Game({
133131
allowedMoves: ...
134132
...
135133
},
136-
]
134+
}
137135
}
138136
});
139137
```

docs/phases.md

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,14 @@ We'll ignore the rendering component of this game, but this is how it might look
4242

4343
Now let's say we want the game to work in two phases:
4444

45-
* the first phase where the player can only draw cards (until the deck is empty).
45+
* a first phase where the player can only draw cards (until the deck is empty).
4646
* a second phase where the player can only play cards (until their hand is empty).
4747

48-
In order to do this, we add a `flow` section to the `Game`
49-
constructor (you should already be familiar with this as the location
48+
In order to do this, we add a `flow` section to the game
49+
spec (you should already be familiar with this as the location
5050
where you placed `endGameIf` in the
51-
[tutorial](#/tutorial?id=add-victory-condition)).
52-
53-
It can also contain a `phases` array, which defines different
54-
phases in the game. Each phase can specify a list of `allowedMoves`,
51+
[tutorial](#/tutorial?id=add-victory-condition)). It can also contain a `phases` object, which defines different phases in the game. Each phase can specify a list of `allowedMoves`,
5552
which allows only those moves to be played during that phase.
56-
They can also contain a `endPhaseIf` function that terminates
57-
the phase automatically if a particular condition is met.
5853

5954
```js
6055
const game = Game({
@@ -66,24 +61,58 @@ const game = Game({
6661
},
6762

6863
flow: {
69-
phases: [
70-
{
71-
name: 'draw phase',
64+
startingPhase: 'draw',
65+
66+
phases: {
67+
draw: {
7268
allowedMoves: ['drawCard'],
7369
endPhaseIf: G => G.deck <= 0,
70+
next: 'play',
7471
},
75-
{
76-
name: 'play phase',
72+
73+
play: {
7774
allowedMoves: ['playCard'],
7875
endPhaseIf: G => G.hand <= 0,
76+
next: 'draw',
7977
},
80-
],
78+
},
8179
},
8280
});
8381
```
8482

85-
!> Phases can also be terminated manually by calling `props.events.endPhase()` from the
86-
board React component (in response to a user action like clicking a button, for example).
83+
!> Every game also has an implicit `default` phase, which is just
84+
a phase with no specific options, so the example above actually
85+
has three phases, even though we never use the `default` phase.
86+
87+
### Terminating a phase
88+
89+
A phase ends when one of the following happens:
90+
91+
###### 1. `endPhaseIf` triggers:
92+
93+
This is a simple boolean function that terminates the phase when
94+
it returns `true` (see the example above).
95+
96+
###### 2. The `endPhase` event is dispatched:
97+
98+
This can happen either in the game logic or from the client
99+
directly. See the [Events API](events.md) for more details
100+
on how to dispatch events.
101+
102+
###### What happens when a phase terminates?
103+
104+
The game moves on to the "next" phase. This phase is determined by the
105+
following in increasing order of precedence (i.e. if [2] and [4] are both
106+
relevant, the result of [4] is used):
107+
108+
1. The `default` phase is chosen as the next phase if no other option is present.
109+
110+
2. If a phase specifies the `next` option (like our example above does), then that is
111+
chosen as the next phase.
112+
113+
3. `endPhaseIf` can return the name of the next phase.
114+
115+
4. The `endPhase` event accepts the name of the next phase as an argument.
87116

88117
Watch our game in action now with phases. Notice that you can only draw cards in the first
89118
phase, and you can only play cards in the second phase.
@@ -101,13 +130,12 @@ end of a phase. These are specified just like normal moves in `onPhaseBegin` and
101130
`onPhaseEnd`.
102131

103132
```js
104-
phases: [
105-
{
106-
name: '...',
133+
phases: {
134+
phaseA: {
107135
onPhaseBegin: (G, ctx) => G,
108136
onPhaseEnd: (G, ctx) => G,
109137
},
110-
];
138+
};
111139
```
112140

113141
#### Triggers / Hooks
@@ -116,7 +144,7 @@ The `flow` section can specify a number of automatic behaviors when a move is ma
116144
or when the turn or phase is ended. These can also be overridden at the phase level.
117145
Let's take a look at some of these:
118146

119-
!> For a more complete set of options, take a look
147+
!> For an authoritative set of options, take a look
120148
[here](https://github.com/nicolodavis/boardgame.io/blob/master/src/core/flow.js#L139).
121149

122150
```js
@@ -136,10 +164,8 @@ flow: {
136164
// Run at the end of a move.
137165
onMove: (G, ctx) => G
138166

139-
phases: [
140-
{
141-
name: 'A',
142-
167+
phases: {
168+
A: {
143169
// Ends the phase if this returns a truthy value.
144170
endPhaseIf: (G, ctx) => {}
145171

@@ -157,12 +183,12 @@ flow: {
157183
onTurnEnd: ...
158184
onMove: ...
159185
}
160-
]
186+
}
161187
}
162188
```
163189

164190
!> An important point to note is that in a multiplayer game, all of the code under
165-
`flow` is executed only on the server. The code under `moves`, in contrast, is
191+
`flow` is executed only on the master. The code under `moves`, on the other hand, is
166192
run on both client and server. It is run on the client in order to effect a
167193
quick state transition without network lag, and run on the server to process
168194
the authoritative version of the state.

docs/react/boardgameio.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/react/phases-2.html

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,30 +41,32 @@
4141
},
4242

4343
flow: {
44-
phases: [
45-
{
46-
name: 'draw phase',
44+
startingPhase: 'draw',
45+
46+
phases: {
47+
draw: {
4748
endPhaseIf: G => (G.deck <= 0),
4849
allowedMoves: ['drawCard'],
50+
next: 'play',
4951
},
50-
{
51-
name: 'play phase',
52+
play: {
5253
allowedMoves: ['playCard'],
5354
endPhaseIf: G => (G.hand <= 0),
55+
next: 'draw',
5456
}
55-
],
57+
},
5658
}
5759
});
5860

5961
class Board extends React.Component {
6062
drawCard = () => {
61-
if (this.props.ctx.phase != 'draw phase') return;
63+
if (this.props.ctx.phase != 'draw') return;
6264
this.props.moves.drawCard();
6365
this.props.events.endTurn();
6466
}
6567

6668
playCard = () => {
67-
if (this.props.ctx.phase != 'play phase') return;
69+
if (this.props.ctx.phase != 'play') return;
6870
this.props.moves.playCard();
6971
this.props.events.endTurn();
7072
}

docs/turn-order.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,9 @@ Game({
106106
},
107107

108108
flow: {
109-
phases: [
110-
{
111-
name: 'A',
112-
turnOrder: TurnOrder.ANY,
113-
},
114-
{
115-
name: 'B',
116-
turnOrder: TurnOrder.ONCE,
117-
},
109+
phases: {
110+
A: { turnOrder: TurnOrder.ANY },
111+
B: { turnOrder: TurnOrder.ONCE },
118112
],
119113
}
120114
}

examples/react/phases/diagram.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,12 @@ const game = Game({
1616
moves: {},
1717

1818
flow: {
19-
phases: [
20-
{
21-
name: 'A',
22-
},
23-
{
24-
name: 'B',
25-
},
26-
{
27-
name: 'C',
28-
},
29-
],
19+
startingPhase: 'A',
20+
phases: {
21+
A: { next: 'B' },
22+
B: { next: 'C' },
23+
C: { next: 'A' },
24+
},
3025
},
3126
});
3227

examples/react/phases/phases.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,19 @@ const game = Game({
2121
},
2222

2323
flow: {
24-
phases: [
25-
{
26-
name: 'take phase',
24+
startingPhase: 'take',
25+
phases: {
26+
take: {
2727
endPhaseIf: G => G.deck <= 0,
2828
allowedMoves: ['takeCard'],
29+
next: 'play',
2930
},
30-
{
31-
name: 'play phase',
31+
play: {
3232
allowedMoves: ['playCard'],
3333
endPhaseIf: G => G.hand <= 0,
34+
next: 'take',
3435
},
35-
],
36+
},
3637
},
3738
});
3839

@@ -45,13 +46,13 @@ class Board extends React.Component {
4546
};
4647

4748
takeCard = () => {
48-
if (this.props.ctx.phase != 'take phase') return;
49+
if (this.props.ctx.phase != 'take') return;
4950
this.props.moves.takeCard();
5051
this.props.events.endTurn();
5152
};
5253

5354
playCard = () => {
54-
if (this.props.ctx.phase != 'play phase') return;
55+
if (this.props.ctx.phase != 'play') return;
5556
this.props.moves.playCard();
5657
this.props.events.endTurn();
5758
};

0 commit comments

Comments
 (0)