use serde::{Deserialize, Serialize}; pub const BARK_BATTLE_RULESET_VERSION_V1: &str = "bark-battle-ruleset-v1"; #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum DifficultyPreset { Easy, Normal, Hard, } impl DifficultyPreset { pub fn ai_preset_key(self) -> &'static str { match self { Self::Easy => "bark-battle-ai-easy", Self::Normal => "bark-battle-ai-normal", Self::Hard => "bark-battle-ai-hard", } } } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct RulesetThresholdsSignature { pub standard_duration_ms: u64, pub min_duration_ms: u64, pub max_duration_ms: u64, pub min_bark_gap_ms: u64, pub trigger_count_tolerance: u32, pub min_volume: f32, pub max_volume: f32, pub min_average_volume: f32, pub max_average_volume: f32, pub min_final_energy: f32, pub max_final_energy: f32, pub min_combo: u32, pub max_combo: u32, pub draw_threshold_energy: u32, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct BarkBattleRuleset { pub version: &'static str, pub difficulty: DifficultyPreset, pub ai_preset_key: &'static str, pub standard_duration_ms: u64, pub min_duration_ms: u64, pub max_duration_ms: u64, pub min_bark_gap_ms: u64, pub trigger_count_tolerance: u32, pub min_volume: f32, pub max_volume: f32, pub min_average_volume: f32, pub max_average_volume: f32, pub min_final_energy: f32, pub max_final_energy: f32, pub min_combo: u32, pub max_combo: u32, pub draw_threshold_energy: u32, } impl BarkBattleRuleset { pub fn v1() -> Self { Self::for_difficulty(DifficultyPreset::Normal) } pub fn for_difficulty(difficulty: DifficultyPreset) -> Self { Self { version: BARK_BATTLE_RULESET_VERSION_V1, difficulty, ai_preset_key: difficulty.ai_preset_key(), standard_duration_ms: 30_000, min_duration_ms: 28_000, max_duration_ms: 35_000, min_bark_gap_ms: 250, trigger_count_tolerance: 2, min_volume: 0.0, max_volume: 1.0, min_average_volume: 0.0, max_average_volume: 1.0, min_final_energy: 0.0, max_final_energy: 100.0, min_combo: 0, max_combo: 999, draw_threshold_energy: 3, } } pub fn thresholds_signature(&self) -> RulesetThresholdsSignature { RulesetThresholdsSignature { standard_duration_ms: self.standard_duration_ms, min_duration_ms: self.min_duration_ms, max_duration_ms: self.max_duration_ms, min_bark_gap_ms: self.min_bark_gap_ms, trigger_count_tolerance: self.trigger_count_tolerance, min_volume: self.min_volume, max_volume: self.max_volume, min_average_volume: self.min_average_volume, max_average_volume: self.max_average_volume, min_final_energy: self.min_final_energy, max_final_energy: self.max_final_energy, min_combo: self.min_combo, max_combo: self.max_combo, draw_threshold_energy: self.draw_threshold_energy, } } } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct BarkBattleFinishMetrics { pub duration_ms: u64, pub trigger_count: u64, /// 归一化音量,合法范围为 0.0..=1.0。 pub max_volume: f32, pub average_volume: f32, pub final_energy: f32, pub max_combo: u32, pub finished_at_micros: i64, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum FinishValidationDecision { Accepted, AcceptedWithFlags, Rejected, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum AntiCheatFlag { DurationTooShort, DurationTooLong, TriggerCountTooHigh, MaxVolumeOutOfRange, AverageVolumeOutOfRange, FinalEnergyOutOfRange, MaxComboOutOfRange, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct FinishValidation { pub decision: FinishValidationDecision, pub anti_cheat_flags: Vec, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum BattleResult { PlayerWin, OpponentWin, Draw, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct BarkBattleLeaderboardScore { pub final_energy_millis: u32, pub trigger_count: u64, pub max_volume_millis: u32, pub duration_closeness_ms: u64, pub finished_at_micros: i64, }