Extending Economic Exchange Conditions

In earlier articles we introduced the economic exchange system we use to help us build Tau Station. Further publications explained how it helps us to write clean code and simplify our combat code. In this technical blog post we look at the economic exchange system in a little more detail.

Search for salvageable goods

Our economic exchange system is mostly declarative and the order in which we declare exchange steps is significant to the outcome. In addition to ordering we can also mark exchange steps with “behaviors” which further modify the conditions. This example code is from “Search for salvageable goods” in the Ruins:

sub scavenge ($self) {
  my $inventory    = $self->inventory;
  my $station_area = $self->station_area;

  my $key = 'found-item';

  my $focus_cost = config(
    qw/ areas ruins scavenge_cost_focus /);
  my $stamina_cost = config(
    qw/ areas ruins scavenge_cost_stamina /);

  my $exchange = $self->new_exchange(
    slug => 'scavenge',
    Steps(
      ASSERT(
        Location( $self => can_scavenge => $station_area )
      ),
      ASSERT(
        Stats(
          $self => minimum_required => {
            curr_stamina => $stamina_cost,
            focus        => $focus_cost,
          }
        )
      ),
      Location(
        $self => scavenge => {
          station_area => $station_area,
          key          => $key,
        }
      ),
      ALWAYS(
        Stats(
          $self => remove =>
            { curr_stamina => $stamina_cost }
        )
      ),
      Stats(
        $self => remove => { focus => $focus_cost }
      ),
      Inventory(
        $inventory => add_item =>
          { item => $key, new_key => 'found' }
      ),
      Event(
        $self => store => {
          event_type => 'find',
          stashed    => { item => $key }
        }
      ),
    ),
  );

  return $exchange->attempt;
}

Ignoring behaviors for a moment we can see that the logic is basically:

  1. Can character scavenge in this station area?
  2. Does character have minimum required stamina and focus?
  3. Character attempts to scavenge for an item.
  4. Reduce character’s stamina.
  5. Reduce character’s focus.
  6. Add item to inventory.
  7. Store a “find” game event.

As mentioned earlier, the order in which we declare steps matters. By default, if a single step fails, then all later steps are skipped. We need more flexibility which is where behaviors come in.

Behaviors

In order to control which steps are executed we can add further conditions which we call “behaviors”. These behaviors alter the flow of steps:

  • ASSERT – If this step fails, do not call any other steps for any reason.
  • ALWAYS – Always run this step (unless an ASSERT was previously failed).
  • FAILURE – Run this step if any earlier step failed (unless an ASSERT was previously failed).

Applying these behaviors we can see that the logic is more complex:

  1. Abort if the character cannot scavenge in this station area.
  2. Abort if character doesn’t have minimum required stamina and focus.
  3. Character attempts to scavenge for an item.
  4. Always reduce character’s stamina.
  5. If the player found something:
    • Reduce character’s focus
    • Add item to inventory
    • Store a “find” game event

Behaviors allows us to refine the logic within economic exchanges whilst keeping them readable and concise. Behaviors also show why ordering matters and how we can use it to our advantage.