use super::{Key, NoteChannelId, NoteKind, PlayerSide};
use Key::*;
fn key_layout_beat_to_channel_id(beat: KeyLayoutBeat) -> NoteChannelId {
let (side, kind, key) = beat.as_tuple();
let first_char = match (kind, side) {
(NoteKind::Visible, PlayerSide::Player1) => '1',
(NoteKind::Visible, PlayerSide::Player2) => '2',
(NoteKind::Invisible, PlayerSide::Player1) => '3',
(NoteKind::Invisible, PlayerSide::Player2) => '4',
(NoteKind::Long, PlayerSide::Player1) => '5',
(NoteKind::Long, PlayerSide::Player2) => '6',
(NoteKind::Landmine, PlayerSide::Player1) => 'D',
(NoteKind::Landmine, PlayerSide::Player2) => 'E',
};
let second_char = match key {
Key::Key(key @ 1..=5) => char::from_digit(key as u32, 10).unwrap_or('0'),
Key::Scratch(1) => '6',
Key::FreeZone => '7',
Key::Key(key @ 6..=7) => char::from_digit((key + 2) as u32, 10).unwrap_or('0'),
_ => '0', };
NoteChannelId::try_from([first_char as u8, second_char as u8])
.expect("generated note channel id should be valid")
}
fn channel_id_to_key_layout_beat(channel_id: NoteChannelId) -> Option<KeyLayoutBeat> {
let [first_char, second_char] = channel_id.0.map(|c| c as char);
let (kind, side) = match first_char {
'1' => (NoteKind::Visible, PlayerSide::Player1),
'2' => (NoteKind::Visible, PlayerSide::Player2),
'3' => (NoteKind::Invisible, PlayerSide::Player1),
'4' => (NoteKind::Invisible, PlayerSide::Player2),
'5' => (NoteKind::Long, PlayerSide::Player1),
'6' => (NoteKind::Long, PlayerSide::Player2),
'D' => (NoteKind::Landmine, PlayerSide::Player1),
'E' => (NoteKind::Landmine, PlayerSide::Player2),
_ => return None,
};
let key = match second_char {
'1'..='5' => Key::Key(second_char as u8 - b'0'),
'6' => Key::Scratch(1),
'7' => Key::FreeZone,
'8'..='9' => Key::Key(second_char as u8 - b'0' - 2),
_ => return None,
};
Some(KeyLayoutBeat::new(side, kind, key))
}
pub trait KeyMapping {
fn new(side: PlayerSide, kind: NoteKind, key: Key) -> Self;
fn side(&self) -> PlayerSide;
fn kind(&self) -> NoteKind;
fn key(&self) -> Key;
#[must_use]
fn from_tuple(tuple: (PlayerSide, NoteKind, Key)) -> Self
where
Self: Sized,
{
Self::new(tuple.0, tuple.1, tuple.2)
}
fn as_tuple(&self) -> (PlayerSide, NoteKind, Key) {
(self.side(), self.kind(), self.key())
}
}
pub trait KeyLayoutMapper: KeyMapping {
fn to_channel_id(self) -> NoteChannelId;
fn from_channel_id(channel_id: NoteChannelId) -> Option<Self>
where
Self: Sized;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KeyLayoutBeat(pub PlayerSide, pub NoteKind, pub Key);
impl KeyMapping for KeyLayoutBeat {
fn new(side: PlayerSide, kind: NoteKind, key: Key) -> Self {
Self(side, kind, key)
}
fn side(&self) -> PlayerSide {
self.0
}
fn kind(&self) -> NoteKind {
self.1
}
fn key(&self) -> Key {
self.2
}
}
impl KeyLayoutMapper for KeyLayoutBeat {
fn to_channel_id(self) -> NoteChannelId {
key_layout_beat_to_channel_id(self)
}
fn from_channel_id(channel_id: NoteChannelId) -> Option<Self> {
channel_id_to_key_layout_beat(channel_id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KeyLayoutPmsBmeType(pub PlayerSide, pub NoteKind, pub Key);
impl KeyMapping for KeyLayoutPmsBmeType {
fn new(side: PlayerSide, kind: NoteKind, key: Key) -> Self {
Self(side, kind, key)
}
fn side(&self) -> PlayerSide {
self.0
}
fn kind(&self) -> NoteKind {
self.1
}
fn key(&self) -> Key {
self.2
}
}
impl KeyLayoutMapper for KeyLayoutPmsBmeType {
fn to_channel_id(self) -> NoteChannelId {
let (side, kind, key) = self.as_tuple();
let key = match key {
Key(8) => Scratch(1),
Key(9) => FreeZone,
other => other,
};
let beat = KeyLayoutBeat::new(side, kind, key);
key_layout_beat_to_channel_id(beat)
}
fn from_channel_id(channel_id: NoteChannelId) -> Option<Self> {
let beat = channel_id_to_key_layout_beat(channel_id)?;
let (side, kind, key) = beat.as_tuple();
let key = match key {
Scratch(1) => Key(8),
FreeZone => Key(9),
_ => key,
};
Some(Self::new(side, kind, key))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KeyLayoutPms(pub PlayerSide, pub NoteKind, pub Key);
impl KeyMapping for KeyLayoutPms {
fn new(side: PlayerSide, kind: NoteKind, key: Key) -> Self {
Self(side, kind, key)
}
fn side(&self) -> PlayerSide {
self.0
}
fn kind(&self) -> NoteKind {
self.1
}
fn key(&self) -> Key {
self.2
}
}
impl KeyLayoutMapper for KeyLayoutPms {
fn to_channel_id(self) -> NoteChannelId {
use PlayerSide::*;
let (side, kind, key) = self.as_tuple();
let (side, key) = match (side, key) {
(Player1, Key(1..=5)) => (Player1, key),
(Player1, Key(key_u8 @ 6..=9)) => (Player2, Key(key_u8 - 4)),
other => other,
};
let beat = KeyLayoutBeat::new(side, kind, key);
key_layout_beat_to_channel_id(beat)
}
fn from_channel_id(channel_id: NoteChannelId) -> Option<Self> {
use PlayerSide::*;
let beat = channel_id_to_key_layout_beat(channel_id)?;
let (side, kind, key) = beat.as_tuple();
let (side, key) = match (side, key) {
(Player1, Key(1..=5)) => (Player1, key),
(Player2, Key(key_u8 @ 2..=5)) => (Player1, Key(key_u8 + 4)),
other => other,
};
Some(Self::new(side, kind, key))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KeyLayoutBeatNanasi(pub PlayerSide, pub NoteKind, pub Key);
impl KeyMapping for KeyLayoutBeatNanasi {
fn new(side: PlayerSide, kind: NoteKind, key: Key) -> Self {
Self(side, kind, key)
}
fn side(&self) -> PlayerSide {
self.0
}
fn kind(&self) -> NoteKind {
self.1
}
fn key(&self) -> Key {
self.2
}
}
impl KeyLayoutMapper for KeyLayoutBeatNanasi {
fn to_channel_id(self) -> NoteChannelId {
let (side, kind, key) = self.as_tuple();
let key = if let FootPedal = key { FreeZone } else { key };
let beat = KeyLayoutBeat::new(side, kind, key);
key_layout_beat_to_channel_id(beat)
}
fn from_channel_id(channel_id: NoteChannelId) -> Option<Self> {
let beat = channel_id_to_key_layout_beat(channel_id)?;
let (side, kind, key) = beat.as_tuple();
let key = if let FreeZone = key { FootPedal } else { key };
Some(Self::new(side, kind, key))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KeyLayoutDscOctFp(pub PlayerSide, pub NoteKind, pub Key);
impl KeyMapping for KeyLayoutDscOctFp {
fn new(side: PlayerSide, kind: NoteKind, key: Key) -> Self {
Self(side, kind, key)
}
fn side(&self) -> PlayerSide {
self.0
}
fn kind(&self) -> NoteKind {
self.1
}
fn key(&self) -> Key {
self.2
}
}
impl KeyLayoutMapper for KeyLayoutDscOctFp {
fn to_channel_id(self) -> NoteChannelId {
use PlayerSide::*;
let (side, kind, key) = self.as_tuple();
let (side, key) = match (side, key) {
(Player1, Key(1..=7) | Scratch(1)) => (Player1, key),
(Player1, Scratch(2)) => (Player2, Scratch(1)),
(Player1, FootPedal) => (Player2, Key(1)),
(Player1, Key(key_u8 @ 8..=13)) => (Player2, Key(key_u8 - 6)),
(s, other) => (s, other),
};
let beat = KeyLayoutBeat::new(side, kind, key);
key_layout_beat_to_channel_id(beat)
}
fn from_channel_id(channel_id: NoteChannelId) -> Option<Self> {
use PlayerSide::*;
let beat = channel_id_to_key_layout_beat(channel_id)?;
let (side, kind, key) = beat.as_tuple();
let (side, key) = match (side, key) {
(Player1, Key(1..=7) | Scratch(1)) => (Player1, key),
(Player2, Scratch(1)) => (Player1, Scratch(2)),
(Player2, Key(1)) => (Player1, FootPedal),
(Player2, Key(key_u8 @ 2..=7)) => (Player1, Key(key_u8 + 6)),
(s, k) => (s, k),
};
Some(Self::new(side, kind, key))
}
}