推进 SpacetimeDB adapter 与 client 收口
This commit is contained in:
@@ -8,8 +8,14 @@ use module_combat::{
|
||||
BattleMode, BattleStateInput, ResolveCombatActionInput, generate_battle_state_id,
|
||||
};
|
||||
use module_npc::{NPC_FIGHT_FUNCTION_ID, NPC_SPAR_FUNCTION_ID, ResolveNpcInteractionInput};
|
||||
use serde::Deserialize;
|
||||
use serde_json::{Value, json};
|
||||
use shared_contracts::story::{
|
||||
CreateStoryBattleRequest, CreateStoryNpcBattleRequest, CreateStoryNpcBattleResponse,
|
||||
ResolveStoryBattleRequest, ResolveStoryBattleResponse, StoryBattleRewardItemPayload,
|
||||
StoryBattleRewardItemRequest, StoryBattleStatePayload, StoryBattleStateResponse,
|
||||
StoryCombatActionPayload, StoryNpcInteractionPayload, StoryNpcStanceProfilePayload,
|
||||
StoryNpcStatePayload,
|
||||
};
|
||||
use shared_kernel::{normalize_optional_string, normalize_required_string, normalize_string_list};
|
||||
use spacetime_client::{ResolveNpcBattleInteractionInput, SpacetimeClientError};
|
||||
|
||||
@@ -18,84 +24,6 @@ use crate::{
|
||||
request_context::RequestContext, state::AppState,
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateStoryBattleRequest {
|
||||
pub story_session_id: String,
|
||||
pub runtime_session_id: String,
|
||||
#[serde(default)]
|
||||
pub chapter_id: Option<String>,
|
||||
pub target_npc_id: String,
|
||||
pub target_name: String,
|
||||
pub battle_mode: 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,
|
||||
#[serde(default)]
|
||||
pub experience_reward: u32,
|
||||
#[serde(default)]
|
||||
pub reward_items: Vec<StoryBattleRewardItemRequest>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ResolveStoryBattleRequest {
|
||||
pub battle_state_id: String,
|
||||
pub function_id: String,
|
||||
pub action_text: String,
|
||||
pub base_damage: i32,
|
||||
pub mana_cost: i32,
|
||||
pub heal: i32,
|
||||
pub mana_restore: i32,
|
||||
pub counter_multiplier_basis_points: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateStoryNpcBattleRequest {
|
||||
pub story_session_id: String,
|
||||
pub runtime_session_id: String,
|
||||
pub npc_id: String,
|
||||
pub npc_name: String,
|
||||
pub interaction_function_id: String,
|
||||
#[serde(default)]
|
||||
pub release_npc_id: Option<String>,
|
||||
#[serde(default)]
|
||||
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,
|
||||
#[serde(default)]
|
||||
pub experience_reward: u32,
|
||||
#[serde(default)]
|
||||
pub reward_items: Vec<StoryBattleRewardItemRequest>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StoryBattleRewardItemRequest {
|
||||
pub item_id: String,
|
||||
pub category: String,
|
||||
pub item_name: String,
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
pub quantity: u32,
|
||||
pub rarity: String,
|
||||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
pub stackable: bool,
|
||||
#[serde(default)]
|
||||
pub stack_key: String,
|
||||
#[serde(default)]
|
||||
pub equipment_slot_id: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn create_story_battle(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
@@ -152,9 +80,9 @@ pub async fn create_story_battle(
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
json!({
|
||||
"battleState": build_battle_state_payload(&result),
|
||||
}),
|
||||
StoryBattleStateResponse {
|
||||
battle_state: build_battle_state_payload(&result),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
@@ -185,14 +113,14 @@ pub async fn resolve_story_battle(
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
json!({
|
||||
"battleState": build_battle_state_payload(&result.battle_state),
|
||||
"combat": {
|
||||
"damageDealt": result.damage_dealt,
|
||||
"damageTaken": result.damage_taken,
|
||||
"outcome": result.outcome,
|
||||
}
|
||||
}),
|
||||
ResolveStoryBattleResponse {
|
||||
battle_state: build_battle_state_payload(&result.battle_state),
|
||||
combat: StoryCombatActionPayload {
|
||||
damage_dealt: result.damage_dealt,
|
||||
damage_taken: result.damage_taken,
|
||||
outcome: result.outcome,
|
||||
},
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
@@ -212,9 +140,9 @@ pub async fn get_story_battle_state(
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
json!({
|
||||
"battleState": build_battle_state_payload(&result),
|
||||
}),
|
||||
StoryBattleStateResponse {
|
||||
battle_state: build_battle_state_payload(&result),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
@@ -278,58 +206,69 @@ pub async fn create_story_npc_battle(
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
json!({
|
||||
"npcInteraction": build_npc_interaction_payload(&result.npc_interaction),
|
||||
"battleState": build_battle_state_payload(&result.battle_state),
|
||||
}),
|
||||
CreateStoryNpcBattleResponse {
|
||||
npc_interaction: build_npc_interaction_payload(&result.npc_interaction),
|
||||
battle_state: build_battle_state_payload(&result.battle_state),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn build_battle_state_payload(record: &spacetime_client::BattleStateRecord) -> Value {
|
||||
json!({
|
||||
"battleStateId": record.battle_state_id,
|
||||
"storySessionId": record.story_session_id,
|
||||
"runtimeSessionId": record.runtime_session_id,
|
||||
"actorUserId": record.actor_user_id,
|
||||
"chapterId": record.chapter_id,
|
||||
"targetNpcId": record.target_npc_id,
|
||||
"targetName": record.target_name,
|
||||
"battleMode": record.battle_mode,
|
||||
"status": record.status,
|
||||
"playerHp": record.player_hp,
|
||||
"playerMaxHp": record.player_max_hp,
|
||||
"playerMana": record.player_mana,
|
||||
"playerMaxMana": record.player_max_mana,
|
||||
"targetHp": record.target_hp,
|
||||
"targetMaxHp": record.target_max_hp,
|
||||
"experienceReward": record.experience_reward,
|
||||
"rewardItems": record.reward_items.iter().map(|item| {
|
||||
json!({
|
||||
"itemId": item.item_id,
|
||||
"category": item.category,
|
||||
"itemName": item.item_name,
|
||||
"description": item.description,
|
||||
"quantity": item.quantity,
|
||||
"rarity": format_runtime_item_reward_item_rarity(item.rarity),
|
||||
"tags": item.tags,
|
||||
"stackable": item.stackable,
|
||||
"stackKey": item.stack_key,
|
||||
"equipmentSlotId": item
|
||||
.equipment_slot_id
|
||||
.map(format_runtime_item_equipment_slot),
|
||||
})
|
||||
}).collect::<Vec<_>>(),
|
||||
"turnIndex": record.turn_index,
|
||||
"lastActionFunctionId": record.last_action_function_id,
|
||||
"lastActionText": record.last_action_text,
|
||||
"lastResultText": record.last_result_text,
|
||||
"lastDamageDealt": record.last_damage_dealt,
|
||||
"lastDamageTaken": record.last_damage_taken,
|
||||
"lastOutcome": record.last_outcome,
|
||||
"version": record.version,
|
||||
"createdAt": record.created_at,
|
||||
"updatedAt": record.updated_at,
|
||||
})
|
||||
fn build_battle_state_payload(
|
||||
record: &spacetime_client::BattleStateRecord,
|
||||
) -> StoryBattleStatePayload {
|
||||
StoryBattleStatePayload {
|
||||
battle_state_id: record.battle_state_id.clone(),
|
||||
story_session_id: record.story_session_id.clone(),
|
||||
runtime_session_id: record.runtime_session_id.clone(),
|
||||
actor_user_id: record.actor_user_id.clone(),
|
||||
chapter_id: record.chapter_id.clone(),
|
||||
target_npc_id: record.target_npc_id.clone(),
|
||||
target_name: record.target_name.clone(),
|
||||
battle_mode: record.battle_mode.clone(),
|
||||
status: record.status.clone(),
|
||||
player_hp: record.player_hp,
|
||||
player_max_hp: record.player_max_hp,
|
||||
player_mana: record.player_mana,
|
||||
player_max_mana: record.player_max_mana,
|
||||
target_hp: record.target_hp,
|
||||
target_max_hp: record.target_max_hp,
|
||||
experience_reward: record.experience_reward,
|
||||
reward_items: record
|
||||
.reward_items
|
||||
.iter()
|
||||
.map(build_battle_reward_item_payload)
|
||||
.collect(),
|
||||
turn_index: record.turn_index,
|
||||
last_action_function_id: record.last_action_function_id.clone(),
|
||||
last_action_text: record.last_action_text.clone(),
|
||||
last_result_text: record.last_result_text.clone(),
|
||||
last_damage_dealt: record.last_damage_dealt,
|
||||
last_damage_taken: record.last_damage_taken,
|
||||
last_outcome: record.last_outcome.clone(),
|
||||
version: record.version,
|
||||
created_at: record.created_at.clone(),
|
||||
updated_at: record.updated_at.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_battle_reward_item_payload(
|
||||
item: &module_runtime_item::RuntimeItemRewardItemSnapshot,
|
||||
) -> StoryBattleRewardItemPayload {
|
||||
StoryBattleRewardItemPayload {
|
||||
item_id: item.item_id.clone(),
|
||||
category: item.category.clone(),
|
||||
item_name: item.item_name.clone(),
|
||||
description: item.description.clone(),
|
||||
quantity: item.quantity,
|
||||
rarity: format_runtime_item_reward_item_rarity(item.rarity).to_string(),
|
||||
tags: item.tags.clone(),
|
||||
stackable: item.stackable,
|
||||
stack_key: item.stack_key.clone(),
|
||||
equipment_slot_id: item
|
||||
.equipment_slot_id
|
||||
.map(format_runtime_item_equipment_slot)
|
||||
.map(ToOwned::to_owned),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_runtime_item_reward_item_rarity(
|
||||
@@ -354,51 +293,53 @@ fn format_runtime_item_equipment_slot(
|
||||
}
|
||||
}
|
||||
|
||||
fn build_npc_state_payload(record: &spacetime_client::NpcStateRecord) -> Value {
|
||||
json!({
|
||||
"npcStateId": record.npc_state_id,
|
||||
"runtimeSessionId": record.runtime_session_id,
|
||||
"npcId": record.npc_id,
|
||||
"npcName": record.npc_name,
|
||||
"affinity": record.affinity,
|
||||
"relationStance": record.relation_stance,
|
||||
"helpUsed": record.help_used,
|
||||
"chattedCount": record.chatted_count,
|
||||
"giftsGiven": record.gifts_given,
|
||||
"recruited": record.recruited,
|
||||
"tradeStockSignature": record.trade_stock_signature,
|
||||
"revealedFacts": record.revealed_facts,
|
||||
"knownAttributeRumors": record.known_attribute_rumors,
|
||||
"firstMeaningfulContactResolved": record.first_meaningful_contact_resolved,
|
||||
"seenBackstoryChapterIds": record.seen_backstory_chapter_ids,
|
||||
"stanceProfile": {
|
||||
"trust": record.trust,
|
||||
"warmth": record.warmth,
|
||||
"ideologicalFit": record.ideological_fit,
|
||||
"fearOrGuard": record.fear_or_guard,
|
||||
"loyalty": record.loyalty,
|
||||
"currentConflictTag": record.current_conflict_tag,
|
||||
"recentApprovals": record.recent_approvals,
|
||||
"recentDisapprovals": record.recent_disapprovals,
|
||||
fn build_npc_state_payload(record: &spacetime_client::NpcStateRecord) -> StoryNpcStatePayload {
|
||||
StoryNpcStatePayload {
|
||||
npc_state_id: record.npc_state_id.clone(),
|
||||
runtime_session_id: record.runtime_session_id.clone(),
|
||||
npc_id: record.npc_id.clone(),
|
||||
npc_name: record.npc_name.clone(),
|
||||
affinity: record.affinity,
|
||||
relation_stance: record.relation_stance.clone(),
|
||||
help_used: record.help_used,
|
||||
chatted_count: record.chatted_count,
|
||||
gifts_given: record.gifts_given,
|
||||
recruited: record.recruited,
|
||||
trade_stock_signature: record.trade_stock_signature.clone(),
|
||||
revealed_facts: record.revealed_facts.clone(),
|
||||
known_attribute_rumors: record.known_attribute_rumors.clone(),
|
||||
first_meaningful_contact_resolved: record.first_meaningful_contact_resolved,
|
||||
seen_backstory_chapter_ids: record.seen_backstory_chapter_ids.clone(),
|
||||
stance_profile: StoryNpcStanceProfilePayload {
|
||||
trust: record.trust,
|
||||
warmth: record.warmth,
|
||||
ideological_fit: record.ideological_fit,
|
||||
fear_or_guard: record.fear_or_guard,
|
||||
loyalty: record.loyalty,
|
||||
current_conflict_tag: record.current_conflict_tag.clone(),
|
||||
recent_approvals: record.recent_approvals.clone(),
|
||||
recent_disapprovals: record.recent_disapprovals.clone(),
|
||||
},
|
||||
"createdAt": record.created_at,
|
||||
"updatedAt": record.updated_at,
|
||||
})
|
||||
created_at: record.created_at.clone(),
|
||||
updated_at: record.updated_at.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_npc_interaction_payload(record: &spacetime_client::NpcInteractionRecord) -> Value {
|
||||
json!({
|
||||
"npcState": build_npc_state_payload(&record.npc_state),
|
||||
"interactionStatus": record.interaction_status,
|
||||
"actionText": record.action_text,
|
||||
"resultText": record.result_text,
|
||||
"storyText": record.story_text,
|
||||
"battleMode": record.battle_mode,
|
||||
"encounterClosed": record.encounter_closed,
|
||||
"affinityChanged": record.affinity_changed,
|
||||
"previousAffinity": record.previous_affinity,
|
||||
"nextAffinity": record.next_affinity,
|
||||
})
|
||||
fn build_npc_interaction_payload(
|
||||
record: &spacetime_client::NpcInteractionRecord,
|
||||
) -> StoryNpcInteractionPayload {
|
||||
StoryNpcInteractionPayload {
|
||||
npc_state: build_npc_state_payload(&record.npc_state),
|
||||
interaction_status: record.interaction_status.clone(),
|
||||
action_text: record.action_text.clone(),
|
||||
result_text: record.result_text.clone(),
|
||||
story_text: record.story_text.clone(),
|
||||
battle_mode: record.battle_mode.clone(),
|
||||
encounter_closed: record.encounter_closed,
|
||||
affinity_changed: record.affinity_changed,
|
||||
previous_affinity: record.previous_affinity,
|
||||
next_affinity: record.next_affinity,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_battle_mode_strict(raw: &str) -> Option<BattleMode> {
|
||||
|
||||
Reference in New Issue
Block a user