chiark / gitweb /
1eddf0e04e9e8a7ea474679f9d377eeef75ccd64
[otter.git] / docs / gamespec.rst
1 Game specs
2 ==========
3
4 A game spec defines the starting layout for a game: **which pieces start
5 on the table, and where**.
6
7 It also defines some ancillary information about the game and its
8 layout.  It does not contain information about the players.
9 Players join a game as permitted by the game access control list,
10 which is specified in a *table specification* (sadly not currently
11 properly documented, but look at the Rustdoc for ``TableSpec``).
12
13 General
14 -------
15
16 A game spec is a TOML file.  It has the following entries at the top
17 level:
18
19  * ``table_size``: Size of the table playing area, in Otter internal
20    units.  [array of 2 numbers, default ``[300,200]``]
21
22  * ``table_colour``: Table backdrop colour.  Only certain colour
23    syntaxes are supported.   [string, colour; default ``green``]
24
25  * ``pcaliases``: [dictionary, values are Piece Spec dicts].  Piece
26    alias definitions.  This is used by the ``"Alias"`` piece spec.
27
28  * ``pieces``: Array of `Piece Specs`_.  Defines the initial pieces
29    and their layout.  Each entry is a piece spec dictionary.
30
31  * ``format`` [integer]: Specifies which version of the Otter data
32    formats the spec file was written to.
33    This document describes ``format=1``.
34    See :ref:`bundle-compatibility`.
35
36
37 Templating
38 ``````````
39
40 Before being parsed as TOML, the spec can be processed as a one-off
41 Tera template.  This is useful because game setups can be rather
42 repetitive.
43
44 Template expansion is done if the file starts, precisely, with the
45 characters ``{#`` (which are the Tera comment introducer).
46
47 Tera does not natively support within-same-file macros.  To allow use
48 of macros in Otter's single-file game specs, Otter can automatically
49 split a game spec file into two pieces and feed them to Tera as two
50 files.
51
52 Splitting is done if the file contains a line starting with precisely
53 ``{% endmacro`` (the usual form of the Tera macro end sequence).
54
55 Everything up to and including the `last` such line is fed to Tera as
56 if it were a file called ``m``.  Everything else is fed to Tera as if
57 it were a second file called ``spec``.  Additionally, a line ``{%
58 import "m" as m %}`` is added to the top of ``spec``, so that the
59 macros are automatically imported and can be called with ``{[
60 m::MACRO...}}``.  And enough blank lines are added to make the input
61 line numbers match up with the lines in the notional ``spec`` file.
62
63 Note that line numbers reported during TOML parsing
64 refer to lines in the template-expanded output.
65 To help diagnose your templates, ``otter -vv reset ...`` will expand
66 the templates itself and print out the results of the file splitting,
67 and then of the expansion, before sending the file to the server.
68
69 For details of the template syntax, see `the Tera documentation`_.
70 For examples, see the built-in game specs.
71
72 .. _the Tera documentation: https://tera.netlify.app/docs/#templates
73
74 Piece Specs
75 -----------
76
77 A piece spec is a dictionary defining one or more pieces.  When part
78 of a game spec, it appears as an entry in the top-level ``pieces``
79 array, and also defines the location(s) on the table to place the
80 pieces, too.
81
82 There is a required entry ``type``, a string.  This determines how the
83 rest of the table is interpreted.  It is one of the `Piece Spec
84 Types`_.
85
86 Universal parameters
87 ````````````````````
88
89 These apply regardless of the value of ``type``.
90
91  * ``type``: Piece type or piece spec type.  One of the types listed
92    in `Piece Spec Types`_.  [string, enum, required]
93
94  * ``pos``: Position, in game coordinates, of
95    the centre of the piece.  The origin is at the top left.
96    [2-element array, default ``[20,20]``]
97
98  * ``count``: Place multiple identical copies of this piece.  [number]
99
100  * ``posd``: Position delta.  When a spec specifies multiple pieces,
101    each successive piece will be shifted by this amount.  [2-element
102    array, default ``[5,5]``]
103
104  * ``face``: Initial face to show (ie, "which way up" the piece
105    starts).  The default face is ``0``.  For most pieces that is the
106    front, and is usually a good choice.  [number]
107
108  * ``pinned``: Whether the piece is pinned to the table.  Players can
109    pin and unpin pieces during the game; this is the initial state.
110    [boolean]
111
112  * ``angle``: Initial orientation of the piece.  The
113    specified value is multiplied by 45 degrees, increasing values
114    rotating anticlockwise.  So for example ``6`` would mean to rotate
115    90 degrees clockwise.  [integer 0..7]
116
117
118 Common parameters
119 `````````````````
120
121 Depending on the ``type``, some of these parameters will be honoured.
122 This is discussed in the descriptions for each piece spec type.
123
124  * ``colour``: The fill colour.  For a piece type which supports only
125    one face.  [string, colour]
126
127  * ``faces``: The main fill colour(s).  [array of string(s), colours]
128
129  * ``edge``: The edge colour to draw.  For a piece with supports only
130    one face.  Default is not to draw edges.  [string, colour]
131
132  * ``edges``: The colour of edges to draw.  Default is not to draw
133    edges.  Must either be a 1-element array, or as long as ``faces``
134    (specifying a different edge colour for each face).  [array of
135    string(s), colours]
136
137  * ``edge_width`` [number, default 0.2 if `edge` or `edges` is
138    specified]
139
140  * ``label``.  Controls display of the label with information about
141    the in-game state.  Dictionary with two sub-entries:
142
143     - ``colour`` [string, defaults to the edge colour].
144     - ``place`` [string]: One of ``"BottomLeft"`` (default),
145       ``"TopLeft"``, ``"BottomLeftOutside"``, ``"TopLeftOutside"``.
146
147  * ``shape``.  The shape of a piece.  Dictionary with two sub-entries:
148
149     - ``type``.  ``"Circle"`` or ``"Rect"`` [required]
150     - ``size`` [array of 1 or 2 numbers]: required if ``type="Rect"``.
151     - ``diam`` [number]: required if ``type="Circle"``.
152
153  * ``itemname``: Used when other parts of the game want to refer to
154    this one.  [string]
155
156
157 Piece Spec Types
158 ----------------
159
160 ``"Lib"``
161 `````````
162
163 A single shape from a piece library.
164
165  * ``lib``: The library name.  [string, required]
166  
167  * ``item``: The item name within that library.  [string, required]
168
169 Example::
170
171   [[pieces]]
172   pos = [150,100]
173   type = "Lib"
174   lib = "edited"
175   item = "chess-board"
176   pinned = true
177
178
179 ``"LibList"``
180 `````````````
181
182 Multiple shapes from a piece library.  Cannot be used with the `count`
183 universal parameter.
184
185  * ``lib``: The library name. [string, required]
186
187  * ``items``: The item names. [array of strings, required]
188
189  * ``prefix``, ``suffix``: Prepended and appended to each
190    entry in ``items``.  Useful for abbreviating.  [strings]
191
192 Example::
193
194   [[pieces]]
195   pos = [150, 84]
196   type = "LibList"
197   lib = "cards-oxymoron"
198   prefix = "card-oxymoron-"
199   suffix = "-s"
200   items = [
201       "2","3","4","5","6","7","8","9","T","J","Q","K","A",
202       "2","3","4","5","6","7","8","9","T","J","Q","K","A",
203       "2","3","4","5","6","7","8","9","T","J","Q","K","A",
204   ]
205   posd = [0, 0]
206
207
208 ``"ChessClock"``
209 ````````````````
210
211 A chess clock.  Additional parameters:
212
213  * ``time``: Initial time for each player. [number, in seconds;
214    required]
215
216  * ``per_move``: Time to add per move.  [number, in seconds]
217
218 (These clock settings cannot be reconfigured via the game UI.)
219
220 Example::
221
222   [[pieces]]
223   pos = [240, 100]
224   type = "ChessClock"
225   time = 900
226   per_move = 30
227
228
229 ``"PickupDeck"``
230 ````````````````
231
232 A pickup or play deck.  This can occult the pieces (eg, cards) you put
233 on it, shuffling them and hiding their identity.
234
235 Requires ``face`` and ``shape``.  Only ``shape.type="Rect"`` is supported.
236
237 Honours ``edges``, ``edge_width``.
238
239 Honours ``label``, displaying the number of of pieces in (on) this deck.
240
241 Example::
242   
243   [[pieces]]
244   pos = [136,115]
245   type = "PickupDeck"
246   faces = ["lightblue", "grey"]
247   edges = ["black", "white"]
248   label.colour = "black"
249   label.place = "BottomLeftOutside"
250   shape.type = "Rect"
251   shape.xy = [25,30]
252
253
254 ``"Hand"``
255 ``````````
256
257 A player hand.  When active, arranges for only that player to be able
258 tos see the contents.  The other players see the occulted view (eg,
259 the backs of cards).
260
261 Requires ``colour`` and ``shape``.  Only ``shape.type="Rect"`` is
262 supported.
263
264 Honours ``edge``, ``edge_width``.
265
266 Honours ``label``, displaying the player whose hand this is, when
267 active.
268
269 Example::
270
271   [[pieces]]
272   pos = [53, 25]
273   colour = "brown"
274   label.place = "BottomLeftOutside"
275   label.colour = "black"
276
277   type = "Hand"
278   edge = "white"
279   edge_width = 0.75
280   shape.type = "Rect"
281   shape.xy = [93,25]
282
283
284 ``"PlayerLabel"``
285 `````````````````
286
287 A simple label which can display a player name.
288
289 Requires ``colour`` and ``shape``.  Only ``shape.type="Rect"`` is supported.
290
291 Honours ``edge``, ``edge_width``.
292
293 Honours ``label``.
294
295
296 ``"Die"``
297 `````````
298
299 A die (or coin), which can choose randomly
300 from a fixed set of aspects.
301 Can be "rolled" to have the server show a randomly-chosen face.
302
303 You must either specify an ``image`` with multiple faces,
304 and/or ``labels``,
305 so that the faces can be distinguished.
306 If the ``image`` has multiple faces *and* you specify ``labels``,
307 the number of faces implied by each must be the same.
308
309 The die will display a circular "cooldown timer",
310 after it has been rolled.
311 This makes rolling the die visually noticeable for all the players.
312 After the die has been rolled,
313 it cannot be flipped to a different face, or re-rolled,
314 until the timer expires.
315 Apart from that, you can see all the faces in sequence,
316 or make the die show a particular face,
317 with the standard flip operation ("f").
318
319 Dice can (possibly) be occulted.
320 An occultable die will, if placed in a player's active hand,
321 obscure its face (but, generally, not existence, nor cooldown time),
322 from other players.
323 Dice aren't "shuffled" with other piece, the way (say) cards are.
324 A die is occultable if its image is occultable,
325 or if ``occult`` is explicitly specified.
326
327
328 Parameters:
329
330  * ``desc``: Descriptive string,
331    used in log messages reporting player actions.
332    The actual description shown to users will also report
333    the description provided by the image,
334    for the particular face showing,
335    and the number of faces.
336    [string; optional]
337
338  * ``image``: Specifies what this die should look like.
339    [inner piece spec, as dictionary; required].
340
341  * ``labels``: Text strings to superimpose on the image.
342
343     - [list of strings] One string per face.
344     - [list of two numbers] Label faces numerically (inclusive).
345     - [single number] Label faces numerically from 1 to n (inclusive).
346
347  * ``label.colour`` [string, defaults to black]:
348    Colour to write the ``labels`` text strings.
349
350  * ``label.size`` [number, default 8]:
351    Font size for the ``labels`` text strings (in pixeels, svg ``px``).
352
353  * ``cooldown``: Duration of the cooldown time.
354    [duration - number(s) with units; default "4s"]
355
356  * ``circle_scale``: Adjusts the size of the cooldown timer circle.
357    The default is an estimate of the best size,
358    calculated from the image's bounding box.
359    [floating point number; default is 1.0, representing the estimated size]
360
361  * ``occult``: If supplied,
362    specifies that the die should be occultable.
363    In this case either the specified ``image``
364    must itself be occultable,
365    or it must have only one face
366    (since otherwise we wouldn't know what to display when occulted).
367    [dictionary; optional;
368    presence of even an empty dictionary is meaningful;
369    default if absent is to occult if the specified image is occultable]
370
371  * ``occult.label``: The text string to display when the die is occulted.
372    [string; if not specified in ``occult``,
373    defaults to ``"?"`` if any nonempty ``labels`` were specified,
374    or the empty string otherwise.]
375    
376 The common parameter ``itemname`` is also supported.
377
378 Example::
379   
380   [[pieces]]
381   pos = [155, 15]
382   type = "Die"
383   labels = ["A", "B"]
384   image.type = "Disc"
385   image.diam = 12
386   circle_scale = 0.833
387   image.edges = ["black","black"]
388   image.faces = ["#ccccff", "#ccffcc"]
389
390
391 ``"Currency"``
392 ```````````````
393
394 Quantified resources of any kind.
395 This could be money, or "resources", or perhaps VPs.
396 In what follows we call each piece representing some currency a "banknote",
397 but it might represent some resource cubes, or whatever.
398
399 Each banknote has an integer quantity.
400 Currency is fungible: it can be split and merged.
401 Dropping a banknote onto another banknote of the same currency will
402 merge the two.
403 The quantity selection function in the game UI can be used to take
404 a subset of the value out of a banknote,
405 splitting the amount requested off the note,
406 and leaving the change behind.
407
408 So individual banknotes can be created or destroyed in play.
409 Each note can represent either resources being moved
410 from one place to another,
411 or a repository such as a bank or a player's stash.
412 (There is no enforced distinction between banknotes
413 in motion or lying about randomly on the table,
414 and a "bank", or players' money.
415 Players are expected to use location on the table to indicate
416 whose money is what, as they would with physical money.)
417
418 Each currency is identified by a string.
419 Different banknotes with the same currency can be merged,
420 even if they look totally different.
421 To avoid confusion,
422 it is a good idea for all banknotes of the same
423 currency in the same game to have the same image.
424
425 The total amount in the game of each currency
426 remains constant during play.
427 (More can be introduced by using ``otter`` to add more banknotes.)
428
429 Currency is not currently occultable:
430 banknotes are always publicly visible.
431
432 Parameters:
433
434  * ``image``: Specifies what each banknote should look like.
435    The image must have only one face.
436    [inner piece spec, as dictionary; required].
437
438  * ``qty``: The initial amount of this banknote or stash.
439    [nonnegative integer; required]
440
441  * ``currency``: The currency, which defines which other
442    banknotes this one can interchange with.
443
444  * ``label.colour``: Text colour to use for the value.
445    [string, default "black"]
446
447  * ``label.size`` [number, default 6]:
448    Font size for the value (in pixeels, svg ``px``).
449
450  * ``label.unit_rel_size`` [number, default 1]:
451    Relative font size for the unit part of the value.
452    (Proportion of the quantity.)
453
454 Exammple::
455
456   [[pieces]]
457   pos = [125, 45]
458   type = "Currency"
459   qty = 400
460   min_unit = 5
461   currency = "ƒ"
462   image.type = "Rect"
463   image.size = [20,7]
464   image.edges = ["#00ff00"]
465   image.faces = ["#008800"]
466
467
468 ``"Rect"``
469 ``````````
470
471 A plain rectangular piece.
472
473  * ``size``: Size and shape  [array of 1 or 2 numbers, required]
474
475 Requires ``faces``.
476
477 Honours ``itemname``, ``edges`` and ``edge_width``.
478
479 Exammple::
480
481   [[pieces]]
482   pos = [20, 85]
483   type = "Rect"
484   faces = ["yellow","#f4f"]
485   posd = [10, 0]
486   size = [7,7]
487   count = 8
488
489
490 ``"Disc"``
491 ``````````
492
493 A plain circular piece.
494
495  * ``diam`` [number, required].
496
497 Requires ``faces``.
498
499 Honours ``itemname``, ``edges`` and ``edge_width``.
500
501
502 ``"Alias"``
503 ```````````
504
505 An alias (generally defined in ``pcaliases`` in the game spec).
506
507 This allows a piece spec (which can be found in a shape library) to
508 refer to something which depends on the game spec.
509
510  * ``target``: Alias name.
511
512 Example, in ``GAME.game.toml``::
513
514   [pcaliases.card-back]
515   type = "Lib"
516   lib = "wikimedia"
517   item = "card-plain-back-maroon"
518
519 And in ``library/LIB.toml``::
520
521   [group.clubs]
522   item_prefix = "card-oxymoron-"
523   outline = "Rect"
524   size = [73, 97]
525   centre = [36.5, 48.5]
526   scale = 0.25
527
528   item_suffix = "-c"
529   sort = "card-playing-c_s"
530   desc_template = "the _desc of clubs"
531
532   occulted.method = "ByBack"
533   occulted.ilk = "card-back"
534
535   files = """
536   :             sort
537   2     -       02      two
538   3     -       03      three
539   4     -       04      four
540   5     -       05      five
541   6     -       06      six
542   7     -       07      seven
543   8     -       08      eight
544   9     -       09      nine
545   T     -       10      ten
546   J     -       11      jack
547   Q     -       12      queen
548   K     -       13      king
549   A     -       14      ace
550   """
551
552   [group.clubs.back]
553   type = "Alias"
554   target = "card-back"