-
Notifications
You must be signed in to change notification settings - Fork 17
Stager API v7
- status: complete
- version: 7.x
The stager defines the game sequence using blocks, stages, and steps.
0: "clear" 4: "addStep" 5: "createStep" 6: "addStage" 7: "createStage" 8: "cloneStep" 9: "cloneStage"
Assuming you have access to a Stager object named stager (e.g., in file game.stages.js), you can build the game sequence by adding stages, steps and blocks to the stager sequentially. Important: the order in which you add them
matters! For example:
stager.stage('stage 1');
stager.step('step_1');
stager.step('step_2');create a sequence of one stage with id stage 1 that contains two steps
with id step_1 and step_2.
When the game runs, it executes step_1 first, and step_2 second. In this example stage 1 is just a container for the two steps.
It is also possible to repeat a stage a certain number of times. For example:
stager.repeatStage('stage 1', 3);
stager.step('step_1');
stager.step('step_2');will repeat stage 1 3 times, leading to the following game sequence:
-
step_1,step_2,step_1,step_2,step_1,step_2.
To add more stages just use the stage or repeatStage multiple
times. For example:
stager.stage('stage 1');
stager.repeatStage('stage 2', 3);
stager.step('step2_1');
stager.step('step2_2');
stager.stage('stage 3');leads to the following game sequence:
-
stage 1,step2_1,step2_2,step2_1,step2_2,step2_1,step2_2,stage 3.
In the last example, no step was explicitly added to stage 1 and stage 3. In fact, a default step named with the same name of the containing stage is added automatically.
When you want to repeat stage, but you do not know in advance how many times,
you can specify a loop or a doLoop:
// Evaluates condition **before** entering the stage,
// the whole stage can be skipped if the condition is false.
stager.loopStage('stage 2', function() {
// Returns true for executing one more iteration of the loop.
// In this example, the game has a custom variable totEarnings,
// and we want to terminate the stage if it has reached 1000.
return node.game.totEarnings < 1000;
});
// doLoop executes the stage at least once, then it behaves like loop.
stager.doLoopStage('stage 2', function() {
// Returns true for executing one more iteration of the loop.
// In this example, the game has a custom variable totEarnings,
// and we want to terminate the stage if it has reached 1000.
return node.game.totEarnings < 1000;
});The loop callback is executed with node.game as context. In the above example
the variable LOOP_ENDED must be created and set it to true in the logic file to end the loop. For example:
stager.extendStep('stage 2', {
cb: function() {
// Example of pseudo-code condition.
if (TOTAL_PLAYER_PAYOFF > 100) this.LOOP_ENDED = true;
}
});The following commands are aliases, and can be used interchangeably:
-
.stage->.next -
.repeatStage->.repeat -
.loopStage->.loop -
.doLoopStage->.doLoop
The stager commands return the stager itself, so it is possible to chain methods together for a more compact script. The following snippets are equivalent:
stager.next('stage1').next('stage2').repeat('stage3', 2);
// is equivalent to:
stager.next('stage1');
stager.next('stage2');
stager.repeat('stage3', 2);
// is equivalent to:
stager.stage('stage1').stage('stage2').repeatStage('stage3', 2)
// is equivalent to:
stager.stage('stage1');
stager.stage('stage2');
stager.repeatStage('stage3', 2);To better organize your code you may use the methods require and share.
const SCALE = [
[ 'No', 'Not a problem<br/>at all' ],
[ 'Small', 'A small<br/>problem' ],
[ 'Problem', 'A problem' ],
[ 'Serious', 'A serious problem' ],
[ 'Very Serious', 'A very serious problem' ]
];
const MIN = 10;
const MAX = 100;
stager.share({ SCALE, MIN, MAX });
// Split questions in two files.
stager.require('includes/initial_questions.js');
stager.require('includes/additional_questions.js');Each required file receives an object containing all shared variables, some defined by the user and a few defaults.
// File includes/initial_questions.js
module.exports = function(shared) {
// Deconstructed shared object includes a reference to the stager itself
// and to JSUS, in addition to custom variables.
let { stager, J, SCALE, MIN, MAX } = shared;
// ...
}Optionally, a game over command can be set at the end of the sequence. For example:
stager.stage('stage 1')
stager.repeatStage('stage 2', 3);
stager.step('step2_1');
stager.step('step2_2');
stager.stage('stage 3');
stager.gameover();After the last stage is done, the game state is set to GAME_OVER, and it is not possible to step back and forth through the game, unless it is re-initialized.
Game over triggers the onGameOver callback, if one has been
defined by the user (see Section "Init and GameOver").
stager.onGameOver(function() {
// Possible actions:
// - Store results
// - Move clients to another room
// - Destroy room
});
It is possible to specify the stage and its steps in one command. For example:
stager.stage({
id: 'mystage',
steps: [ 'step1', 'step2', 'step3' ]
});adds a new stage with id mystage containing steps step1, step2,
step3 into the sequence. The stage id must be unique. Any step that
is not found in the stager is automatically created with a default
callback.
The same stage can be played again later in the sequence if you define an "alias." In the following snippet, we are going to play stage "game" twice, using the 'AS' keyword to define the "game2" alias.
stager.next('game');
stager.next('something_in_betwen');
// Now add an alias.
stager.next('game AS game2');Important! All steps of the aliased stage need to be added before the alias, see this issue.
-
extendStage(id, update)
Extends the stage with given id.
-
id{string} The id of the stage to extend. -
update{object|function} The object or function update.
-
-
extendStep(id, update)
Extends the step with given id.
-
id{string} The id of the step to extend. -
update{object|function} The object or function update.
-
The update object simply add new properties to existing stage/step object. If a property with the same name already exists it will overwritten.
The update function allows the creation of more complex inheritance patterns, retaining the existing properties of the extended object. For example:
Note! The id of a stage/step cannot be updated, else an error is thrown.
// Create a stage with one default step called 'instructions'.
stager.stage('instructions');
// Extend step with an update object.
stager.extendStep('instructions', {
cb: function() {
console.log('Instructions displayed');
},
timer: 2000
});// Update stage
// Extend step with an update function.
stager.extendStep('instructions', function(o) {
// Copy the original callback under a new property named _cb.
o._cb = o.cb;
o.cb: function() {
// Call existing parent callback function.
this.getCurrentStepObj()._cb();
// Do something else.
console.log('I am even more interesting!');
},
o.timer: 10000; // More time.
// Return the updated object.
return o;
});-
extendAllStages(update):
Extends all stages in the sequence.
-
update{object|function} The object or function update.
-
-
extendAllSteps(update):
Extends all steps in the sequence.
-
update{object|function} The object or function update.
-
-
extendStages(stages, update):
Extends selected stages.
-
stages{array} Array with ids of the stages to extend. -
update{object|function} The object or function update.
-
-
extendSteps(steps, update): extend all steps in the steps array.
Extends selected steps.
-
steps{array} Array with ids of the steps to extend. -
update{object|function} The object or function update.
-
-
Game.getProperty(property, nf)
Returns the requested step property from the game plot.
-
property{string} The name of the property. -
nf{mixed} Optional. The return value in case the requested property is not found. Default: null.
Returns {mixed} The value of the requested step property.
-
-
skip(stageId, stepId):
Removes a stage or a step from the sequence.
-
stageId{string} The id of the stage -
stepId{string} Optional. The id of the step
-
-
skip(stageId, stepId):
Re-inserts a previously skipped stage or a step into the sequence.
-
stageId{string} The id of the stage -
stepId{string} Optional. The id of the step
-
-
isSkipped(stageId, stepId):
Checks if a stage or a step is currently skipped.
-
stageId{string} The id of the stage -
stepId{string} Optional. The id of the step
Returns {boolean} TRUE, if the stage/step is currently skipped.
-
To have a finer control on the game sequence, it is possible to specify the positions that stages and steps are allowed to take (0-indexed).
When a position is specified the order in which stages and steps are inserted might lose importance.
stager.stage('stage_1');
// * means take any available position.
stager.step('step_1', '*');
stager.step('step_2', '1');
stager.step('step_3', '*');defines a sequence of one stage with three steps, one of which is fixed in position 1 (positions are zero-indexed), and the other two can assume a variable position.
When the game runs it will follow a variable game sequence which will alternate between two possibilities:
-
step_1,step_2,step_3, -
step_3,step_2,step_1.
Positions can be specified for stages as well, and the position parameter is always the last one. Position can be specified as an expression which will be evaluated.
// Place stage 1 after position 0.
stager.stage('stage_1', '>0');
stager.step('step_1', '*');
stager.step('step_2', '1');
stager.step('step_3', '*');
// When no position is specified, the order
// of insertion into the sequence counts.
stager.stage('stage_2');
stager.repeatStage('stage_3', 2, '*');leads to one of the two sequences:
-
stage_3,stage_2,step_1,step_2,step_3, -
stage_3,stage_2,step_3,step_2,step_1(note the steps 1 and 3 are switched).
Blocks group together stages or steps and define the scope within which the position parameter applies. For example:
// Place block of stages with id "first_block" at any position > 0.
stager.stageBlock('first_block', '>0');
stager.stage('stage_1')
// This step can at any position within stage 1.
.step('step_1', '*')
// This block of steps must be at position 0 within stage 1.
// Notice that blocks can optionally be given an id (first param).
.stepBlock('Step Block 1', '0')
.step('step_2');
.step('step_3');
// This block of steps must be at position 1.
.stepBlock('Step Block 2', '1')
.step('step_4');
.step('step_5');
// Place this block of stages with ID "anywhere" anywhere in the sequence.
stager.stageBlock('anywhere', '*');
// Place at position 0 or 1.
stager.stage('stage_2', '0..1');
stager.stage('stage_3', '2');
.step('step_3_1', '*');
.step('step_3_2', '*');
stager.stage('stage_4', '*');would create the sequence (among others):
-
stage_4,stage_2,step_3_2,step_3_1,step_1_2,step_1_3,step_1_4,step_1_5,step_1_1.
The stager's state can be directly used to setup local or remote clients. When the game-sequence is passed onto clients is named Game Plot.
var state2 = stager2.getState();
// Setups locally the game-plot.
node.setup('plot', state2);
// Setups remotely the game-plot on client 'client'.
node.remoteSetup('plot', 'client', state2);The game-plot will be used at run-time to execute the game as defined by the stager.
The setup of the game-plot can be done automatically when a client
enters a new game room if the plot is exported by one of the
game-paths defined in the game.settings.js file. For details, refer
to the User Guide.
Load the nodegame-client library and request a new stager instance.
var ngc = require('nodegame-client');
var stager = ngc.getStager();After all the stages have been added, and the sequence created, it is
possible to fetch the stager state with stager.getState(). The state
can be passed directly to the constructor of a new Stager object, to
create a copy that can be extended.
var state = stager.getState();
// Variable state contains the stages, the steps,
// and the sequence, as previously defined.
var stager2 = ngc.getStager(state);
// Extend stager2 as needed.In the standard execution mode, the sequence of all the stages of a game must be completely defined before the game starts.
However, it is also possible to decide the next stage of the game dynamically at run-time. This way the actual sequence played can depend on players's actions. This is called the flexible execution mode.
-
registerGeneralNext(func): Sets general callback for next stage decision. Available only when nodegame is executed in flexible mode. The callback given here is used to determine the next stage. -
registerNext(id, func): Registers a step-decider callback for a specific stage. The function overrides the general callback for the specific stage, and determines the next stage. Available only when nodegame is executed in flexible mode.
The next functions must return the id of the next stage to be
executed or 'NODEGAME_GAMEOVER' in case the game is over.
Go back to the wiki Home.
Copyright (C) 2021 Stefano Balietti
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.