Refine Eibenreith Ink bucket architecture
This commit is contained in:
+171
-119
@@ -8,6 +8,7 @@
|
||||
// - global non-character variables
|
||||
// - route counters and route helper functions
|
||||
// - time-slot, meal-plan, episode, location, and traversal helpers
|
||||
// - active choice-surface knots and bucket dispatch
|
||||
// - global story-state storage and LIST/state-tree helpers
|
||||
// - relationship helper functions
|
||||
//
|
||||
@@ -52,17 +53,56 @@ VAR slot_early_night_episode = no_episode
|
||||
VAR slot_late_night_episode = no_episode
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// ACTIVE CHOICE SURFACE / BUCKET DISPATCH
|
||||
// ============================================================================
|
||||
// Author-facing rule:
|
||||
// - End a chosen atomic weave with "-> TURN" when play should continue at the
|
||||
// current room/episode choice surface.
|
||||
// - Do not call provide_choices directly from chapter files. It is the internal
|
||||
// implementation behind TURN.
|
||||
// - Bucket provider knots end with "-> DONE". They only offer choices; they do
|
||||
// not themselves count as chosen player turns.
|
||||
//
|
||||
// Dispatch order:
|
||||
// - On first room entry: entry, moment, exits, episode, game.
|
||||
// - On later room entries: moment, look, exits, episode, game.
|
||||
//
|
||||
// This keeps room descriptions one-shot through Ink's own visit tracking while
|
||||
// preserving the design priority Moment > Room > Episode > Game.
|
||||
// The actual game_bucket remains in buckets.ink because it is content, not
|
||||
// helper logic.
|
||||
|
||||
=== TURN ===
|
||||
-> provide_choices
|
||||
|
||||
|
||||
=== provide_choices ===
|
||||
{
|
||||
- room_seen_on_enter():
|
||||
<- current_moment_bucket
|
||||
<- current_room_look_bucket
|
||||
- else:
|
||||
<- current_room_entry_bucket
|
||||
<- current_moment_bucket
|
||||
}
|
||||
<- current_room_exit_bucket
|
||||
<- current_episode_bucket
|
||||
<- current_game_bucket
|
||||
-> DONE
|
||||
|
||||
|
||||
=== empty_bucket ===
|
||||
-> DONE
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// INTRO / CHARACTER-GENERATION VARIABLES
|
||||
// ============================================================================
|
||||
// These values are defined during chapter 01, but they are global character
|
||||
// facts and may be referenced by later chapters.
|
||||
|
||||
VAR tut_choice_intro = false
|
||||
VAR tut_traversal_intro = false
|
||||
VAR tut_character_intro = false
|
||||
VAR tut_dialog_intro = false
|
||||
VAR tut_optional_intro = false
|
||||
VAR tutorial_state = ()
|
||||
|
||||
VAR class = ()
|
||||
VAR title_part = ""
|
||||
@@ -103,6 +143,23 @@ VAR route_eccentric = 0
|
||||
// Therefore:
|
||||
// - route_inc(route) increases by 1
|
||||
// - route_inc_by(route, amount) increases by amount
|
||||
//
|
||||
// Author-facing helpers:
|
||||
// - route(value): returns the current counter value for interpolation/debugging.
|
||||
// - route_inc(ref route): add 1 to a route counter.
|
||||
// - route_inc_by(ref route, amount): add an explicit amount.
|
||||
// - route_dec(ref route): subtract 1.
|
||||
// - route_dec_by(ref route, amount): subtract an explicit amount.
|
||||
// - route_move_to(ref route, amount): set an exact route counter value.
|
||||
// - route_clear(ref route): reset a route counter to 0.
|
||||
// - route_is(route, amount): true when the counter equals amount.
|
||||
// - route_before(route, amount): true when the counter is <= amount.
|
||||
// - route_reached(route, amount): true when the counter is >= amount.
|
||||
// - route_between(route, min, max): true when the counter is within the
|
||||
// inclusive range min..max.
|
||||
//
|
||||
// Route counters are simple totals, not Ingold-style two-value axes. Do not use
|
||||
// them with relationship matrix helpers.
|
||||
|
||||
=== function route(value) ===
|
||||
~ return value
|
||||
@@ -137,119 +194,18 @@ VAR route_eccentric = 0
|
||||
=== function route_between(value, min, max) ===
|
||||
~ return value >= min && value <= max
|
||||
|
||||
=== function route_min(a, b) ===
|
||||
{
|
||||
- a <= b:
|
||||
~ return a
|
||||
- else:
|
||||
~ return b
|
||||
}
|
||||
|
||||
=== function route_max(a, b) ===
|
||||
{
|
||||
- a >= b:
|
||||
~ return a
|
||||
- else:
|
||||
~ return b
|
||||
}
|
||||
|
||||
=== function route_diff(positive, negative) ===
|
||||
~ return positive - negative
|
||||
|
||||
=== function route_diff_reached(positive, negative, threshold) ===
|
||||
~ return positive - negative >= threshold
|
||||
|
||||
=== function route_diff_before(positive, negative, threshold) ===
|
||||
~ return positive - negative <= threshold
|
||||
|
||||
=== function route_diff_between(positive, negative, min, max) ===
|
||||
~ temp d = positive - negative
|
||||
~ return d >= min && d <= max
|
||||
|
||||
=== function route_total() ===
|
||||
~ return route_composure + route_detective + route_lover + route_sapphic + route_careless + route_eccentric
|
||||
|
||||
=== function route_share_reached(value, numerator, denominator) ===
|
||||
~ temp total = route_total()
|
||||
~ return total > 0 && value * denominator >= total * numerator
|
||||
|
||||
=== function route_share_before(value, numerator, denominator) ===
|
||||
~ temp total = route_total()
|
||||
~ return total > 0 && value * denominator < total * numerator
|
||||
|
||||
=== function route_high(value) ===
|
||||
~ return route_share_reached(value, 9, 10)
|
||||
|
||||
=== function route_up(value) ===
|
||||
~ return route_share_reached(value, 7, 10)
|
||||
|
||||
=== function route_down(value) ===
|
||||
~ return route_share_before(value, 3, 10)
|
||||
|
||||
=== function route_low(value) ===
|
||||
~ return route_share_before(value, 1, 10)
|
||||
|
||||
=== function matrix_total(positive, negative) ===
|
||||
~ return positive + negative
|
||||
|
||||
=== function matrix_share_reached(positive, negative, numerator, denominator) ===
|
||||
~ temp total = matrix_total(positive, negative)
|
||||
~ return total > 0 && positive * denominator >= total * numerator
|
||||
|
||||
=== function matrix_high(positive, negative) ===
|
||||
~ return matrix_share_reached(positive, negative, 9, 10)
|
||||
|
||||
=== function matrix_up(positive, negative) ===
|
||||
~ return matrix_share_reached(positive, negative, 7, 10)
|
||||
|
||||
=== function matrix_down(positive, negative) ===
|
||||
~ return matrix_share_reached(negative, positive, 7, 10)
|
||||
|
||||
=== function matrix_low(positive, negative) ===
|
||||
~ return matrix_share_reached(negative, positive, 9, 10)
|
||||
|
||||
=== function axis_positive(axis) ===
|
||||
{
|
||||
- axis == order_axis:
|
||||
~ return route_composure
|
||||
- axis == inquiry_axis:
|
||||
~ return route_detective
|
||||
- axis == allure_axis:
|
||||
~ return route_lover + route_sapphic
|
||||
- else:
|
||||
~ return 0
|
||||
}
|
||||
|
||||
=== function axis_negative(axis) ===
|
||||
{
|
||||
- axis == order_axis:
|
||||
~ return route_eccentric
|
||||
- axis == inquiry_axis:
|
||||
~ return route_careless
|
||||
- axis == allure_axis:
|
||||
~ return route_composure
|
||||
- else:
|
||||
~ return 0
|
||||
}
|
||||
|
||||
=== function axis_high(axis) ===
|
||||
~ return matrix_high(axis_positive(axis), axis_negative(axis))
|
||||
|
||||
=== function axis_up(axis) ===
|
||||
~ return matrix_up(axis_positive(axis), axis_negative(axis))
|
||||
|
||||
=== function axis_down(axis) ===
|
||||
~ return matrix_down(axis_positive(axis), axis_negative(axis))
|
||||
|
||||
=== function axis_low(axis) ===
|
||||
~ return matrix_low(axis_positive(axis), axis_negative(axis))
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// TIME-SLOT HELPERS
|
||||
// ============================================================================
|
||||
// TimeSlot is an ordered LIST declared in Eibenreith.ink.
|
||||
// Use these helpers for the daily schedule structure.
|
||||
//
|
||||
// Author-facing helpers:
|
||||
// - day(value), day_move_to(value), day_inc()
|
||||
// - time(slot), time_is(slot), time_move_to(slot)
|
||||
// - time_before(slot), time_reached(slot), time_between(first, last)
|
||||
//
|
||||
// time_before/reached/between rely on TimeSlot LIST order in main.ink.
|
||||
|
||||
=== function day(value) ===
|
||||
~ return current_day == value
|
||||
@@ -284,6 +240,17 @@ VAR route_eccentric = 0
|
||||
// ============================================================================
|
||||
// Content files define episode entry knots and buckets. Timetable control lives
|
||||
// here so authored episode files do not choose themselves.
|
||||
//
|
||||
// Author-facing helpers:
|
||||
// - slot_episode(slot, episode): query what episode is assigned to a timetable
|
||||
// slot.
|
||||
// - slot_schedule(slot, episode): assign an episode to a timetable slot.
|
||||
// - advance_to_slot(slot): move time forward and run the slot's episode.
|
||||
//
|
||||
// Internal knots:
|
||||
// - start_game, run_slot(slot), and run_episode(episode) are dispatch knots used
|
||||
// by the root entry point and timetable. Chapter files should normally not
|
||||
// bypass them.
|
||||
|
||||
=== start_game ===
|
||||
-> run_slot(mid_morning)
|
||||
@@ -364,6 +331,18 @@ VAR route_eccentric = 0
|
||||
// ============================================================================
|
||||
// EpisodeId is a LIST declared in Eibenreith.ink.
|
||||
// active_episode is the coarse structural episode currently being played.
|
||||
//
|
||||
// Author-facing helpers:
|
||||
// - episode(value), episode_active(value): true when the given episode is active.
|
||||
// - episode_move_to(value): manually change active_episode.
|
||||
// - episode_end(outcome): close the active episode and clear episode buckets.
|
||||
// - outcome_is(value), outcome_move_to(value): query/set the last slot outcome.
|
||||
//
|
||||
// Structural helper:
|
||||
// - enter_episode(value, slot, start_bucket, end_bucket, episode_bucket) is a
|
||||
// tunnel used by run_episode. It installs the episode's start/end/bucket
|
||||
// targets and resets the slot outcome. It should be called from timetable
|
||||
// dispatch, not from random room content.
|
||||
|
||||
=== function episode(value) ===
|
||||
~ return active_episode == value
|
||||
@@ -398,6 +377,11 @@ VAR route_eccentric = 0
|
||||
// ============================================================================
|
||||
// MealPlan is a LIST declared in Eibenreith.ink.
|
||||
// The arrival day uses this to remember how lunch is handled.
|
||||
//
|
||||
// Author-facing helpers:
|
||||
// - meal(value), meal_is(value): true when the selected plan equals value.
|
||||
// - meal_choose(value): set the plan.
|
||||
// - meal_clear(): reset to meal_unset.
|
||||
|
||||
=== function meal(value) ===
|
||||
~ return meal_plan == value
|
||||
@@ -418,6 +402,24 @@ VAR route_eccentric = 0
|
||||
// Location is a LIST declared in Eibenreith.ink.
|
||||
// current_location is intentionally coarse and exists so episode/game buckets
|
||||
// can react to where Valerie currently is.
|
||||
//
|
||||
// Author-facing helpers:
|
||||
// - loc(value), loc_is(value): true when Valerie is at location.
|
||||
// - loc_move_to(value): move Valerie and all current companions to location.
|
||||
// - accompanied_by(character): true when character is in the companion list.
|
||||
// - companion_join(character), companion_leave(character): update companions.
|
||||
// - present(character): true when a character's location equals current_location.
|
||||
// - character_move_to(character, location): move a known NPC independently.
|
||||
//
|
||||
// Structural helper:
|
||||
// - enter_room(location, entry_bucket, look_bucket, exit_bucket, moment_bucket)
|
||||
// moves Valerie, updates companions, and installs the active room buckets.
|
||||
// Room knots should normally do nothing except call enter_room and then TURN.
|
||||
//
|
||||
// Internal helper:
|
||||
// - move_companions_to(location) is used by loc_move_to().
|
||||
// - room_seen_on_enter() is used by provide_choices() to decide whether to show
|
||||
// the one-shot entry text or the repeat look action.
|
||||
|
||||
=== function loc(value) ===
|
||||
~ return current_location == value
|
||||
@@ -493,6 +495,26 @@ VAR story_state = ()
|
||||
// ============================================================================
|
||||
// STATE HELPERS
|
||||
// ============================================================================
|
||||
// There are two kinds of story state:
|
||||
// - Ordered encounter/progress states use state_reach/state_reached. Reaching a
|
||||
// later value automatically counts all earlier values in the same LIST.
|
||||
// - Independent checklist facts use mark/has/lacks. They do not imply their
|
||||
// neighbours in the LIST.
|
||||
//
|
||||
// Author-facing helpers:
|
||||
// - state_reach(state_or_states): advance one or more ordered LIST states.
|
||||
// - state_move_to(state): readable alias for state_reach(state).
|
||||
// - state_reached(state), state_before(state), state_between(a, b),
|
||||
// state_is(state), state_current(state): query ordered progress.
|
||||
// - mark(fact_or_facts), has(fact_or_facts), lacks(fact_or_facts): manage exact
|
||||
// checklist facts.
|
||||
// - tutorial(tutorial_fact): returns true once and marks the tutorial as shown.
|
||||
// - state_clear(state), state_clear_all(): reset helpers for rare debug/dream
|
||||
// logic, not normal authoring.
|
||||
//
|
||||
// Internal helper:
|
||||
// - state_pop(ref list) exists only so state_reach can process multi-value
|
||||
// lists. Do not use it in chapter prose.
|
||||
|
||||
// Remove and return the lowest item from a list.
|
||||
// Internal helper for state_reach().
|
||||
@@ -605,6 +627,17 @@ VAR story_state = ()
|
||||
~ return true
|
||||
|
||||
|
||||
// Return true once for a tutorial prompt, and mark it as seen immediately.
|
||||
=== function tutorial(tutorial_to_show) ===
|
||||
{
|
||||
- tutorial_state ? tutorial_to_show:
|
||||
~ return false
|
||||
- else:
|
||||
~ tutorial_state += tutorial_to_show
|
||||
~ return true
|
||||
}
|
||||
|
||||
|
||||
// Alias for readability when scene code wants to say move_to.
|
||||
// Same high-watermark behavior as state_reach().
|
||||
=== function state_move_to(state) ===
|
||||
@@ -633,6 +666,24 @@ VAR story_state = ()
|
||||
// Therefore:
|
||||
// - rel_inc(rel) increases by 1
|
||||
// - rel_inc_by(rel, amount) increases by amount
|
||||
//
|
||||
// Author-facing helpers:
|
||||
// - rel(value): returns the current counter value for interpolation/debugging.
|
||||
// - rel_inc/ref, rel_inc_by/ref, rel_dec/ref, rel_dec_by/ref: change a counter.
|
||||
// - rel_move_to(ref value, amount), rel_clear(ref value): set/reset a counter.
|
||||
// - rel_is(value, amount), rel_before(value, amount),
|
||||
// rel_reached(value, amount), rel_between(value, min, max): numeric queries.
|
||||
// - rel_diff(positive, negative) and rel_diff_* compare a two-value axis by
|
||||
// subtraction.
|
||||
// - rel_share_reached(positive, negative, numerator, denominator) and
|
||||
// rel_high/up/down/low implement Ingold-style percentage queries for a pair.
|
||||
//
|
||||
// Use relationship helpers with the five standard relationship pairs, e.g.:
|
||||
// {rel_up(viktor_open, viktor_closed)}
|
||||
// {rel_high(amalia_reliable, amalia_unreliable)}
|
||||
//
|
||||
// Do not create per-character custom dimensions. If a concept does not fit the
|
||||
// standard pairs, express it in prose or in a semantic encounter LIST.
|
||||
|
||||
=== function rel(value) ===
|
||||
~ return value
|
||||
@@ -696,17 +747,18 @@ VAR story_state = ()
|
||||
~ temp d = positive - negative
|
||||
~ return d >= min && d <= max
|
||||
|
||||
=== function rel_share_reached(value, total, numerator, denominator) ===
|
||||
~ return total > 0 && value * denominator >= total * numerator
|
||||
=== function rel_share_reached(positive, negative, numerator, denominator) ===
|
||||
~ temp total = positive + negative
|
||||
~ return total > 0 && positive * denominator >= total * numerator
|
||||
|
||||
=== function rel_high(positive, negative) ===
|
||||
~ return matrix_high(positive, negative)
|
||||
~ return rel_share_reached(positive, negative, 9, 10)
|
||||
|
||||
=== function rel_up(positive, negative) ===
|
||||
~ return matrix_up(positive, negative)
|
||||
~ return rel_share_reached(positive, negative, 7, 10)
|
||||
|
||||
=== function rel_down(positive, negative) ===
|
||||
~ return matrix_down(positive, negative)
|
||||
~ return rel_share_reached(negative, positive, 7, 10)
|
||||
|
||||
=== function rel_low(positive, negative) ===
|
||||
~ return matrix_low(positive, negative)
|
||||
~ return rel_share_reached(negative, positive, 9, 10)
|
||||
|
||||
Reference in New Issue
Block a user