Skip to content

🧠Technical Memo: Adding Intrinsics #43

@PJensen

Description

@PJensen

Technical Memo: Intrinsics in JSHACK 🧠

Overview

This memo proposes introducing intrinsics as a first-class concept in JSHACK. Intrinsics represent meaningful game facts about an entity that are not stored directly as raw components but are instead derived from world state, event history, or virtual relationships.

From the perspective of systems consuming them, intrinsics behave similarly to components. However, unlike components, their values are evaluated rather than stored. This allows higher-level game rules to operate against stable semantic concepts without requiring ad-hoc flags scattered throughout the ECS data model.

Typical consumers of intrinsics include:

  • dialog systems
  • quest logic
  • faction reactions
  • item attunement
  • environmental interactions
  • achievement checks

The goal is to provide a clear semantic layer that sits between low-level simulation data and high-level gameplay logic.


Motivation

JSHACK currently favors a pure data model where most state is represented as ECS components. This works well for structural state but becomes awkward for facts that are:

  • derived from relationships (e.g., hierarchy or membership)
  • temporal (e.g., something that has never happened)
  • policy-driven (e.g., moral or faction interpretations of actions)

Attempting to encode these facts directly as components tends to produce brittle logic or proliferating flags.

Intrinsics solve this by representing derived truths about entities that can be queried consistently without requiring them to exist as stored state.


Concept

An intrinsic represents a semantic property of an entity.

(subject, intrinsic_name) → value

Most intrinsics will be boolean, though numeric or categorical values are possible.

Intrinsics should represent game meaning, not mechanical signals.

For example:

peaceful
never_hit_monster

These are gameplay truths, not implementation details like counters or timestamps.


Example Intrinsics

peaceful

Represents whether a player has maintained peaceful behavior.

Possible evaluation criteria:

  • no hostile verbs executed
  • no aggressive actions within a defined window
  • no intimidation or theft events

never_hit_monster

Represents whether the player has ever dealt damage to a monster.

Evaluation condition:

playerDamageToMonsterCount == 0

Note that this intrinsic may intentionally diverge from other behavioral concepts such as peacefulness.


Implementation Direction

Intrinsics are expected to be implemented as a hybrid of:

  • virtual components
  • evaluation rules

Virtual components already provide a mechanism for derived state within ECS. Intrinsics extend this idea by allowing evaluation logic to incorporate additional signals such as event history.

Core Structure

A minimal implementation might include:

Intrinsic Registry

Maps intrinsic names to evaluation functions.

intrinsic_name → evaluator

Intrinsic Evaluator

Function responsible for computing the intrinsic value.

evaluate(world, entityId) → value

Evaluators may inspect:

  • ECS components
  • virtual components
  • indexed event facts
  • hierarchical relationships

Per-Tick Memoization

Intrinsic values should be cached during a simulation tick to avoid repeated evaluation.

(entityId, intrinsicName) → cachedValue

This ensures intrinsic checks remain inexpensive even when frequently queried.


Event-Derived Facts

Many intrinsics depend on historical events.

Instead of scanning the entire event log repeatedly, the engine should maintain compact indexed facts per entity as events are applied.

Examples:

playerDamageToMonsterCount
playerHasEverHitMonster
lastAggressiveActionTick

Intrinsics can then evaluate cheaply using these indexed values.

Example:

never_hit_monster = playerDamageToMonsterCount == 0

This allows intrinsics to remain efficient without sacrificing expressiveness.


Usage: Gates

The primary consumer of intrinsics will be gates.

A gate represents a rule boundary where a condition must be evaluated before allowing some behavior to proceed.

Examples include:

  • dialog options
  • quest progression
  • faction reactions
  • shrine interactions
  • item attunement
  • achievements

Instead of embedding low-level checks:

if (player.damageDealtToMonstersCount === 0)

systems would instead query the intrinsic:

intrinsic(player, "never_hit_monster")

This keeps rules declarative and stable.


Design Principles

Intrinsics should follow several guiding principles.

Semantic meaning

Intrinsics represent meaningful gameplay truths rather than low-level mechanical signals.

Deterministic evaluation

Given the same world snapshot and event facts, intrinsic results should always be the same.

Cheap queries

Evaluation should be inexpensive through memoization and indexed facts.

Small surface area

The set of intrinsic names should remain small and stable.

Minimal persistence

Intrinsic values themselves should generally not be persisted. Instead, the underlying facts required for evaluation should be stored.


Benefits

Introducing intrinsics provides several structural improvements.

  • Gameplay rules become declarative and expressive
  • Semantic game truths become centralized
  • ECS components remain focused on structural state
  • Narrative systems gain a stable gating interface
  • Derived conditions can evolve without breaking consumers

Intrinsics effectively introduce a semantic layer above raw simulation data.


Conclusion

Intrinsics provide a clean mechanism for representing derived gameplay truths within the JSHACK simulation.

They allow systems to reason about meaningful concepts such as peacefulness or pacifism without depending on raw counters or flags. By combining virtual components, evaluation logic, and indexed event facts, intrinsics can remain both expressive and efficient.

Conceptually, intrinsics sit above components and events as a lightweight semantic layer that enables more maintainable rule systems and richer gameplay logic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions