133 lines
3.7 KiB
Rust
133 lines
3.7 KiB
Rust
use bevy::prelude::*;
|
|
use thiserror::Error;
|
|
|
|
use crate::{
|
|
fighter::FighterId,
|
|
match_loading::{MatchConfig, MatchPlayer, MatchRules},
|
|
stage::manifest::StageId,
|
|
};
|
|
|
|
pub struct MenuMatchPlayerSlot {
|
|
pub slot: usize,
|
|
pub fighter: Option<FighterId>,
|
|
}
|
|
|
|
#[derive(Resource, Default)]
|
|
pub struct MenuMatchConfig {
|
|
pub stage: Option<StageId>,
|
|
pub players: Vec<MenuMatchPlayerSlot>,
|
|
}
|
|
|
|
#[derive(Error, Debug, Reflect)]
|
|
pub enum MenuMatchConfigErrors {
|
|
#[error("Fighter slot {0} was invalid!")]
|
|
PlayerSlotInvalid(usize),
|
|
#[error("Invalid configuration: Slot {0} doesn't have a fighter selected")]
|
|
FighterNotSelectedForSlot(usize),
|
|
#[error("Invalid configuration: No stage selected")]
|
|
StageNotSelected,
|
|
}
|
|
|
|
impl MenuMatchConfig {
|
|
pub fn set_player_fighter(
|
|
&mut self,
|
|
player_slot: usize,
|
|
fighter_id: Option<FighterId>,
|
|
) -> Result<(), MenuMatchConfigErrors> {
|
|
let slot = self
|
|
.players
|
|
.get_mut(player_slot)
|
|
.ok_or(MenuMatchConfigErrors::PlayerSlotInvalid(player_slot))?;
|
|
slot.fighter = fighter_id;
|
|
Ok(())
|
|
}
|
|
pub fn get_player_fighter(
|
|
&self,
|
|
player_slot: usize,
|
|
) -> Result<Option<FighterId>, MenuMatchConfigErrors> {
|
|
let slot = self
|
|
.players
|
|
.get(player_slot)
|
|
.ok_or(MenuMatchConfigErrors::PlayerSlotInvalid(player_slot))?;
|
|
Ok(slot.fighter)
|
|
}
|
|
|
|
pub fn all_players_ready(&self) -> bool {
|
|
let has_unselected_fighter = self
|
|
.players
|
|
.iter()
|
|
.any(|player_slot| player_slot.fighter.is_none());
|
|
!has_unselected_fighter
|
|
}
|
|
|
|
pub fn to_match_config(&self) -> Result<MatchConfig, MenuMatchConfigErrors> {
|
|
let mut match_players = vec![];
|
|
|
|
let stage = self.stage.ok_or(MenuMatchConfigErrors::StageNotSelected)?;
|
|
|
|
let mut players = self.players.iter().collect::<Vec<_>>();
|
|
players.sort_unstable_by_key(|player| player.slot);
|
|
|
|
for (handle, player) in players.into_iter().enumerate() {
|
|
let fighter_id =
|
|
player
|
|
.fighter
|
|
.ok_or(MenuMatchConfigErrors::FighterNotSelectedForSlot(
|
|
player.slot,
|
|
))?;
|
|
match_players.push(MatchPlayer {
|
|
fighter: fighter_id,
|
|
handle,
|
|
controller_port: player.slot,
|
|
});
|
|
}
|
|
|
|
Ok(MatchConfig {
|
|
players: match_players,
|
|
stage,
|
|
rules: MatchRules::default(),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn menu_config_uses_default_match_rules() {
|
|
let menu = MenuMatchConfig {
|
|
stage: Some(StageId::TestStage),
|
|
players: vec![MenuMatchPlayerSlot {
|
|
slot: 0,
|
|
fighter: Some(FighterId::TestFighter),
|
|
}],
|
|
};
|
|
|
|
assert_eq!(menu.to_match_config().unwrap().rules, MatchRules::default());
|
|
}
|
|
|
|
#[test]
|
|
fn sparse_controller_ports_get_dense_ggrs_handles() {
|
|
let menu = MenuMatchConfig {
|
|
stage: Some(StageId::TestStage),
|
|
players: vec![
|
|
MenuMatchPlayerSlot {
|
|
slot: 3,
|
|
fighter: Some(FighterId::TestFighter),
|
|
},
|
|
MenuMatchPlayerSlot {
|
|
slot: 0,
|
|
fighter: Some(FighterId::TestFighter),
|
|
},
|
|
],
|
|
};
|
|
|
|
let config = menu.to_match_config().unwrap();
|
|
assert_eq!(config.players[0].handle, 0);
|
|
assert_eq!(config.players[0].controller_port, 0);
|
|
assert_eq!(config.players[1].handle, 1);
|
|
assert_eq!(config.players[1].controller_port, 3);
|
|
}
|
|
}
|