Files
Genarrative/server-rs/crates/spacetime-client/src/mapper/npc.rs

625 lines
23 KiB
Rust

use super::*;
impl From<DomainBattleStateInput> for BattleStateInput {
fn from(input: DomainBattleStateInput) -> Self {
Self {
battle_state_id: input.battle_state_id,
story_session_id: input.story_session_id,
runtime_session_id: input.runtime_session_id,
actor_user_id: input.actor_user_id,
chapter_id: input.chapter_id,
target_npc_id: input.target_npc_id,
target_name: input.target_name,
battle_mode: map_battle_mode(input.battle_mode),
player_hp: input.player_hp,
player_max_hp: input.player_max_hp,
player_mana: input.player_mana,
player_max_mana: input.player_max_mana,
target_hp: input.target_hp,
target_max_hp: input.target_max_hp,
experience_reward: input.experience_reward,
reward_items: input
.reward_items
.into_iter()
.map(map_runtime_item_reward_item_snapshot)
.collect(),
created_at_micros: input.created_at_micros,
}
}
}
pub(crate) fn map_npc_battle_interaction_procedure_result(
result: NpcBattleInteractionProcedureResult,
) -> Result<NpcBattleInteractionRecord, SpacetimeClientError> {
if !result.ok {
return Err(SpacetimeClientError::procedure_failed(result.error_message));
}
let interaction_result = result
.result
.ok_or_else(|| SpacetimeClientError::missing_snapshot("NPC 开战结果"))?;
Ok(build_npc_battle_interaction_record(
map_npc_battle_interaction_result(interaction_result),
))
}
pub(crate) fn map_battle_state_snapshot(
snapshot: BattleStateSnapshot,
) -> DomainBattleStateSnapshot {
DomainBattleStateSnapshot {
battle_state_id: snapshot.battle_state_id,
story_session_id: snapshot.story_session_id,
runtime_session_id: snapshot.runtime_session_id,
actor_user_id: snapshot.actor_user_id,
chapter_id: snapshot.chapter_id,
target_npc_id: snapshot.target_npc_id,
target_name: snapshot.target_name,
battle_mode: map_battle_mode_back(snapshot.battle_mode),
status: map_battle_status(snapshot.status),
player_hp: snapshot.player_hp,
player_max_hp: snapshot.player_max_hp,
player_mana: snapshot.player_mana,
player_max_mana: snapshot.player_max_mana,
target_hp: snapshot.target_hp,
target_max_hp: snapshot.target_max_hp,
experience_reward: snapshot.experience_reward,
reward_items: snapshot
.reward_items
.into_iter()
.map(map_runtime_item_reward_item_snapshot_back)
.collect(),
turn_index: snapshot.turn_index,
last_action_function_id: snapshot.last_action_function_id,
last_action_text: snapshot.last_action_text,
last_result_text: snapshot.last_result_text,
last_damage_dealt: snapshot.last_damage_dealt,
last_damage_taken: snapshot.last_damage_taken,
last_outcome: map_combat_outcome(snapshot.last_outcome),
version: snapshot.version,
created_at_micros: snapshot.created_at_micros,
updated_at_micros: snapshot.updated_at_micros,
}
}
pub(crate) fn map_npc_battle_interaction_result(
result: NpcBattleInteractionResult,
) -> NpcBattleInteractionSnapshot {
NpcBattleInteractionSnapshot {
interaction: map_npc_interaction_result(result.interaction),
battle_state: map_battle_state_snapshot(result.battle_state),
}
}
pub(crate) fn map_npc_interaction_result(
result: NpcInteractionResult,
) -> DomainNpcInteractionResult {
DomainNpcInteractionResult {
npc_state: map_npc_state_snapshot(result.npc_state),
interaction_status: map_npc_interaction_status(result.interaction_status),
action_text: result.action_text,
result_text: result.result_text,
story_text: result.story_text,
battle_mode: result.battle_mode.map(map_npc_interaction_battle_mode),
encounter_closed: result.encounter_closed,
affinity_changed: result.affinity_changed,
previous_affinity: result.previous_affinity,
next_affinity: result.next_affinity,
}
}
pub(crate) fn map_npc_state_snapshot(snapshot: NpcStateSnapshot) -> DomainNpcStateSnapshot {
DomainNpcStateSnapshot {
npc_state_id: snapshot.npc_state_id,
runtime_session_id: snapshot.runtime_session_id,
npc_id: snapshot.npc_id,
npc_name: snapshot.npc_name,
affinity: snapshot.affinity,
relation_state: map_npc_relation_state(snapshot.relation_state),
help_used: snapshot.help_used,
chatted_count: snapshot.chatted_count,
gifts_given: snapshot.gifts_given,
recruited: snapshot.recruited,
trade_stock_signature: snapshot.trade_stock_signature,
revealed_facts: snapshot.revealed_facts,
known_attribute_rumors: snapshot.known_attribute_rumors,
first_meaningful_contact_resolved: snapshot.first_meaningful_contact_resolved,
seen_backstory_chapter_ids: snapshot.seen_backstory_chapter_ids,
stance_profile: map_npc_stance_profile(snapshot.stance_profile),
created_at_micros: snapshot.created_at_micros,
updated_at_micros: snapshot.updated_at_micros,
}
}
pub(crate) fn map_npc_relation_state(value: NpcRelationState) -> DomainNpcRelationState {
DomainNpcRelationState {
affinity: value.affinity,
stance: map_npc_relation_stance(value.stance),
}
}
pub(crate) fn map_npc_stance_profile(value: NpcStanceProfile) -> DomainNpcStanceProfile {
DomainNpcStanceProfile {
trust: value.trust,
warmth: value.warmth,
ideological_fit: value.ideological_fit,
fear_or_guard: value.fear_or_guard,
loyalty: value.loyalty,
current_conflict_tag: value.current_conflict_tag,
recent_approvals: value.recent_approvals,
recent_disapprovals: value.recent_disapprovals,
}
}
pub(crate) fn map_npc_interaction_status(
value: NpcInteractionStatus,
) -> DomainNpcInteractionStatus {
match value {
NpcInteractionStatus::Previewed => DomainNpcInteractionStatus::Previewed,
NpcInteractionStatus::Dialogue => DomainNpcInteractionStatus::Dialogue,
NpcInteractionStatus::Resolved => DomainNpcInteractionStatus::Resolved,
NpcInteractionStatus::Recruited => DomainNpcInteractionStatus::Recruited,
NpcInteractionStatus::BattlePending => DomainNpcInteractionStatus::BattlePending,
NpcInteractionStatus::Left => DomainNpcInteractionStatus::Left,
}
}
pub(crate) fn map_npc_interaction_battle_mode(
value: NpcInteractionBattleMode,
) -> DomainNpcInteractionBattleMode {
match value {
NpcInteractionBattleMode::Fight => DomainNpcInteractionBattleMode::Fight,
NpcInteractionBattleMode::Spar => DomainNpcInteractionBattleMode::Spar,
}
}
pub(crate) fn map_npc_relation_stance(value: NpcRelationStance) -> DomainNpcRelationStance {
match value {
NpcRelationStance::Hostile => DomainNpcRelationStance::Hostile,
NpcRelationStance::Guarded => DomainNpcRelationStance::Guarded,
NpcRelationStance::Neutral => DomainNpcRelationStance::Neutral,
NpcRelationStance::Cooperative => DomainNpcRelationStance::Cooperative,
NpcRelationStance::Bonded => DomainNpcRelationStance::Bonded,
}
}
pub(crate) fn map_ai_task_kind(value: DomainAiTaskKind) -> AiTaskKind {
match value {
DomainAiTaskKind::StoryGeneration => AiTaskKind::StoryGeneration,
DomainAiTaskKind::CharacterChat => AiTaskKind::CharacterChat,
DomainAiTaskKind::NpcChat => AiTaskKind::NpcChat,
DomainAiTaskKind::CustomWorldGeneration => AiTaskKind::CustomWorldGeneration,
DomainAiTaskKind::QuestIntent => AiTaskKind::QuestIntent,
DomainAiTaskKind::RuntimeItemIntent => AiTaskKind::RuntimeItemIntent,
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BattleStateRecord {
pub battle_state_id: String,
pub story_session_id: String,
pub runtime_session_id: String,
pub actor_user_id: String,
pub chapter_id: Option<String>,
pub target_npc_id: String,
pub target_name: String,
pub battle_mode: String,
pub status: String,
pub player_hp: i32,
pub player_max_hp: i32,
pub player_mana: i32,
pub player_max_mana: i32,
pub target_hp: i32,
pub target_max_hp: i32,
pub experience_reward: u32,
pub reward_items: Vec<DomainRuntimeItemRewardItemSnapshot>,
pub turn_index: u32,
pub last_action_function_id: Option<String>,
pub last_action_text: Option<String>,
pub last_result_text: Option<String>,
pub last_damage_dealt: i32,
pub last_damage_taken: i32,
pub last_outcome: String,
pub version: u32,
pub created_at: String,
pub updated_at: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CustomWorldLibraryEntryRecord {
pub owner_user_id: String,
pub profile_id: String,
pub public_work_code: Option<String>,
pub author_public_user_code: Option<String>,
pub profile: serde_json::Value,
pub visibility: String,
pub published_at: Option<String>,
pub updated_at: String,
pub author_display_name: String,
pub world_name: String,
pub subtitle: String,
pub summary_text: String,
pub cover_image_src: Option<String>,
pub theme_mode: String,
pub playable_npc_count: u32,
pub landmark_count: u32,
pub play_count: u32,
pub remix_count: u32,
pub like_count: u32,
pub recent_play_count_7d: u32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CustomWorldGalleryEntryRecord {
pub owner_user_id: String,
pub profile_id: String,
pub public_work_code: String,
pub author_public_user_code: String,
pub visibility: String,
pub published_at: Option<String>,
pub updated_at: String,
pub author_display_name: String,
pub world_name: String,
pub subtitle: String,
pub summary_text: String,
pub cover_image_src: Option<String>,
pub theme_mode: String,
pub playable_npc_count: u32,
pub landmark_count: u32,
pub play_count: u32,
pub remix_count: u32,
pub like_count: u32,
pub recent_play_count_7d: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CustomWorldPublishedProfileCompileRecord {
pub profile_id: String,
pub owner_user_id: String,
pub world_name: String,
pub subtitle: String,
pub summary_text: String,
pub theme_mode: String,
pub cover_image_src: Option<String>,
pub playable_npc_count: u32,
pub landmark_count: u32,
pub author_display_name: String,
pub compiled_profile: serde_json::Value,
pub updated_at: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CustomWorldWorkSummaryRecord {
pub work_id: String,
pub source_type: String,
pub status: String,
pub title: String,
pub subtitle: String,
pub summary: String,
pub cover_image_src: Option<String>,
pub cover_render_mode: Option<String>,
pub cover_character_image_srcs: Vec<String>,
pub updated_at: String,
pub published_at: Option<String>,
pub stage: Option<String>,
pub stage_label: Option<String>,
pub playable_npc_count: u32,
pub landmark_count: u32,
pub role_visual_ready_count: Option<u32>,
pub role_animation_ready_count: Option<u32>,
pub role_asset_summary_label: Option<String>,
pub session_id: Option<String>,
pub profile_id: Option<String>,
pub can_resume: bool,
pub can_enter_world: bool,
pub blocker_count: u32,
pub publish_ready: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CustomWorldProfileUpsertRecordInput {
pub profile_id: String,
pub owner_user_id: String,
pub public_work_code: Option<String>,
pub author_public_user_code: Option<String>,
pub source_agent_session_id: Option<String>,
pub world_name: String,
pub subtitle: String,
pub summary_text: String,
pub theme_mode: DomainCustomWorldThemeMode,
pub cover_image_src: Option<String>,
pub profile_payload_json: String,
pub playable_npc_count: u32,
pub landmark_count: u32,
pub author_display_name: String,
pub updated_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResolveNpcBattleInteractionInput {
pub npc_interaction: DomainResolveNpcInteractionInput,
pub story_session_id: String,
pub actor_user_id: String,
pub battle_state_id: Option<String>,
pub player_hp: i32,
pub player_max_hp: i32,
pub player_mana: i32,
pub player_max_mana: i32,
pub target_hp: i32,
pub target_max_hp: i32,
pub experience_reward: u32,
pub reward_items: Vec<DomainRuntimeItemRewardItemSnapshot>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NpcStateRecord {
pub npc_state_id: String,
pub runtime_session_id: String,
pub npc_id: String,
pub npc_name: String,
pub affinity: i32,
pub relation_stance: String,
pub help_used: bool,
pub chatted_count: u32,
pub gifts_given: u32,
pub recruited: bool,
pub trade_stock_signature: Option<String>,
pub revealed_facts: Vec<String>,
pub known_attribute_rumors: Vec<String>,
pub first_meaningful_contact_resolved: bool,
pub seen_backstory_chapter_ids: Vec<String>,
pub trust: u8,
pub warmth: u8,
pub ideological_fit: u8,
pub fear_or_guard: u8,
pub loyalty: u8,
pub current_conflict_tag: Option<String>,
pub recent_approvals: Vec<String>,
pub recent_disapprovals: Vec<String>,
pub created_at: String,
pub updated_at: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NpcInteractionRecord {
pub npc_state: NpcStateRecord,
pub interaction_status: String,
pub action_text: String,
pub result_text: String,
pub story_text: Option<String>,
pub battle_mode: Option<String>,
pub encounter_closed: bool,
pub affinity_changed: bool,
pub previous_affinity: i32,
pub next_affinity: i32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NpcBattleInteractionRecord {
pub npc_interaction: NpcInteractionRecord,
pub battle_state: BattleStateRecord,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct NpcBattleInteractionSnapshot {
interaction: DomainNpcInteractionResult,
battle_state: DomainBattleStateSnapshot,
}
pub(crate) fn build_battle_state_record(snapshot: DomainBattleStateSnapshot) -> BattleStateRecord {
BattleStateRecord {
battle_state_id: snapshot.battle_state_id,
story_session_id: snapshot.story_session_id,
runtime_session_id: snapshot.runtime_session_id,
actor_user_id: snapshot.actor_user_id,
chapter_id: snapshot.chapter_id,
target_npc_id: snapshot.target_npc_id,
target_name: snapshot.target_name,
battle_mode: snapshot.battle_mode.as_str().to_string(),
status: snapshot.status.as_str().to_string(),
player_hp: snapshot.player_hp,
player_max_hp: snapshot.player_max_hp,
player_mana: snapshot.player_mana,
player_max_mana: snapshot.player_max_mana,
target_hp: snapshot.target_hp,
target_max_hp: snapshot.target_max_hp,
experience_reward: snapshot.experience_reward,
reward_items: snapshot.reward_items,
turn_index: snapshot.turn_index,
last_action_function_id: snapshot.last_action_function_id,
last_action_text: snapshot.last_action_text,
last_result_text: snapshot.last_result_text,
last_damage_dealt: snapshot.last_damage_dealt,
last_damage_taken: snapshot.last_damage_taken,
last_outcome: snapshot.last_outcome.as_str().to_string(),
version: snapshot.version,
created_at: format_timestamp_micros(snapshot.created_at_micros),
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
}
}
impl From<ResolveNpcBattleInteractionInput>
for crate::module_bindings::ResolveNpcBattleInteractionInput
{
fn from(input: ResolveNpcBattleInteractionInput) -> Self {
Self {
npc_interaction: crate::module_bindings::ResolveNpcInteractionInput {
runtime_session_id: input.npc_interaction.runtime_session_id,
npc_id: input.npc_interaction.npc_id,
npc_name: input.npc_interaction.npc_name,
interaction_function_id: input.npc_interaction.interaction_function_id,
release_npc_id: input.npc_interaction.release_npc_id,
updated_at_micros: input.npc_interaction.updated_at_micros,
},
story_session_id: input.story_session_id,
actor_user_id: input.actor_user_id,
battle_state_id: input.battle_state_id,
player_hp: input.player_hp,
player_max_hp: input.player_max_hp,
player_mana: input.player_mana,
player_max_mana: input.player_max_mana,
target_hp: input.target_hp,
target_max_hp: input.target_max_hp,
experience_reward: input.experience_reward,
reward_items: input
.reward_items
.into_iter()
.map(map_runtime_item_reward_item_snapshot)
.collect(),
}
}
}
pub(crate) fn validate_npc_battle_interaction_input(
input: &ResolveNpcBattleInteractionInput,
) -> Result<(), SpacetimeClientError> {
let battle_state_input = DomainBattleStateInput {
battle_state_id: input
.battle_state_id
.clone()
.unwrap_or_else(|| "battle_preview".to_string()),
story_session_id: input.story_session_id.clone(),
runtime_session_id: input.npc_interaction.runtime_session_id.clone(),
actor_user_id: input.actor_user_id.clone(),
chapter_id: None,
target_npc_id: input.npc_interaction.npc_id.clone(),
target_name: input.npc_interaction.npc_name.clone(),
battle_mode: DomainBattleMode::Fight,
player_hp: input.player_hp,
player_max_hp: input.player_max_hp,
player_mana: input.player_mana,
player_max_mana: input.player_max_mana,
target_hp: input.target_hp,
target_max_hp: input.target_max_hp,
experience_reward: input.experience_reward,
reward_items: input.reward_items.clone(),
created_at_micros: input.npc_interaction.updated_at_micros,
};
validate_battle_state_input(&battle_state_input)
.map_err(|error| SpacetimeClientError::Runtime(error.to_string()))?;
for reward_item in input.reward_items.iter().cloned() {
normalize_reward_item_snapshot(reward_item)
.map_err(|error| SpacetimeClientError::Runtime(error.to_string()))?;
}
Ok(())
}
pub(crate) fn build_npc_state_record(snapshot: DomainNpcStateSnapshot) -> NpcStateRecord {
NpcStateRecord {
npc_state_id: snapshot.npc_state_id,
runtime_session_id: snapshot.runtime_session_id,
npc_id: snapshot.npc_id,
npc_name: snapshot.npc_name,
affinity: snapshot.affinity,
relation_stance: format_npc_relation_stance(snapshot.relation_state.stance).to_string(),
help_used: snapshot.help_used,
chatted_count: snapshot.chatted_count,
gifts_given: snapshot.gifts_given,
recruited: snapshot.recruited,
trade_stock_signature: snapshot.trade_stock_signature,
revealed_facts: snapshot.revealed_facts,
known_attribute_rumors: snapshot.known_attribute_rumors,
first_meaningful_contact_resolved: snapshot.first_meaningful_contact_resolved,
seen_backstory_chapter_ids: snapshot.seen_backstory_chapter_ids,
trust: snapshot.stance_profile.trust,
warmth: snapshot.stance_profile.warmth,
ideological_fit: snapshot.stance_profile.ideological_fit,
fear_or_guard: snapshot.stance_profile.fear_or_guard,
loyalty: snapshot.stance_profile.loyalty,
current_conflict_tag: snapshot.stance_profile.current_conflict_tag,
recent_approvals: snapshot.stance_profile.recent_approvals,
recent_disapprovals: snapshot.stance_profile.recent_disapprovals,
created_at: format_timestamp_micros(snapshot.created_at_micros),
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
}
}
pub(crate) fn build_npc_interaction_record(
result: DomainNpcInteractionResult,
) -> NpcInteractionRecord {
NpcInteractionRecord {
npc_state: build_npc_state_record(result.npc_state),
interaction_status: format_npc_interaction_status(result.interaction_status).to_string(),
action_text: result.action_text,
result_text: result.result_text,
story_text: result.story_text,
battle_mode: result
.battle_mode
.map(|mode| format_npc_interaction_battle_mode(mode).to_string()),
encounter_closed: result.encounter_closed,
affinity_changed: result.affinity_changed,
previous_affinity: result.previous_affinity,
next_affinity: result.next_affinity,
}
}
pub(crate) fn build_npc_battle_interaction_record(
result: NpcBattleInteractionSnapshot,
) -> NpcBattleInteractionRecord {
NpcBattleInteractionRecord {
npc_interaction: build_npc_interaction_record(result.interaction),
battle_state: build_battle_state_record(result.battle_state),
}
}
pub(crate) fn format_npc_relation_stance(value: DomainNpcRelationStance) -> &'static str {
match value {
DomainNpcRelationStance::Hostile => "hostile",
DomainNpcRelationStance::Guarded => "guarded",
DomainNpcRelationStance::Neutral => "neutral",
DomainNpcRelationStance::Cooperative => "cooperative",
DomainNpcRelationStance::Bonded => "bonded",
}
}
pub(crate) fn format_npc_interaction_status(value: DomainNpcInteractionStatus) -> &'static str {
match value {
DomainNpcInteractionStatus::Previewed => "previewed",
DomainNpcInteractionStatus::Dialogue => "dialogue",
DomainNpcInteractionStatus::Resolved => "resolved",
DomainNpcInteractionStatus::Recruited => "recruited",
DomainNpcInteractionStatus::BattlePending => "battle_pending",
DomainNpcInteractionStatus::Left => "left",
}
}
pub(crate) fn format_npc_interaction_battle_mode(
value: DomainNpcInteractionBattleMode,
) -> &'static str {
match value {
DomainNpcInteractionBattleMode::Fight => "fight",
DomainNpcInteractionBattleMode::Spar => "spar",
}
}
pub(crate) fn map_inventory_item_source_kind(
value: InventoryItemSourceKind,
) -> module_inventory::InventoryItemSourceKind {
match value {
InventoryItemSourceKind::StoryReward => {
module_inventory::InventoryItemSourceKind::StoryReward
}
InventoryItemSourceKind::QuestReward => {
module_inventory::InventoryItemSourceKind::QuestReward
}
InventoryItemSourceKind::TreasureReward => {
module_inventory::InventoryItemSourceKind::TreasureReward
}
InventoryItemSourceKind::NpcGift => module_inventory::InventoryItemSourceKind::NpcGift,
InventoryItemSourceKind::NpcTrade => module_inventory::InventoryItemSourceKind::NpcTrade,
InventoryItemSourceKind::CombatDrop => {
module_inventory::InventoryItemSourceKind::CombatDrop
}
InventoryItemSourceKind::ForgeCraft => {
module_inventory::InventoryItemSourceKind::ForgeCraft
}
InventoryItemSourceKind::ForgeReforge => {
module_inventory::InventoryItemSourceKind::ForgeReforge
}
InventoryItemSourceKind::ManualPatch => {
module_inventory::InventoryItemSourceKind::ManualPatch
}
}
}