Skip to content

Snowman template methods

jackdarker edited this page Apr 4, 2025 · 5 revisions

The main feature of snowman are template methods.

Imagine you want to output a text on your webpage that states the name of the player. In a story-passage you have therefore something written like that:
"The players name is Ratchel."

But maybe the player can enter any name when starting a game. So you have to somehow modify this static text and for example replace Ratchel with a value stored in javascript variable.

And this is where those template methods come into play.
There are actually 3 forms that look like that:

<% fooBar();fooMoo(); %>
<%= fooMoo()%>
<%- fooMoo()%>
The first form will run the javascriptcode in between <% and %> as soon as the story-passage is rendered as html.
The second form will run the javascriptcode and then insert the result of the code into the rendered html right at the position where it is stated. The third form will do like the second but will escape html-tokens before render, f.e. replace < with &lt.

So we can for our example simply write (assuming that we have created a variable playername in the object window.story.state.vars and assigned a value to it):
"The players name is <%= window.story.state.vars.playername%>."

The variable playername needs to be created and assigned somewhere before it is used. To do so I create a javascript-codefile game.js in folder src/scripts:
"use strict";
window.gm = window.gm || {}; //creates object gm that is my collection of game related functions and variables
window.gm.initGame= function() {
var s = window.story.state; //s in template is window.story.state from snowman!
if (!s.vars) { //create storage of variables if not existing
s.vars = {
playername='Cyril'
};
}};
In the first passage that is called, you then have to add the following to run initGame and initialise the variable:
<%window.gm.initGame()%>

As a rule of mine:

  • I create (simple & global) game variables in window.story.state. Because this will be written to a save-game/history automatically.
  • I create (reused) game-functions and objects in window.gm. See [here] why objects are not created in window.story.state and why I use (mostly) static functions.

Note that the replacement can take place in any text defined in s story-passage. Not just in the text to display to the player, but also in the script and style attached to it: <div class='<%= getClassToUse()%>' >
The players name is <%= window.story.state.vars.playername%>.
</div>
<script>
function foo() { setPlayerName(<%='Cyril'%>); }
foo();
</script>

In this example the class used to style the

will be set by calling the function getClassToUse.

And in the script the content of the function defined there will be set also before the page is rendered. Because there is not just the function definiton but also a line stating foo(), the function will be called automatically when rendering the page. Please note that in this example it would be also valid to not define and call foo at all and just writing the bare <%setPlayerName('Cyril');%> inside the script. This would have the same effect.

So, the templated methods give us an easy way to dynamical create html including possibilities to modify its styling - before rendering the page.

If you check the example above you will find a design-flaw related to the execution-flow of the replacement:

When the passage is activated, the storyformat will run its duty and execute and replace the templates in the passage-text.
Then the passage-text is send to the browser-renderer which interpretes the html and executes the scripts in it.
In our example this means:

  • the storyformat will search for templates from top to bottom and will execute their code in that order
  • the function foo is executed when the page is shown
  • but at this moment the playername in the output-line was already replaced. So you will get the name before the execution of foo !

Note about defining functions for use in templates: Within <%...%> you cannot call functions/variables that are defined in the <script>-section of the passage (because the script is not yet compiled).
You have to define the function inside <%...%>. You dont need to define the function again if you want to use it in other templates in THIS passage. Each passage has its own context. If Passage A calls <%=window.story.render("PassageB")%> and PassageB has <% function foo(){return("x");}%>, only PassageB can access foo(). But PassageA can savely define its own function foo without the risk overwriting foo from PassageB. A template can call functions attached to window.story.state or other global data (window....)

Clone this wiki locally