Files
shinespark/src/match_runtime.rs
T
EIREXE 1276562890
CI / build-and-test (push) Successful in 1m17s
CI / production-build (push) Successful in 9m37s
Rename pending match to match config and add match state (#51)
Reviewed-on: #51
Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
2026-09-20 11:13:15 +00:00

103 lines
2.7 KiB
Rust

//! Rollback-simulated state for an active match.
use bevy::prelude::*;
use bevy_ggrs::prelude::*;
use crate::match_loading::{MatchConfig, MatchRules};
#[derive(Resource, Clone, Debug, Hash, PartialEq, Eq)]
pub struct MatchState {
pub elapsed_frames: u32,
/// Indexed by the player's session handle.
pub players: Vec<PlayerMatchState>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct PlayerMatchState {
pub stocks_remaining: Option<u8>,
pub score: i32,
pub deaths: u32,
}
impl MatchState {
pub fn from_config(config: &MatchConfig) -> Self {
let stocks_remaining = match &config.rules {
MatchRules::Stock {
starting_stocks, ..
} => Some(*starting_stocks),
MatchRules::Timed { .. } => None,
};
Self {
elapsed_frames: 0,
players: (0..config.players.len())
.map(|_| PlayerMatchState {
stocks_remaining,
score: 0,
deaths: 0,
})
.collect(),
}
}
}
pub struct MatchRuntimePlugin;
impl Plugin for MatchRuntimePlugin {
fn build(&self, app: &mut App) {
app.rollback_resource_with_clone::<MatchState>()
.checksum_resource_with_hash::<MatchState>();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{fighter::FighterId, match_loading::MatchPlayer, stage::manifest::StageId};
fn config(rules: MatchRules) -> MatchConfig {
MatchConfig {
stage: StageId::TestStage,
players: vec![
MatchPlayer {
handle: 1,
fighter: FighterId::TestFighter,
},
MatchPlayer {
handle: 0,
fighter: FighterId::TestFighter,
},
],
rules,
}
}
#[test]
fn stock_state_starts_with_configured_stocks() {
let state = MatchState::from_config(&config(MatchRules::Stock {
starting_stocks: 4,
time_limit_frames: Some(60),
}));
assert_eq!(state.elapsed_frames, 0);
assert_eq!(state.players.len(), 2);
assert!(state.players.iter().all(|player| {
player.stocks_remaining == Some(4) && player.score == 0 && player.deaths == 0
}));
}
#[test]
fn timed_state_has_no_stock_counter() {
let state = MatchState::from_config(&config(MatchRules::Timed {
time_limit_frames: 60,
}));
assert!(
state
.players
.iter()
.all(|player| player.stocks_remaining.is_none())
);
}
}