Rez v1.7.0 — open source language & engine for making procedural narrative games

I’ve released Rez v1.6.2 with two new conveniences:

The ^i{} initializer attribute type now supports an optional priority from 1-10. Initializers with lower priority score run before those with a higher priority score.

This allows one initializer to depend upon the value of a previous initializer, something that was previously impossible. For example:

@actor rando {
  gender: ^i:1{return ["male", "female"].randomElement();}
  given_name: ^i:2{return $(`${this.gender}_names`).randomElement();}
  family_name: ^i:1{return $("family_names").randomElement();}
  name: ^i{return `${this.given_name} ${this.family_name}`;}
}

This is a big one and removes the need for some ^p{} properties and cleans up some on_init and on_copy handlers.

The RezDieRoll class now supports the idea of advantage and disadvantage on die rolls.

Rez now has a makeDie("1d20+1") function for creating RezDieRoll instances from a string specification.

1 Like

I try to favour composition for most things but, yes, sometimes it’s the right tool for the job :slight_smile:

Well this is a bit of a surprise, Claude turns out to be quite capable of writing Rez code. It helps that Rez is declarative and mainly consists of values or Javascript functions but feeding it the language & element catalog as project source docs, it seems to have picked up the structure and relationships and can build me examples.

Released v1.6.4

This update adds the following:

  • Adds $init_after: attribute that allows specifying a custom init order for elements
  • Dispatches document_loaded event on DOMContentLoaded
  • Calls to setAttribute() trigger on_set_attr
  • Stdlib: +Array#zip, +Array#min, +Array#max, +Math#perc, +RezBasicObject#unmap_attr
  • Stdlib: +Rez.D4, +Rez.D6, +Rez.D8, +Rez.D10, +Rez.D12, +Rez.D20, +Rez.D100
  • Stdlib: -Number#chance

The two biggest changes are the new on_set_attr handler behaviour and the on_document_loaded behaviour.

For example I have something like this:

@skill base_skill {
  value: 1
  potential: 4
  score: 3

  on_set_attr: (obj, params) => {
    const {attrName, oldValue, newValue} = params;
    if(attrName == "value" || attrName == "potential") {
      obj.setAttribute("score", skill.value + ((0.5 * skill.potential).roundp()), false);
    }
  }
}

I could have defined score as:

score: ^p{
  return this.value + (0.5*this.potential).roundp();
}

and it would have been fine but it felt wasteful given that score is getting read a lot but changed very infrequently. I could also have provided a special method for changing the value or potential fields. But it felt error prone. So, now you can respond to changes to attributes.

Next I wanted to create a progress dialog that tracks the progress of my world builder while it is generating the sector and its history. This process takes quite a few seconds and blocks the UI.

Rez includes Alpine.JS which makes this pretty easy with setTimeout scheduling the work:

<div id="progress-container" x-data="{message: 'Initializing simulation...',progress: 0}">
  <div class="box mt-6">
    <h3 class="title is-4">
      <span x-show="progress<100" class="icon-text">
        <span class="icon">
          <i class="fas fa-spinner fa-spin"></i>
        </span>
      </span>
      Building the Galaxy
    </h3>

    <p class="mb-4" x-text="message"></p>

    <progress
      class="progress is-primary"
      x-bind:value="progress"
      max="100"
      x-text="Math.round(progress) + '%'">
    </progress>
  </div>
</div>

However there is one big problem. Alpine.js has to be loaded with defer which means it’s not available when the Game.start() method is called and the view gets painted. So using the regular event handlers Rez makes available don’t help.

Enter the on_document_loaded handler. This is triggered by the DOMContentLoaded event and ensures that the Alpine reference is available.

@card c_world_building {
  ...

  simulate: function() {
    const progressData = Alpine.$data(document.getElementById("progress-container"));

    if(this.cur_year < this.end_year) {
      this.cur_year += 1;
      const progress = (100 * (this.cur_year - this.start_year) / (this.end_year - this.start_year)).roundp();

      progressData.message = `Simulating ${this.cur_year}`;
      progressData.progress = progress;

      this.world_builder.simulate_year(this.cur_year);
      setTimeout(this.simulate.bind(this));
    } else {
      progressData.message = "Finished";
      setTimeout(() => {$game.startSceneWithId("sc_academy")}, 1500);
    }
  }

  on_document_loaded: (card) => {
    card.cur_year = card.start_year;
    card.simulate();
  }

  ...
}

The card has painted the display but the simulator doesn’t get started until after the document load is finished.

1 Like

Just released Rez v1.6.6

The main change is that @alias has been deprecated in favour of @elem where the main difference is you can now do this:

@elem hat = item
@defaults hat {
  type: :hat
  bogie_would_approve: false
}

@elem wool_hat = hat
@defaults wool_hat {
  material: :wool
}

@wool_hat black_fedora {
  color: :black
  bogie_would_approve: true
}

It’s a kind of poor mans inheritance because all its really doing is compile-time copying attribute defaults up the chain from whichever built-in element type you started with to the element being defined.

This can be blended with the @mixin element allowing you to bring in properties and functions from outside. Mixins operate at run-time and don’t involve copying.

I continue to fix & tune as I work with Rez on Fleet Commander so am releasing v1.6.8.

This version updates to Elixir v1.17/Erlang v27.7 and so brings Rez completely up to date with current Elixir.

Additionally it adds a new syntax for dice-roll attributes, e.g. ^r:2d6+1 and a new shorthand where defining an attribute with a _die suffix automatically generates a corresponding property with a _roll suffix.

So defining:

temp_die: ^r:d10-1

means you automatically can automatically use temp_roll as a property to return temp_die values.

Some initialization bugs and ordering issues have been cleaned up so that you dont have to use on_copy so often and can more and more depending on ^i attributes.

In FleetCommander I am generating hundreds of stars and similar numbers of officers and it’s all working pretty nicely at this point. For example here is the template for creating G class stars like sol:

@main_sequence_star g_star {
  $template: true

  class: "G"

  luminosities: [9 9 8 8 7 7 6 6 5 5]
  types: ["G" "G" "G" "G" "M" "M" "M" "M" "M" "M"]

  base_color: {r: 255 g: 244 b: 232}

  min_zol: 0.8
  max_zol: 1.6
  mod_zol: 0.02
}

Makes use of @elem aliases, initializers, and defaults. Systems can also generate planetary bodies using a semi-realistic system borrowed (with permission) from my friend Chris Bateman’s game Outlands. I still need to hook up the zone-of-life calculation from the star generator to the planet generator. But soon I shall have a sector with hundreds of star systems to explore.

I’ve been reading Edwin MacRae’s book Narrative Design for Indies and the more I read the less interested in plot I become and the more interested in story.

Do you mean the story that comes from unscripted, emergent gameplay?

1 Like

I think so, yes. I’m still feeling my way here but what I am taking away is how plot works against player agency. MacRae quotes Hemingway:

The king dies then the queen dies is a story. The king dies and the queen dies of grief is a plot.

In this context story means theme & tone while plot means causality.

High causality is desirable in novels and movies because they don’t have gameplay. The more gameplay we have the less we require the causality of plot to make sense of things and the more we may find it undesirable because it constrains our freedom of action.

MacRae gives examples of games where small, independent, units of story can be used to convey a sense of character and place, while allowing the rest of the story to emerge through gameplay.

I’m still figuring this stuff out but it’s giving me a way to think about what I like in video games: that I deeply enjoy story but also desire high agency. Perhaps this explains something I have sometimes struggled with about IF: when it is tightly plotted like a story and where the puzzles “have a solution.”

In terms of FC this means that plot is something the player will largely not experience directly (although, at the moment, there is still an overarching plot). However plot will be something that NPCs experience very directly in the context of situation their ships are sent to deal with and how those play out.

2 Likes

Your explanation makes sense to me. If I understand it correctly…

In order to have player freedom in a simulated world, authorial control (plot) needs to be loosened. However, the concept of authorship changes from that of a scripted plot to scripted rules that dynamically provide a semblance of plot.

Idea: The consequences of your actions could read like a news feed and how the other factions view your motivations.

1 Like

I’m not really sure what I understand yet myself, but I feel like we’re more or less on the same wavelength.

In this case “scripted rules” is, I think, about gameplay mechanics that, done well, allow the player to infer causality (plot) for themselves without the game needing to spell it out. I think.

I begin to feel that I am exploring an outer edge of what this community considers to be interactive fiction. I certainly feel a long way from Zork.

1 Like

The news feed idea I mentioned was the “spelling out” part. :wink:

Sounds like Fallen London’s mission statement :smiley:

2 Likes

Kind of off topic, but still regarding story/plot/mechanics. The one thing I really enjoy about sci-fi worlds is an interesting premise that forms the basis for the player’s motivation and their interpretation of game-play mechanics. However, when it is established well, it will inevitably dictate some of the mechanics themselves. And I think this is what makes a game stand out from the crowd, I feel.

Idea: I know that you have entertained following the service of personnel in your game. Without knowing much about Fleet Commander, that sounds potentially overwhelming to me. Perhaps you are overseeing the first implementation of augmented military officers in conjunction with expected military decisions/strategies. They are high performing personnel and each ship has at least one augmented member. Something like this, creates a motivation to follow their careers, report back to the department heading up the initiative, transfer personnel… and this can grow into a connection between the player and these experimental crew members. Based on the data collected (performance success and failure translates to attribute ratings), it might influence the next batch of augmented officers or how the current officers might be upgraded.

Anyway, Fleet Commander seems ambitious enough without writing a fully fleshed out sci-fi setting, but I just wanted to share some insight you sparked with me. In Star Trek it’s Geordi LaForge and his visor, the Borg (and 7 of 9) and Commander Data himself. In Halo, it’s Master Chief (John-117) and the Spartan program. Merging organic and synthetic form and thought is always an interesting premise to me, but augmentation could be genetic manipulation or something else entirely.

I was just thinking to myself, what would motivate me to care about the careers of certain military personnel among an armada of spaceships at my command? Food for thought.

It’s hard to know in advance, but you won’t need to care about every crew member. I anticipate that you will pick favourites and follow them more closely. The simulation will run itself and you intervene where you like. But we’ll see.

Ah, I didn’t realize it’s more simulation than strategy. I thought the simulation part was for generating the universe to be played in at the beginning of the game, not the crux of the game itself. My bad! :crazy_face:

The strategy part will be about which officers you assign to which ships and which ships you assign to handle different situations. You won’t, at least in the current concept, have any control over how those situations play out - that part will be simulated, then you can react.

1 Like

Personally I don’t care for the people in these management-style games (I feel that that’s the direction it’s going?), but for the gameplay mechanics and world building.

Which makes me realize, Crying Suns is like the perfect sci-fi game for me. Glad I got it on sale sometime.

Will you be able to choose your crew from a list of candidates at the start?

Something I want to turn into a game of some kind is an idea I had - merging sci-fi and fantasy, with things like an FTL-priest instead of an FTL engine, who prays to some FTL god that transports the ship. Instead of shield generators you have your ship wizards that listen to the keyword “shields up”.

1 Like

There is an academy that recruits cadets from each colony and these cadets develop until they are ready to graduate and join a ship. You’ll have a good deal of control over this.

I played a little of the free demo a few weeks ago and saw some things I liked about it. I didn’t particularly care for the combat so I didn’t persist but it had some interesting ideas.

Also although I haven’t played it, I quite liked the look of Star Dynasties which has a lot of world building also.

To begin with I am going to eschew plot altogether and focus on gameplay and introducing story using minimal elements (what MacRae calls story glyphs).

If the core game isn’t fun then no amount of plot will save it.

1 Like

This is such a silly little component:

%% block-para
@component pb (bindings, assigns, content) => {
  return `<p class="block">${content}</p>`;
}

But with Bulma CSS, I am often using the block class on my paras, and it’s both easier to write and nicer to read:

<.pb>Two boys have been found rubbing linseed oil into the school cormorant.</.pb>

than:

<p class="block">...</p>

Makes me very glad I went through the hassle of implementing components in the renderer. Another component that I make a lot of use of is:

%% card button
@component cb (bindings, assigns, content) => {
  let target = assigns["card"];
  return `<a class="button is-danger is-dark" data-event="card" data-target="${target}">${content}</a>`;
}

So I can write:

<.cb card="c_next_card">Rub linseed oil into cormorant</.cb>

Instead of:

<a class="button is-danger is-dark" card="c_next_card">....</a>

Small affordances but they add up.