use serde_json::Value; use shared_contracts::runtime_story::{ RuntimeBattlePresentation, RuntimeStoryActionRequest, RuntimeStoryOptionView, RuntimeStoryPatch, RuntimeStorySnapshotPayload, }; pub mod battle; #[cfg(test)] mod battle_tests; pub mod core; pub mod forge; pub mod forge_actions; pub mod game_state; pub mod npc_support; pub mod options; pub mod post_battle; pub mod prompt_context; pub mod story_engine; pub mod view_model; pub use battle::{ build_battle_runtime_story_options, inventory_item_has_usable_effect, resolve_battle_action, restore_player_resource, }; pub use core::{ MAX_PLAYER_LEVEL, add_player_currency, add_player_inventory_items, append_active_build_buffs, append_story_history, clear_encounter_only, clear_encounter_state, cumulative_xp_required, ensure_json_object, first_hostile_npc_string_field, format_now_rfc3339, grant_player_progression_experience, increment_runtime_stat, normalize_optional_string, normalize_required_string, read_array_field, read_bool_field, read_field, read_i32_field, read_object_field, read_optional_string_field, read_required_string_field, read_runtime_session_id, read_u32_field, remove_player_inventory_item, resolve_progression_level, write_bool_field, write_current_encounter_i32_field, write_first_hostile_npc_i32_field, write_i32_field, write_null_field, write_string_field, write_u32_field, xp_to_next_level_for, }; pub use forge::{build_runtime_equipment_item, build_runtime_material_item, format_currency_text}; pub use forge_actions::{ resolve_forge_craft_action, resolve_forge_dismantle_action, resolve_forge_reforge_action, }; pub use game_state::{ add_inventory_items_to_list, apply_equipment_loadout_to_state, battle_mode_text, build_current_build_toast, clone_inventory_item_with_quantity, current_encounter_id, current_encounter_name, current_encounter_name_from_battle, ensure_inventory_action_available, equipment_bonus_fallbacks, equipment_item_bonuses, equipment_slot_label, find_player_inventory_entry, has_giftable_player_inventory, item_rarity_key, normalize_equipment_slot_id, normalize_equipped_item, read_equipment_total_bonuses, read_inventory_item_name, read_player_equipment_item, read_player_inventory_values, read_runtime_equipment_bonus_cache, remove_inventory_item_from_list, resolve_equipment_slot_for_item, write_player_equipment_item, write_player_inventory_values, write_runtime_equipment_bonus_cache, }; pub use npc_support::{ build_npc_gift_result_text, build_runtime_npc_interaction_view, npc_buyback_price, npc_purchase_price, recruit_companion_to_party, resolve_npc_gift_affinity_gain, trade_quantity_suffix, write_runtime_npc_interaction_view, }; pub use options::{ build_disabled_runtime_story_option, build_runtime_story_option_from_story_option, build_runtime_story_option_interaction, build_runtime_story_option_with_payload, build_static_runtime_story_option, build_story_option_from_runtime_option, infer_option_scope, }; pub use post_battle::{ finalize_post_battle_resolution, is_terminal_battle_outcome, resolve_post_battle_story_options, }; pub use prompt_context::{RuntimeStoryPromptContextExtras, build_runtime_story_prompt_context}; pub use story_engine::project_story_engine_after_action; pub use view_model::{ build_runtime_story_companions, build_runtime_story_encounter, build_runtime_story_view_model, resolve_current_encounter_npc_state, }; pub const CONTINUE_ADVENTURE_FUNCTION_ID: &str = "story_continue_adventure"; pub const MAX_TASK5_COMPANIONS: usize = 2; pub struct StoryResolution { pub action_text: String, pub result_text: String, pub story_text: Option, pub presentation_options: Option>, pub saved_current_story: Option, pub patches: Vec, pub battle: Option, pub toast: Option, } pub struct GeneratedStoryPayload { pub story_text: String, pub history_result_text: String, pub presentation_options: Vec, pub saved_current_story: Value, } pub struct CurrentEncounterNpcQuestContext { pub npc_id: String, pub npc_name: String, } pub struct PendingQuestOfferContext { pub dialogue: Vec, pub turn_count: i32, pub custom_input_placeholder: String, pub quest: Value, pub quest_id: String, pub intro_text: Option, } pub struct RuntimeStoryActionResponseParts { pub requested_session_id: String, pub server_version: u32, pub snapshot: RuntimeStorySnapshotPayload, pub action_text: String, pub result_text: String, pub story_text: String, pub options: Vec, pub patches: Vec, pub toast: Option, pub battle: Option, } pub fn simple_story_resolution( game_state: &Value, action_text: String, result_text: &str, ) -> StoryResolution { StoryResolution { action_text, result_text: result_text.to_string(), story_text: None, presentation_options: None, saved_current_story: None, patches: vec![build_status_patch(game_state)], battle: None, toast: None, } } pub fn resolve_action_text(default_text: &str, request: &RuntimeStoryActionRequest) -> String { request .action .payload .as_ref() .and_then(|payload| read_optional_string_field(payload, "optionText")) .unwrap_or_else(|| default_text.to_string()) } pub fn build_status_patch(game_state: &Value) -> RuntimeStoryPatch { RuntimeStoryPatch::StatusChanged { in_battle: read_bool_field(game_state, "inBattle").unwrap_or(false), npc_interaction_active: read_bool_field(game_state, "npcInteractionActive") .unwrap_or(false), current_npc_battle_mode: read_optional_string_field(game_state, "currentNpcBattleMode"), current_npc_battle_outcome: read_optional_string_field( game_state, "currentNpcBattleOutcome", ), } } pub fn current_world_type(game_state: &Value) -> Option { read_optional_string_field(game_state, "worldType") }