430 lines
17 KiB
Rust
430 lines
17 KiB
Rust
use super::*;
|
|
pub use shared_contracts::jump_hop::{
|
|
JumpHopActionRequest, JumpHopActionResponse, JumpHopActionType, JumpHopCharacterAsset,
|
|
JumpHopDefaultCharacter, JumpHopDifficulty, JumpHopDraftResponse, JumpHopGalleryCardResponse,
|
|
JumpHopGalleryDetailResponse, JumpHopGalleryResponse, JumpHopGenerationStatus,
|
|
JumpHopJumpRequest, JumpHopJumpResponse, JumpHopJumpResult, JumpHopLastJump,
|
|
JumpHopLeaderboardEntry, JumpHopLeaderboardResponse, JumpHopPath, JumpHopPlatform,
|
|
JumpHopRestartRunRequest, JumpHopRunResponse, JumpHopRunStatus,
|
|
JumpHopRuntimeRunSnapshotResponse, JumpHopScoring, JumpHopSessionResponse,
|
|
JumpHopSessionSnapshotResponse, JumpHopStartRunRequest, JumpHopStylePreset, JumpHopTileAsset,
|
|
JumpHopTileType, JumpHopWorkDetailResponse, JumpHopWorkMutationResponse,
|
|
JumpHopWorkProfileResponse, JumpHopWorkSummaryResponse, JumpHopWorksResponse,
|
|
JumpHopWorkspaceCreateRequest,
|
|
};
|
|
|
|
pub(crate) fn map_jump_hop_agent_session_procedure_result(
|
|
result: JumpHopAgentSessionProcedureResult,
|
|
) -> Result<JumpHopSessionSnapshotResponse, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
let session = result
|
|
.session
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("jump hop agent session 快照"))?;
|
|
Ok(map_jump_hop_session_snapshot(session))
|
|
}
|
|
|
|
pub(crate) fn map_jump_hop_work_procedure_result(
|
|
result: JumpHopWorkProcedureResult,
|
|
) -> Result<JumpHopWorkProfileResponse, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
let work = result
|
|
.work
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("jump hop work 快照"))?;
|
|
map_jump_hop_work_snapshot(work)
|
|
}
|
|
|
|
pub(crate) fn map_jump_hop_works_procedure_result(
|
|
result: JumpHopWorksProcedureResult,
|
|
) -> Result<Vec<JumpHopWorkProfileResponse>, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
result
|
|
.items
|
|
.into_iter()
|
|
.map(map_jump_hop_work_snapshot)
|
|
.collect()
|
|
}
|
|
|
|
pub(crate) fn map_jump_hop_run_procedure_result(
|
|
result: JumpHopRunProcedureResult,
|
|
) -> Result<JumpHopRuntimeRunSnapshotResponse, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
let run = result
|
|
.run
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("jump hop run 快照"))?;
|
|
Ok(map_jump_hop_run_snapshot(run))
|
|
}
|
|
|
|
pub(crate) fn map_jump_hop_leaderboard_procedure_result(
|
|
result: JumpHopLeaderboardProcedureResult,
|
|
) -> Result<JumpHopLeaderboardResponse, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
Ok(JumpHopLeaderboardResponse {
|
|
profile_id: result.profile_id,
|
|
items: result
|
|
.items
|
|
.into_iter()
|
|
.map(map_jump_hop_leaderboard_entry_snapshot)
|
|
.collect(),
|
|
viewer_best: result
|
|
.viewer_best
|
|
.map(map_jump_hop_leaderboard_entry_snapshot),
|
|
})
|
|
}
|
|
|
|
pub(crate) fn map_jump_hop_gallery_card_view_row(
|
|
row: JumpHopGalleryCardViewRow,
|
|
) -> JumpHopGalleryCardResponse {
|
|
let theme_text = if row.theme_text.trim().is_empty() {
|
|
row.work_title.clone()
|
|
} else {
|
|
row.theme_text.clone()
|
|
};
|
|
JumpHopGalleryCardResponse {
|
|
public_work_code: row.public_work_code,
|
|
work_id: row.work_id,
|
|
profile_id: row.profile_id,
|
|
owner_user_id: row.owner_user_id,
|
|
author_display_name: row.author_display_name,
|
|
theme_text,
|
|
work_title: row.work_title,
|
|
work_description: row.work_description,
|
|
cover_image_src: empty_string_to_none(row.cover_image_src),
|
|
theme_tags: row.theme_tags,
|
|
difficulty: parse_difficulty(&row.difficulty),
|
|
style_preset: parse_style_preset(&row.style_preset),
|
|
publication_status: normalize_publication_status(&row.publication_status).to_string(),
|
|
play_count: row.play_count,
|
|
updated_at: format_timestamp_micros(row.updated_at_micros),
|
|
published_at: row.published_at_micros.map(format_timestamp_micros),
|
|
generation_status: parse_generation_status(&row.generation_status),
|
|
}
|
|
}
|
|
|
|
fn map_jump_hop_session_snapshot(
|
|
snapshot: JumpHopAgentSessionSnapshot,
|
|
) -> JumpHopSessionSnapshotResponse {
|
|
JumpHopSessionSnapshotResponse {
|
|
session_id: snapshot.session_id,
|
|
owner_user_id: snapshot.owner_user_id,
|
|
status: snapshot
|
|
.draft
|
|
.as_ref()
|
|
.map(|draft| parse_generation_status(&draft.generation_status))
|
|
.unwrap_or(JumpHopGenerationStatus::Draft),
|
|
draft: snapshot.draft.map(map_jump_hop_draft_snapshot),
|
|
created_at: format_timestamp_micros(snapshot.created_at_micros),
|
|
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
|
|
}
|
|
}
|
|
|
|
fn map_jump_hop_work_snapshot(
|
|
snapshot: JumpHopWorkSnapshot,
|
|
) -> Result<JumpHopWorkProfileResponse, SpacetimeClientError> {
|
|
let theme_text = if snapshot.theme_text.trim().is_empty() {
|
|
snapshot.work_title.clone()
|
|
} else {
|
|
snapshot.theme_text.clone()
|
|
};
|
|
let draft = JumpHopDraftResponse {
|
|
template_id: "jump-hop".to_string(),
|
|
template_name: "跳一跳".to_string(),
|
|
profile_id: Some(snapshot.profile_id.clone()),
|
|
theme_text: theme_text.clone(),
|
|
work_title: snapshot.work_title.clone(),
|
|
work_description: snapshot.work_description.clone(),
|
|
theme_tags: snapshot.theme_tags.clone(),
|
|
difficulty: parse_difficulty(&snapshot.difficulty),
|
|
style_preset: parse_style_preset(&snapshot.style_preset),
|
|
default_character: Some(default_jump_hop_character()),
|
|
character_prompt: snapshot.character_prompt.clone(),
|
|
tile_prompt: snapshot.tile_prompt.clone(),
|
|
end_mood_prompt: snapshot.end_mood_prompt.clone(),
|
|
character_asset: snapshot.character_asset.clone().map(map_character_asset),
|
|
tile_atlas_asset: snapshot.tile_atlas_asset.clone().map(map_character_asset),
|
|
tile_assets: snapshot
|
|
.tile_assets
|
|
.clone()
|
|
.into_iter()
|
|
.map(map_tile_asset)
|
|
.collect(),
|
|
path: Some(map_jump_hop_path(snapshot.path.clone())),
|
|
cover_composite: snapshot.cover_composite.clone(),
|
|
generation_status: parse_generation_status(&snapshot.generation_status),
|
|
};
|
|
let character_asset = draft
|
|
.character_asset
|
|
.clone()
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("jump hop character asset"))?;
|
|
let tile_atlas_asset = draft
|
|
.tile_atlas_asset
|
|
.clone()
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("jump hop tile atlas asset"))?;
|
|
Ok(JumpHopWorkProfileResponse {
|
|
summary: JumpHopWorkSummaryResponse {
|
|
runtime_kind: "jump-hop".to_string(),
|
|
work_id: snapshot.work_id,
|
|
profile_id: snapshot.profile_id,
|
|
owner_user_id: snapshot.owner_user_id,
|
|
source_session_id: empty_string_to_none(snapshot.source_session_id),
|
|
theme_text,
|
|
work_title: snapshot.work_title,
|
|
work_description: snapshot.work_description,
|
|
theme_tags: snapshot.theme_tags,
|
|
difficulty: parse_difficulty(&snapshot.difficulty),
|
|
style_preset: parse_style_preset(&snapshot.style_preset),
|
|
cover_image_src: empty_string_to_none(snapshot.cover_image_src),
|
|
publication_status: normalize_publication_status(&snapshot.publication_status)
|
|
.to_string(),
|
|
play_count: snapshot.play_count,
|
|
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
|
|
published_at: snapshot.published_at_micros.map(format_timestamp_micros),
|
|
publish_ready: snapshot.publish_ready,
|
|
generation_status: parse_generation_status(&snapshot.generation_status),
|
|
},
|
|
draft,
|
|
path: map_jump_hop_path(snapshot.path),
|
|
default_character: Some(default_jump_hop_character()),
|
|
character_asset,
|
|
tile_atlas_asset,
|
|
tile_assets: snapshot
|
|
.tile_assets
|
|
.into_iter()
|
|
.map(map_tile_asset)
|
|
.collect(),
|
|
})
|
|
}
|
|
|
|
fn map_jump_hop_draft_snapshot(snapshot: JumpHopDraftSnapshot) -> JumpHopDraftResponse {
|
|
let theme_text = if snapshot.theme_text.trim().is_empty() {
|
|
snapshot.work_title.clone()
|
|
} else {
|
|
snapshot.theme_text.clone()
|
|
};
|
|
JumpHopDraftResponse {
|
|
template_id: snapshot.template_id,
|
|
template_name: snapshot.template_name,
|
|
profile_id: snapshot.profile_id,
|
|
theme_text,
|
|
work_title: snapshot.work_title,
|
|
work_description: snapshot.work_description,
|
|
theme_tags: snapshot.theme_tags,
|
|
difficulty: parse_difficulty(&snapshot.difficulty),
|
|
style_preset: parse_style_preset(&snapshot.style_preset),
|
|
default_character: Some(default_jump_hop_character()),
|
|
character_prompt: snapshot.character_prompt,
|
|
tile_prompt: snapshot.tile_prompt,
|
|
end_mood_prompt: snapshot.end_mood_prompt,
|
|
character_asset: snapshot.character_asset.map(map_character_asset),
|
|
tile_atlas_asset: snapshot.tile_atlas_asset.map(map_character_asset),
|
|
tile_assets: snapshot
|
|
.tile_assets
|
|
.into_iter()
|
|
.map(map_tile_asset)
|
|
.collect(),
|
|
path: snapshot.path.map(map_jump_hop_path),
|
|
cover_composite: snapshot.cover_composite,
|
|
generation_status: parse_generation_status(&snapshot.generation_status),
|
|
}
|
|
}
|
|
|
|
fn map_character_asset(snapshot: JumpHopCharacterAssetSnapshot) -> JumpHopCharacterAsset {
|
|
JumpHopCharacterAsset {
|
|
asset_id: snapshot.asset_id,
|
|
image_src: snapshot.image_src,
|
|
image_object_key: snapshot.image_object_key,
|
|
asset_object_id: snapshot.asset_object_id,
|
|
generation_provider: snapshot.generation_provider,
|
|
prompt: snapshot.prompt,
|
|
width: snapshot.width,
|
|
height: snapshot.height,
|
|
}
|
|
}
|
|
|
|
fn map_tile_asset(snapshot: JumpHopTileAssetSnapshot) -> JumpHopTileAsset {
|
|
JumpHopTileAsset {
|
|
tile_type: parse_tile_type(&snapshot.tile_type),
|
|
tile_id: snapshot.tile_id,
|
|
image_src: snapshot.image_src,
|
|
image_object_key: snapshot.image_object_key,
|
|
asset_object_id: snapshot.asset_object_id,
|
|
source_atlas_cell: snapshot.source_atlas_cell,
|
|
atlas_row: snapshot.atlas_row,
|
|
atlas_col: snapshot.atlas_col,
|
|
visual_width: snapshot.visual_width,
|
|
visual_height: snapshot.visual_height,
|
|
top_surface_radius: snapshot.top_surface_radius,
|
|
landing_radius: snapshot.landing_radius,
|
|
}
|
|
}
|
|
|
|
fn map_jump_hop_path(snapshot: crate::module_bindings::JumpHopPath) -> JumpHopPath {
|
|
JumpHopPath {
|
|
seed: snapshot.seed,
|
|
difficulty: parse_domain_difficulty(snapshot.difficulty),
|
|
platforms: snapshot
|
|
.platforms
|
|
.into_iter()
|
|
.map(|platform| JumpHopPlatform {
|
|
platform_id: platform.platform_id,
|
|
tile_type: parse_domain_tile_type(platform.tile_type),
|
|
x: platform.x,
|
|
y: platform.y,
|
|
width: platform.width,
|
|
height: platform.height,
|
|
landing_radius: platform.landing_radius,
|
|
perfect_radius: platform.perfect_radius,
|
|
score_value: platform.score_value,
|
|
})
|
|
.collect(),
|
|
finish_index: snapshot.finish_index,
|
|
camera_preset: snapshot.camera_preset,
|
|
scoring: JumpHopScoring {
|
|
charge_to_distance_ratio: snapshot.scoring.charge_to_distance_ratio,
|
|
max_charge_ms: snapshot.scoring.max_charge_ms,
|
|
hit_bonus: snapshot.scoring.hit_bonus,
|
|
perfect_bonus: snapshot.scoring.perfect_bonus,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn map_jump_hop_run_snapshot(snapshot: JumpHopRunSnapshot) -> JumpHopRuntimeRunSnapshotResponse {
|
|
JumpHopRuntimeRunSnapshotResponse {
|
|
run_id: snapshot.run_id,
|
|
profile_id: snapshot.profile_id,
|
|
owner_user_id: snapshot.owner_user_id,
|
|
status: match snapshot.status {
|
|
crate::module_bindings::JumpHopRunStatus::Failed => JumpHopRunStatus::Failed,
|
|
crate::module_bindings::JumpHopRunStatus::Cleared => JumpHopRunStatus::Cleared,
|
|
crate::module_bindings::JumpHopRunStatus::Playing => JumpHopRunStatus::Playing,
|
|
},
|
|
current_platform_index: snapshot.current_platform_index,
|
|
successful_jump_count: snapshot.current_platform_index,
|
|
duration_ms: jump_hop_duration_ms(snapshot.started_at_ms, snapshot.finished_at_ms),
|
|
score: snapshot.score,
|
|
combo: snapshot.combo,
|
|
path: map_jump_hop_path(snapshot.path),
|
|
last_jump: snapshot.last_jump.map(|jump| JumpHopLastJump {
|
|
charge_ms: jump.charge_ms,
|
|
jump_distance: jump.jump_distance,
|
|
target_platform_index: jump.target_platform_index,
|
|
landed_x: jump.landed_x,
|
|
landed_y: jump.landed_y,
|
|
result: match jump.result {
|
|
crate::module_bindings::JumpHopJumpResultKind::Miss => JumpHopJumpResult::Miss,
|
|
crate::module_bindings::JumpHopJumpResultKind::Hit => JumpHopJumpResult::Hit,
|
|
crate::module_bindings::JumpHopJumpResultKind::Finish => JumpHopJumpResult::Finish,
|
|
crate::module_bindings::JumpHopJumpResultKind::Perfect => {
|
|
JumpHopJumpResult::Perfect
|
|
}
|
|
},
|
|
}),
|
|
started_at_ms: snapshot.started_at_ms,
|
|
finished_at_ms: snapshot.finished_at_ms,
|
|
}
|
|
}
|
|
|
|
fn map_jump_hop_leaderboard_entry_snapshot(
|
|
snapshot: JumpHopLeaderboardEntrySnapshot,
|
|
) -> JumpHopLeaderboardEntry {
|
|
JumpHopLeaderboardEntry {
|
|
rank: snapshot.rank,
|
|
player_id: snapshot.player_id,
|
|
successful_jump_count: snapshot.successful_jump_count,
|
|
duration_ms: snapshot.duration_ms,
|
|
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
|
|
}
|
|
}
|
|
|
|
fn default_jump_hop_character() -> JumpHopDefaultCharacter {
|
|
JumpHopDefaultCharacter {
|
|
character_id: "jump-hop-default-runner".to_string(),
|
|
display_name: "默认角色".to_string(),
|
|
model_kind: "builtin-three".to_string(),
|
|
body_color: "#f59e0b".to_string(),
|
|
accent_color: "#2563eb".to_string(),
|
|
}
|
|
}
|
|
|
|
fn jump_hop_duration_ms(started_at_ms: u64, finished_at_ms: Option<u64>) -> u64 {
|
|
finished_at_ms
|
|
.unwrap_or(started_at_ms)
|
|
.saturating_sub(started_at_ms)
|
|
}
|
|
|
|
fn parse_difficulty(value: &str) -> JumpHopDifficulty {
|
|
match value {
|
|
"easy" => JumpHopDifficulty::Easy,
|
|
"advanced" => JumpHopDifficulty::Advanced,
|
|
"challenge" => JumpHopDifficulty::Challenge,
|
|
_ => JumpHopDifficulty::Standard,
|
|
}
|
|
}
|
|
|
|
fn parse_domain_difficulty(value: crate::module_bindings::JumpHopDifficulty) -> JumpHopDifficulty {
|
|
match value {
|
|
crate::module_bindings::JumpHopDifficulty::Easy => JumpHopDifficulty::Easy,
|
|
crate::module_bindings::JumpHopDifficulty::Advanced => JumpHopDifficulty::Advanced,
|
|
crate::module_bindings::JumpHopDifficulty::Challenge => JumpHopDifficulty::Challenge,
|
|
crate::module_bindings::JumpHopDifficulty::Standard => JumpHopDifficulty::Standard,
|
|
}
|
|
}
|
|
|
|
fn parse_style_preset(value: &str) -> JumpHopStylePreset {
|
|
match value {
|
|
"paper-toy" => JumpHopStylePreset::PaperToy,
|
|
"neon-glass" => JumpHopStylePreset::NeonGlass,
|
|
"forest-stone" => JumpHopStylePreset::ForestStone,
|
|
"future-metal" => JumpHopStylePreset::FutureMetal,
|
|
"custom" => JumpHopStylePreset::Custom,
|
|
_ => JumpHopStylePreset::MinimalBlocks,
|
|
}
|
|
}
|
|
|
|
fn parse_tile_type(value: &str) -> JumpHopTileType {
|
|
match value {
|
|
"start" => JumpHopTileType::Start,
|
|
"target" => JumpHopTileType::Target,
|
|
"finish" => JumpHopTileType::Finish,
|
|
"bonus" => JumpHopTileType::Bonus,
|
|
"accent" => JumpHopTileType::Accent,
|
|
_ => JumpHopTileType::Normal,
|
|
}
|
|
}
|
|
|
|
fn parse_domain_tile_type(value: crate::module_bindings::JumpHopTileType) -> JumpHopTileType {
|
|
match value {
|
|
crate::module_bindings::JumpHopTileType::Start => JumpHopTileType::Start,
|
|
crate::module_bindings::JumpHopTileType::Target => JumpHopTileType::Target,
|
|
crate::module_bindings::JumpHopTileType::Finish => JumpHopTileType::Finish,
|
|
crate::module_bindings::JumpHopTileType::Bonus => JumpHopTileType::Bonus,
|
|
crate::module_bindings::JumpHopTileType::Accent => JumpHopTileType::Accent,
|
|
crate::module_bindings::JumpHopTileType::Normal => JumpHopTileType::Normal,
|
|
}
|
|
}
|
|
|
|
fn parse_generation_status(value: &str) -> JumpHopGenerationStatus {
|
|
match value {
|
|
"generating" => JumpHopGenerationStatus::Generating,
|
|
"ready" => JumpHopGenerationStatus::Ready,
|
|
"failed" => JumpHopGenerationStatus::Failed,
|
|
_ => JumpHopGenerationStatus::Draft,
|
|
}
|
|
}
|
|
|
|
fn normalize_publication_status(value: &str) -> &str {
|
|
match value {
|
|
"Published" | "published" => "published",
|
|
_ => "draft",
|
|
}
|
|
}
|