character-sheet
Version:
CharacterSheet library for assembling relationships between stats and modifiers.
450 lines (333 loc) • 10.5 kB
Markdown
# :game_die: Character-Sheet: Beta :game_die:
:crown: Quickly define the relationships between stats.
:crown: Manage inventories, talents, equipment, etc.
:crown: Create items, skills, buffs.
:crown: Save and export modified sheets.
:triangular_flag_on_post: This library is still in beta.
## Concepts
**Sheet**
- A sheet is used to define stats.
- Stats may have computable relationships between them.
- For example: **hitpoints** can be based off of **constitution**.
**Character**
- A character is one instance of a sheet.
- Characters may assign values to stats.
- For example: You may set **constitution** from _8_ to _10_, which will cause **hitpoints** to rise from _80_ to _100_.
**Stats**
- A stat is a single named property on a character.
- Computed stats must be numerical, but sheets may also store other types of information.
- For example: **player level**, **character name**, or **experience**.
**Modifiers**
- Modifiers allow you to define sets of modifications to stats on a character.
- They can be added or removed at any time.
- For example: **talents**, **equipment**, **spells**, **items**, and **buffs**.
**Inventory**
- Inventories hold modifiers that are applied to a character.
- Inventories may also serve as storage for modifiers that are set to inactive.
- Example inventories: **equipment**, **active effects**, **bags**, or **class features**.
## Design a Sheet
In the below snippet you will learn how to:
- Generate a sheet
- Define some stats
```js
// import the library
import CharacterSheet from 'character-sheet';
// generate a new sheet called "TableDungeons"
const TableDungeons = CharacterSheet();
// define constitution
TableDungeons.define('constitution').initially(10);
// hp is based on constitution
TableDungeons
.define('hp')
.initially(0)
.using('constitution')
.calculate((currentValue, constitution) {
return currentValue + constitution * 10;
});
```
## Create a Character
A continuation of the previous example, here we'll:
- Create a character
- Derive a stats' computed value
```js
// create a character called sirKnight
const sirKnight = new TableDungeons();
// check what sirKnight's hp is
sirKnight('hp').is(); // 125
```
## Save My Character
After a sheet has been designed, and a character created, it can be exported and stored.
```js
// export the sheet as an object
const exportedSheet = sirKnight.export(); // { ... constitution: 10, hp: 125 }
```
**For in depth examples see below.**
## *Define* API
- Used to define base stats on a Sheet
```js
// define constitution
TableDungeons.define('constitution').initially(10);
```
### Chainable Heirarchy
- `.define()`
- `.describe()`
- `.initially()`
- `.add()`
- `.subtract()`
- `.divideBy()`
- `.roundUp()`
- `.roundDown()`
- `.calculate()`
- `.using()`
- `.calculate()`
#### .define(*stat* `String`)
> Define a **stat** on a Sheet
```js
// import the library
import CharacterSheet from 'character-sheet';
// generate a new CharacterSheet Class
const TableDungeons = CharacterSheet();
// generate a new CharacterSheet Class
TableDungeons
// a stat named constitution
.define('player-name')
// initial value of 10
.initially("Anonymous Player");
// a new character sheet
const sirKnight = new TableDungeons();
// get player name
sirKnight('player-name').is(); // "Anonymous Player"
```
#### .initially(*value* `String | Number | Boolean`)
> Set the initial value of the *stat*. The type of value cannot change once set.
```js
// import the library
import CharacterSheet from 'character-sheet';
// generate a new Sheet
const TableDungeons = CharacterSheet();
// define a string
TableDungeons
.define('player-name')
.initially('');
// define a number
TableDungeons
.define('strength')
.initially(10);
// define a boolean
TableDungeons
.define('poisoned')
.initially(false);
```
#### .add | .subtract | .divideBy (*value* `String | Number`)
> Mathematical operation
- `String` will add the computed value of another stat.
- `Number` will add a static value.
```js
// import the library
import CharacterSheet from 'character-sheet';
// generate a new Sheet
const TableDungeons = CharacterSheet();
// create a new character
const sirKnight = new TableDungeons();
TableDungeons
// define a stat named constitution
.define('constitution')
// initial value of 10
.initially(10);
TableDungeons
// define a stat named hp
.define('hp')
// initially 100
.initially(100)
// + whatever constitution's calculated value is
.add('constitution')
// + 25
.add(25);
```
#### .roundUp(), .roundDown()
> Round to a whole number, with the respective rule to describe which way to round.
## *Inventory* API
> A storage mechanism that contains modifiers, defined in the CharacterSheet, and accessed on the character.
### Functional Heirarchy
- `.give()`
- `.of()`
- `.filter()`
- `.prune()`
- `.remove()`
- `.move()`
- `.to()`
- `.forEach()`
- `.on()`
- `.off()`
## *Modifier* API
> Modifiers represent templates of modifications, that can be added to inventories. They are not in effect until added to an inventory that is on.
### Features
- Create a modifier template which can be placed into any inventory
- Describe which stat to modify and augment it with sequential mathematical operations
- Switch default on or off
### Functional Heirarchy
- modifier
- modifies
- add
- subtract
- divideBy
- roundDown
- roundUp
- on
- off
## *Modifier* API
> Modifiers are placed exclusively on the character, and not on the CharacterSheet. They represent any mechanical adjustments to stats during computation.
### Features
- Switch on or off
- Move to another inventory
- Destroy
- Duplicate and place in desired inventory
### Functional Heirachy
- on
- off
- moveTo
- destroy
- duplicate
- to
## Example Character Sheets
### Example 1: Dungeons and Dragons - Stat Modifiers
Let's replicate the functionality of Dungeons and Dragons style stat modifiers. The rule is: whatever your stat is, your stat modifier is that number divided by 2, rounded down, subtract 5.
For example:
- Stat is **15**
- 15 / 2 = **7.5**
- Round Down 7.5 = **7**
- 7 - 5 = **2**
- Stat Modifier is **2**.
> Let's build our CharacterSheet Class, called `TableDungeons`.
```js
// import the library
import CharacterSheet from 'character-sheet';
// generate a new CharacterSheet Class
const TableDungeons = CharacterSheet();
// define whatever stats to attach to CharacterSheet Class
const stats = ['strength', 'dexterity', 'charisma'];
// loop over each stat - 'strength', 'dexterity', 'charisma'
stats.forEach(stat => {
// define the stat - ex: 'strength'
TableDungeons
.define(stat)
.initially(10);
// define that stat modifier - ex: 'strength-modifier'
TableDungeons
// define the stat
.define(`${stat}-modifier`)
// set its initial value
.initially(0)
// pull stat into the calculation context
.using(stat)
// caclulate the final value
.calculate(stat => (stat / 2) - 5);
});
```
> Let's create our `CharacterSheet Instance` of the `CharacterSheet Class` called `sirKnight`.
```js
// sirKnights previously exported sheet
const savedSheet = {
strength: 8,
dexterity: 10,
charisma: 18
};
// instantiate a character sheet for a player
const sirKnight = new TableDungeon(savedSheet);
// the modifiers are calculated and vailable for query
sirKnight('strength-modifier').is(); // -1
sirKnight('dexterity-modifier').is(); // 0
sirKnight('charisma-modifier').is(); // 4
```
### Example 2: Talents, Equipment, and Inventory
In most table top games, there are ways to augment your character as you go. These kinds of things can include **inventory**, **spells**, **equipment**, **talents**, and so on.
In this example we'll work with **passive talents**, active **equipment**, and an **inventory** which holds items not currently equipped.
> Let's build our CharacterSheet Class, called `TableDungeons`.
```js
// import the library
import CharacterSheet from 'character-sheet';
// generate a new CharacterSheet Class
const TableDungeons = CharacterSheet();
```
> We'll add 3 inventories, with an `initially()` of `[]` to indiciate that these will hold modifiers.
```js
// holds items that are not actively in use - we'll turn this one off()
TableDungeons.define('inventory').initially([]).off();
// holds passive talents, on() by default
TableDungeons.define('passive-talents').initially([]);
// holds items that are actively in use, on() by default
TableDungeons.define('equipment').initially([]);
// health
TableDungeons.define('hp').initially(100);
// mana
TableDungeons.define('mana').initially(250);
```
> We'll instantiate our good sir knight
```js
// instantiate a character sheet for a player
const sirKnight = new TableDungeon();
```
> Let's create some modifiers that `sirKnight` will be able to consume
```js
// create the Wand of Power
sirKnight
// let's give it a descriptive name
.modifier('1h/wand-of-power')
// and modify mana
.modifies('mana')
// by adding 100 to it
.add(100);
// no change - we have to give sirKnight
// a wand in order for him to gain its bonus
sirKnight('mana').is(); // 250
```
> We'll equip the Wand of Power
```js
// select the equipment inventory
sirKnight('equipment')
// sirKnight will gain 1
.give(1)
// Wand of Power
.of('1h/wand-of-power');
// the bonus is now being applied
sirKnight('mana').is(); // 350
```
> Let's take the wand off for now
```js
// select the equipment inventory
sirKnight('equipment')
// find the 1h/wand-of-power
.findOne('1h/wand-of-power')
// and move to the inventory
.giveTo('inventory');
// because inventory is .off()
// the Wand of Power no longer
// affects mana.
sirKnight('mana').is(); // 250
```
> The same thing can be done with talents.
```js
// create the talent: Sturdy and Stout
sirKnight
// give it a descriptive namespace
.modifier('talent/sturdy-and-stout')
// modifies hp
.modifies('hp')
// adds 50 to hp
.add(50)
// also modifies mana
.modifies('mana')
// subtracts 50 from mana
.subtract(50);
// select sigKnight's equipment
sirKnight('passive-talent')
// give 1
.give(1)
// Study and Stout
.of('talent/sturdy-and-stout');
// it behaves just like the equipment
// inventory. talents and items are really
// the same thing. :)~
sirKnight('hp').is(); // 150
sirKnight('mana').is(); // 200
```