This document describes the inner workings of the TAGF code: all the different components and how they interact.
A TAGF game is typically loaded from a YAML
definition file, and
likewise a game in progress can be saved to a YAML
file.
Once a game has been built in memory, either by hand or by loading it
from a definition file, it can be exported back to a YAML
file.
A TAGF definition file is a hash (or dictionary). Most of the top-level keys are the lowerecased names of element classes, either singular or plural. For example, here is an empty definition file:
%YAML 1.2
---
game: {}
player: {}
factions: []
features: []
items: []
keywords: []
locations: []
paths: []
npcs: []
The game
and player
are singular because there's only one of each
for any particular game.
The pluralised keys are for arrays of hashes, one per component.
Singular forms are rare, because there can be only one entry in the
hash with that key, so only one keyword
(for instance) could be
defined by itself. However, additional keywords could be defined in
the same file by putting them into the keywords
array, sinc
keyword
and keywords
are different keys in the hash.
I hope that was more clear that it seems on re-reading.
Loading, expoerting, and saving of TAGF games is performed by methods
in the TAGF::Filer
class. Also in that class is
the Loadables
, which defines
the meaningful keys in the
Exceptions defined by the TAGF package are detailed and instrumented. Some are used internally to signal conditions (such as inventory overflow), and others are used more conventionally to end the application with an explanatory message. Most of the latter apply to logic or dependency issues, and will only be seen by game developers.
Every TAGF exception is assigned a unique numeric identifier,
accessible via the #exception_id
attribute. This attribute is
available for both the exception class and instances of it.
Every TAGF exception also has a numeric severity value, accessible
through #severity
. By default, when an exception is instantiated,
the instance's severity is inherited from the class' #severity
value. Unlike the #exception_id
attribute, an exception instance's
severity can be changed, either at construction by passing a
severity:
keyword argument to the constructor, or after creation by
using the instance's #severity=
method.
Every TAGF exception instance also has an #errorcode
attribute,
which is a combination of its exception ID value and its severity.
The errorcode is built by left-shifting the exception ID four bits and
ORing it with the severity.
Every TAGF exception class (and instance thereof) is assigned a severity value (see the overview section on Exceptions. Exception class severities are immutable, but that of exception instances can be changed.
Exception values which are odd numbers (1, 3) indicate either total success or something of note that isn't a problem. Even values indicate some sort of problem.
Success : Numerical value: 1
The application has successfully performed a task. In some cases, processing continues after the message is issued, but this usually is an end-of-run thing.
N.B.: Should be converted to 0 if it's going to be interpreted in situations expecting Unixish shell/libc conventions.
Informational, Info : Numerical value: 3
The application has performed a task, or is providing a status update on one in progress. The message provides information about the process. Generally suppressed, and only used to bring to notice something of interest.
Warning, Warn : Numerical value: 4
The application may have performed some, but not all, of a task. The message may suggest that you verify the result or check for other messages.
Error : Numerical value: 6
The output or result of a task is known to be incorrect, but it may be localised and the application may attempt to continue execution.
Severe, Fatal : Numerical value: 8
The application cannot continue. A severe exception indicates an unrecoverable condition, such as the inability to access a file, or perhaps a logic error has gotten the player stuck. Should always result in an abnormal termination.
Components of a TAGF game, such as rooms, NPCs, items, the player
itself, and so on, are all called game elements. A
Location
is an element. A lantern is an
element. Everything is an element. All elements include the
TAGF::Mixin::Element
mixin. All elements are
described later in their own section(s).
TAGF::Mixin::Element
is only one of the mixins.
Features that might apply to disparate elements are separated out and
mixed in selectively. For instance, anything that provides light
mixes in the TAGF::Mixin::LightSource
module; anything that might have contents includes the
TAGF::Mixin::Container
module, and anything
which can be opened or closed (or locked) mixes in
TAGF::Mixin::Sealable
. Mixins are described in
their own section.
Some tools are provided to help game developers, and more will doubtless be added as the need arises. These are described in the Tools section.
A few tools are part of the TAGF package, and each is accessed from
the command line via the tagf
command (supplied in the package as
bin/tagf
). These include:
render
: Allows rendering of the game layout as a visual map in a graphic file. For example,
% tagf render --format=png --source=advent.yaml
will produce a file named advent.png
showing all the locations and
the paths between them
validate
: Evaluates a game definition (YAML
) file and identifies potential
mapping errors or playability issues (such as unreachable rooms,
rooms that can be entered but not exited, locked items (or doors)
with no defined keys, &c.).
% tagf validate --source=advent.yaml
- Mixes in Element
- Mixes in Element
- May mix in Consumable
- May mix in Container
- Mixes in Element
- Mixes in Portable
- May mix in Sealable
- May mix in Weapon
- Mixes in Element
An NPC is a 'non-player character,' and the term refers to any sort of 'character' wholly under the control of the game.
The Player
element (of which there should be exactly ONE in a TAGF
game) is the virtual avatar of the person playing the game — the
User, as opposed to the Player. The Player is controlled
primarily by commands entered by the User, but sometimes may be
controlled by game dynamics (such as running away randomly if
panicked).
- Mixes in Container
The UniversalMethods
mixin module declares game-wide constancts,
methods and attributes that should be available everywhere (such as
game settings, or the
raise_exception
method).
When it is mixed in with include
, it also extends itself into the
including components' eigenclass (unless it is unnamed, which usually
means its a singleton class). So if it is included in class X
, all
of its definitions are available to instances of X
and also to
X
's class context and methods.
attitude
[rw] (Symbol)
: TBS
breadcrumbs
[rw] (Array)
: TBS
faction
[rw] (Faction)
: TBS
hp
[rw] (Float)
: TBS (See the section on Hitpoints)
maxhp
[rw] (Integer)
: TBS (See the section on Hitpoints)
allow_containers
[rw] (Boolean)
: TBS
capacity_items
[rw] (Integer)
: TBS (See the section on Item, Weight, and Volume Limits)
capacity_mass
[rw] (Float)
: TBS (See the section on Item, Weight, and Volume Limits)
capacity_volume
[rw] (Float)
: TBS (See the section on Item, Weight, and Volume Limits)
current_items
[rw] (Integer)
: TBS (See the section on Item, Weight, and Volume Limits)
current_mass
[rw] (Float)
: TBS (See the section on Item, Weight, and Volume Limits)
current_volume
[rw] (Float)
: TBS (See the section on Item, Weight, and Volume Limits)
is_open
[rw] (Boolean)
: TBS
(See is_openable
,
is_surface
)
is_openable
[rw] (Boolean)
: TBS
(See is_open
,
is_surface
)
is_surface
[rw] (Boolean)
: TBS
(See is_open
,
is_openable
)
is_transparent
[rw] (Boolean)
: TBS
light_level
[rw] (Float)
(See the section on Lighting)
: TBS
paths
[rw] (Hash)
: TBS
event_queue
[r] (Array)
: TBS
events_heard
[rw] (Set)
: TBS
article
[rw] (String)
: TBS
desc
[rw] (String)
: TBS
(See shortdesc
)
eid
[ro] (String)
: The game-wide unique element identifier for the object.
game
[rw] (Game)
: The game object that ultimately owns all the other objects. Each object has this attribute, which is how they can find each other.
illumination
[rw] (Integer)
: TBS (See the section on Lighting)
is_static
[rw] (Boolean)
: TBS
is_visible
[rw] (Boolean)
: TBS
mass
[rw] (Float)
: TBS
name
[rw] (String)
: TBS
owned_by
[rw] (Element)
: TBS
only_dim_near_player
[rw] (Boolean)
: TBS (See the section on Lighting)
pct_dim_per_turn
[rw] (Float)
: TBS (See the section on Lighting)
preposition
[rw] (String)
: TBS
shortdesc
[rw] (String)
: TBS
(See desc
)
volume
[rw] (Float)
: TBS
The game_options
method.
EnableHitpoints
EnforceCapacities
EnforceLighting
EnforceItemCounts
EnforceMass
EnforceVolume
RaiseOnInvalidValues
is_container?
: Returns true
if the object has mixed in the Mixin::Container
module, and can therefore have an inventory and
'own' other objects.
Description goes here.
Description goes here.
Description goes here.
Description goes here. See the Inventories section for details.
Description goes here.
Description goes here.
Description goes here.
Description goes here.
Description goes here.
Description goes here.
Description goes here.
Description goes here.
- Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
- Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
- Fork the project.
- Start a feature/bugfix branch.
- Commit and push until you are happy with your contribution. Don't forget to keep up to date with the master branch.
- Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
- Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
TAGF
is copyright (c) 2022 by Ken Coar, and is made available
under the terms of the Apache Licence 2.0. See the
LICENCE file for further details.