What would you like to learn next?

Game-State Formulas

Chapter 4 by Friedman Friedman

Game-State Formulas

Set by formula lets a chapter calculate a Number, Percent, or Age when a reader enters it. A formula can combine current Game State values, arithmetic, conditions, rounding, and random results. Enter the formula in Changes to game state on the chapter form, not in the chapter's text. The calculation needs an active game and does not place a visible control in the chapter.

Build your first formula

Suppose a shop should subtract an item's discounted price from the reader's money. First create three Number game variables as described in Game State Variables and Inventory, with these initial values:

  • money = 100
  • item_price = 30
  • discount = 5

Then edit the shop chapter:

  1. Turn on Changes to game state.
  2. Choose the money variable.
  3. Choose Set by formula.
  4. Enter game:money - (game:item_price - game:discount).
  5. Save the chapter and test it in a fresh game.

The formula calculates 100 - (30 - 5), so the reader enters the chapter with money set to 75. A formula may read the same value it changes, as this one does, but that value must already exist in the current game.

Values a formula can use

Write game:name to read a game variable. The name and capitalization must exactly match Story variables.

  • Number, Percent, and Age values can be used as numbers.
  • Yes/No values can be used with true, false, comparisons, AND, OR, NOT, and IF.
  • Text, Dropdown, and reader variables cannot be used in a formula.
  • The completed formula must return a number, even when it uses a Yes/No check.
  • Hidden Game State values remain available to formulas; a hidden value is different from a missing or removed value.

Every referenced name must be a known Game State variable when the chapter is saved. During play, every reference that the formula actually calculates must exist in the reader's current game. Define important inputs with starting values so a fresh game has them. A variable added to the story after a reader began playing can still be missing from that existing game until the reader resets it or a chapter creates the value. If an earlier chapter can remove a value, recreate it before the formula. Another option is to use a separate, existing flag with IF, AND, or OR so the branch containing the possibly missing reference is skipped.

Useful formula patterns

These examples use common game-story tasks. Replace their variable names with the exact names from your story:

Task Formula What it does
Keep health in range CLAMP(game:health + game:healing; 0; 100) Adds healing, but never returns less than 0 or more than 100
Give a conditional bonus IF(game:has_bonus = true; game:score + 10; game:score) Adds 10 only when has_bonus is Yes
Divide safely IF(game:item_count = 0; 0; ROUND(game:total / game:item_count)) Returns 0 for an empty group and otherwise returns a rounded average
Make a random skill check IF(DICE(20) + game:bargain_skill >= 20; 5; 0) Returns 5 when the die result plus skill reaches 20, otherwise 0

IF calculates only the selected result. In the safe-division example, the division is not attempted when item_count is 0. AND and OR also stop once the result is known, so a skipped right side is not calculated.

Changes run from top to bottom

A formula sees the results of change rows above it. Suppose one row sets bonus to 5 and the next formula adds game:bonus to score; that formula sees 5. Use Move Up and Move Down to put dependent changes in the required order.

After each row, CHYOA keeps the result within the target type's range: Number -100000 through 100000, Percent 0 through 100, and Age 18 through 150. The next row sees that adjusted value.

CHYOA checks formula syntax, variable names, and compatible types when the chapter is saved. Some problems depend on the reader's current game, such as a missing value, division by zero, a fractional final result, or calculated random bounds in the wrong order. If any formula cannot be completed when the reader enters the chapter, the reader sees a Game State calculation error, none of that chapter's change rows are kept, and the previous Game State remains intact. If the failed calculation is on the first chapter, CHYOA leaves Game Mode for that attempt so the reader is not trapped there.

Arithmetic and checks

Syntax Meaning
+, -, * Add, subtract, and multiply
/ Divide exactly; round the result when it may be fractional
// Divide and round down to a whole number
% Find the remainder using the same round-down division
^ Raise the left value to a whole-number power
( ) Group a calculation and make its order clear
=, ==, !=, <>, <, <=, >, >= Compare two numbers, or compare two Yes/No values with equality
AND, OR, NOT Combine or reverse true-or-false checks

The usual arithmetic order applies: powers, multiplication and division, then addition and subtraction. Powers group from right to left, so 2 ^ 3 ^ 2 means 2 ^ (3 ^ 2). Use parentheses whenever the intended order might not be obvious.

Write formula logic with the words AND, OR, and NOT. The &&, ||, and ! aliases accepted by some chapter conditions are not formula syntax.

Decimal values may use a point or comma, so 1.5 and 1,5 mean the same exact value. Function arguments are always separated with semicolons, not commas.

Formula functions

Function names are not case-sensitive.

Function Result
IF(check; when_true; when_false) Uses one result when the check passes and the other when it does not; only the selected result is calculated
MIN(value; ...) The smallest of one or more numeric values
MAX(value; ...) The largest of one or more numeric values
ABS(value) The value without a negative sign
ROUND(value) The nearest whole number; halfway values round away from zero
ROUND(value; places) The value rounded to the requested decimal place, from -12 through 12
FLOOR(value) The next whole number at or below the value
CEIL(value) The next whole number at or above the value; CEILING also works
CLAMP(value; minimum; maximum) The value kept between the supplied minimum and maximum
DICE(sides) A whole number from 1 through sides, including both ends
RANDOM(minimum; maximum) A whole number between the two bounds, including both ends

Whole-number results

The completed formula must produce a whole number. game:total / 4 works only when that division has no remainder. When a fraction is possible, choose the behavior the story needs:

  • ROUND(game:total / 4) uses the nearest whole number.
  • FLOOR(game:total / 4) rounds down.
  • CEIL(game:total / 4) rounds up.

Rounding is needed only before the completed result is saved. A formula may use exact fractions in the middle of a longer calculation.

Random results inside formulas

Use a random function when the result also depends on Game State or needs custom bounds:

  • DICE(20) chooses a whole number from 1 through 20, including both ends.
  • RANDOM(-2; 2) chooses -2, -1, 0, 1, or 2.
  • game:score + DICE(6) adds a six-sided result to the current score.
  • RANDOM(game:minimum; game:maximum) uses current numeric game values as inclusive bounds.

Each call makes a separate choice. DICE(6) + DICE(6) therefore produces 2 through 12, with results near 7 occurring more often.

DICE needs a whole-number side count from 1 through 1000000. RANDOM needs whole-number bounds from -1000000 through 1000000, and its minimum cannot be greater than its maximum.

If no calculation is needed and a fixed upper bound is enough, use Random Values in Chapter Changes. For a reader-controlled roll button, use Dice Rolls. For a themed choice from written outcomes, use Random Draws.

Fix common formula problems

Problem What to check
Unknown variable while saving Match the name and capitalization shown on Story variables
Missing value while playing Test an existing game as well as a fresh one; reset the game or create the value on an earlier route
Formula returns a fraction Use ROUND, FLOOR, or CEIL for the completed result
Division by zero Use IF to choose a result that does not perform the division when the divisor is 0
Function arguments are rejected Separate them with semicolons, such as MIN(2; 5), rather than commas
A range comparison is rejected Write two complete checks, such as game:score > 0 AND game:score < 10; do not write 0 < game:score < 10
CLAMP is rejected while playing Put the minimum before the maximum
A value has the wrong type Use only numeric game values for calculations and Yes/No game values for true-or-false checks

Formula limits

Most story calculations stay well below these limits:

Limit Maximum
Formula length 4096 bytes; ordinary formula characters use one byte each
Formula pieces, including values, names, operators, separators, and parentheses 512
Nested expressions 32 levels
Arguments in one function call 32
Calculated steps during one use 1024
Digits after a decimal separator 12
Power -32 through 32
Exact whole or fraction component during a calculation 1000000000000000 in either direction
DICE sides 1 through 1000000
RANDOM bounds -1000000 through 1000000

What the reader experiences

The change happens before the chapter is shown. The reader sees its effect in chapter text, Game State, inventory, conditions, and follow-up choices, but does not see the formula itself.

Going Back past the chapter restores the earlier Game State. Entering the chapter again runs the formula again and makes new choices for every DICE or RANDOM call.

Before publishing

Test the chapter with both a fresh game and an existing game. Check that:

  1. every referenced value exists on each route that can reach the chapter;
  2. change rows are in the required order;
  3. division, rounding, minimums, and maximums behave as intended;
  4. when the formula uses DICE or RANDOM, its smallest and largest results fit the story;
  5. Percent and Age results stay meaningful at their supported limits;
  6. going Back and entering the chapter again gives the expected result.

Older game-variable names

New variable names use letters, numbers, and underscores, so game:name is the normal formula form. An older story may have a game-variable name with spaces or punctuation. Put that exact name in double quotes inside brackets:

game["Money on hand"] - game:price

Inside the quoted name, write \" for a double quote and \\ for a backslash. Braces and control characters are not allowed. Keep a working older name unchanged unless the story is being carefully revised.

Start your own immersive adult AI roleplay story
Ad

You've reached the end of this Guide topic.

  • No further chapters
Back Start Over View Story Map

0 comments