推进 SpacetimeDB adapter 与 client 收口
This commit is contained in:
@@ -138,6 +138,220 @@ pub struct StoryRuntimeProjectionResponse {
|
||||
pub toast: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[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(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[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(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[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(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StoryBattleRewardItemPayload {
|
||||
pub item_id: String,
|
||||
pub category: String,
|
||||
pub item_name: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
pub quantity: u32,
|
||||
pub rarity: String,
|
||||
pub tags: Vec<String>,
|
||||
pub stackable: bool,
|
||||
pub stack_key: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub equipment_slot_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StoryBattleStatePayload {
|
||||
pub battle_state_id: String,
|
||||
pub story_session_id: String,
|
||||
pub runtime_session_id: String,
|
||||
pub actor_user_id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
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<StoryBattleRewardItemPayload>,
|
||||
pub turn_index: u32,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub last_action_function_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub last_action_text: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
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, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StoryBattleStateResponse {
|
||||
pub battle_state: StoryBattleStatePayload,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StoryCombatActionPayload {
|
||||
pub damage_dealt: i32,
|
||||
pub damage_taken: i32,
|
||||
pub outcome: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ResolveStoryBattleResponse {
|
||||
pub battle_state: StoryBattleStatePayload,
|
||||
pub combat: StoryCombatActionPayload,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StoryNpcStanceProfilePayload {
|
||||
pub trust: u8,
|
||||
pub warmth: u8,
|
||||
pub ideological_fit: u8,
|
||||
pub fear_or_guard: u8,
|
||||
pub loyalty: u8,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub current_conflict_tag: Option<String>,
|
||||
pub recent_approvals: Vec<String>,
|
||||
pub recent_disapprovals: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StoryNpcStatePayload {
|
||||
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,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
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 stance_profile: StoryNpcStanceProfilePayload,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StoryNpcInteractionPayload {
|
||||
pub npc_state: StoryNpcStatePayload,
|
||||
pub interaction_status: String,
|
||||
pub action_text: String,
|
||||
pub result_text: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub story_text: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub battle_mode: Option<String>,
|
||||
pub encounter_closed: bool,
|
||||
pub affinity_changed: bool,
|
||||
pub previous_affinity: i32,
|
||||
pub next_affinity: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateStoryNpcBattleResponse {
|
||||
pub npc_interaction: StoryNpcInteractionPayload,
|
||||
pub battle_state: StoryBattleStatePayload,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -312,4 +526,66 @@ mod tests {
|
||||
assert!(payload.get("viewModel").is_none());
|
||||
assert!(payload.get("presentation").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn story_battle_responses_use_story_contract_shape() {
|
||||
let battle_state = StoryBattleStatePayload {
|
||||
battle_state_id: "battle_1".to_string(),
|
||||
story_session_id: "storysess_1".to_string(),
|
||||
runtime_session_id: "runtime_1".to_string(),
|
||||
actor_user_id: "user_1".to_string(),
|
||||
chapter_id: None,
|
||||
target_npc_id: "npc_wolf".to_string(),
|
||||
target_name: "黑爪狼".to_string(),
|
||||
battle_mode: "fight".to_string(),
|
||||
status: "active".to_string(),
|
||||
player_hp: 28,
|
||||
player_max_hp: 40,
|
||||
player_mana: 12,
|
||||
player_max_mana: 20,
|
||||
target_hp: 18,
|
||||
target_max_hp: 30,
|
||||
experience_reward: 12,
|
||||
reward_items: vec![StoryBattleRewardItemPayload {
|
||||
item_id: "wolf-fang".to_string(),
|
||||
category: "material".to_string(),
|
||||
item_name: "狼牙".to_string(),
|
||||
description: None,
|
||||
quantity: 1,
|
||||
rarity: "common".to_string(),
|
||||
tags: vec!["beast".to_string()],
|
||||
stackable: true,
|
||||
stack_key: "wolf-fang".to_string(),
|
||||
equipment_slot_id: None,
|
||||
}],
|
||||
turn_index: 1,
|
||||
last_action_function_id: Some("battle_attack_basic".to_string()),
|
||||
last_action_text: Some("普通攻击".to_string()),
|
||||
last_result_text: Some("你击中了黑爪狼。".to_string()),
|
||||
last_damage_dealt: 10,
|
||||
last_damage_taken: 3,
|
||||
last_outcome: "ongoing".to_string(),
|
||||
version: 2,
|
||||
created_at: "1.000000Z".to_string(),
|
||||
updated_at: "2.000000Z".to_string(),
|
||||
};
|
||||
|
||||
let payload = serde_json::to_value(ResolveStoryBattleResponse {
|
||||
battle_state,
|
||||
combat: StoryCombatActionPayload {
|
||||
damage_dealt: 10,
|
||||
damage_taken: 3,
|
||||
outcome: "ongoing".to_string(),
|
||||
},
|
||||
})
|
||||
.expect("payload should serialize");
|
||||
|
||||
assert_eq!(payload["battleState"]["battleStateId"], json!("battle_1"));
|
||||
assert_eq!(
|
||||
payload["battleState"]["rewardItems"][0]["itemName"],
|
||||
json!("狼牙")
|
||||
);
|
||||
assert_eq!(payload["combat"]["damageDealt"], json!(10));
|
||||
assert!(payload["battleState"].get("chapterId").is_none());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user