werewolves/werewolves-proto/src/player.rs

329 lines
10 KiB
Rust

use core::{fmt::Display, num::NonZeroU8};
use serde::{Deserialize, Serialize};
use crate::{
diedto::DiedTo,
error::GameError,
game::{DateTime, Village},
message::{CharacterIdentity, Identification, PublicIdentity, night::ActionPrompt},
modifier::Modifier,
role::{MAPLE_WOLF_ABSTAIN_LIMIT, PreviousGuardianAction, Role, RoleTitle},
};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct PlayerId(uuid::Uuid);
impl PlayerId {
pub fn new() -> Self {
Self(uuid::Uuid::new_v4())
}
pub const fn from_u128(v: u128) -> Self {
Self(uuid::Uuid::from_u128(v))
}
}
impl Display for PlayerId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct CharacterId(uuid::Uuid);
impl CharacterId {
pub fn new() -> Self {
Self(uuid::Uuid::new_v4())
}
pub const fn from_u128(v: u128) -> Self {
Self(uuid::Uuid::from_u128(v))
}
}
impl Display for CharacterId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Player {
id: PlayerId,
name: String,
}
impl Player {
pub fn new(name: String) -> Self {
Self {
id: PlayerId::new(),
name,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Protection {
Guardian { source: CharacterId, guarding: bool },
Protector { source: CharacterId },
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum KillOutcome {
Killed,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Character {
player_id: PlayerId,
identity: CharacterIdentity,
role: Role,
modifier: Option<Modifier>,
died_to: Option<DiedTo>,
role_changes: Vec<RoleChange>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoleChange {
role: Role,
new_role: RoleTitle,
changed_on_night: u8,
}
impl Character {
pub fn new(
Identification {
player_id,
public:
PublicIdentity {
name,
pronouns,
number,
},
}: Identification,
role: Role,
) -> Option<Self> {
Some(Self {
role,
identity: CharacterIdentity {
character_id: CharacterId::new(),
name,
pronouns,
number: number?,
},
player_id,
modifier: None,
died_to: None,
role_changes: Vec::new(),
})
}
pub fn identity(&self) -> CharacterIdentity {
self.identity.clone()
}
pub fn name(&self) -> &str {
self.identity.name.as_str()
}
pub const fn number(&self) -> NonZeroU8 {
self.identity.number
}
pub const fn pronouns(&self) -> Option<&str> {
match self.identity.pronouns.as_ref() {
Some(p) => Some(p.as_str()),
None => None,
}
}
pub fn died_to(&self) -> Option<&DiedTo> {
self.died_to.as_ref()
}
pub fn kill(&mut self, died_to: DiedTo) {
match &self.died_to {
Some(_) => {}
None => self.died_to = Some(died_to),
}
}
pub const fn alive(&self) -> bool {
self.died_to.is_none()
}
pub fn execute(&mut self, day: NonZeroU8) -> Result<(), GameError> {
if self.died_to.is_some() {
return Err(GameError::CharacterAlreadyDead);
}
self.died_to = Some(DiedTo::Execution { day });
Ok(())
}
pub const fn character_id(&self) -> &CharacterId {
&self.identity.character_id
}
pub const fn player_id(&self) -> &PlayerId {
&self.player_id
}
pub const fn role(&self) -> &Role {
&self.role
}
pub const fn role_mut(&mut self) -> &mut Role {
&mut self.role
}
pub fn role_change(&mut self, new_role: RoleTitle, at: DateTime) -> Result<(), GameError> {
let mut role = new_role.title_to_role_excl_apprentice();
core::mem::swap(&mut role, &mut self.role);
self.role_changes.push(RoleChange {
role,
new_role,
changed_on_night: match at {
DateTime::Day { number: _ } => return Err(GameError::NotNight),
DateTime::Night { number } => number,
},
});
Ok(())
}
pub const fn is_wolf(&self) -> bool {
self.role.wolf()
}
pub const fn is_village(&self) -> bool {
!self.is_wolf()
}
pub fn night_action_prompt(
&self,
village: &Village,
) -> Result<Option<ActionPrompt>, GameError> {
if !self.alive() || !self.role.wakes(village) {
return Ok(None);
}
let night = match village.date_time() {
DateTime::Day { number: _ } => return Err(GameError::NotNight),
DateTime::Night { number } => number,
};
Ok(Some(match &self.role {
Role::Shapeshifter {
shifted_into: Some(_),
}
| Role::AlphaWolf { killed: Some(_) }
| Role::Militia { targeted: Some(_) }
| Role::Scapegoat
| Role::Villager => return Ok(None),
Role::Seer => ActionPrompt::Seer {
character_id: self.identity(),
living_players: village.living_players_excluding(self.character_id()),
},
Role::Arcanist => ActionPrompt::Arcanist {
character_id: self.identity(),
living_players: village.living_players_excluding(self.character_id()),
},
Role::Protector {
last_protected: Some(last_protected),
} => ActionPrompt::Protector {
character_id: self.identity(),
targets: village.living_players_excluding(last_protected),
},
Role::Protector {
last_protected: None,
} => ActionPrompt::Protector {
character_id: self.identity(),
targets: village.living_players_excluding(self.character_id()),
},
Role::Apprentice(role) => {
let current_night = match village.date_time() {
DateTime::Day { number: _ } => return Ok(None),
DateTime::Night { number } => number,
};
return Ok(village
.characters()
.into_iter()
.filter(|c| c.role().title() == role.title())
.filter_map(|char| char.died_to)
.any(|died_to| match died_to.date_time() {
DateTime::Day { number } => number.get() + 1 >= current_night,
DateTime::Night { number } => number + 1 >= current_night,
})
.then(|| ActionPrompt::RoleChange {
character_id: self.identity(),
new_role: role.title(),
}));
}
Role::Elder { knows_on_night } => {
let current_night = match village.date_time() {
DateTime::Day { number: _ } => return Ok(None),
DateTime::Night { number } => number,
};
return Ok((current_night == knows_on_night.get()).then_some({
ActionPrompt::RoleChange {
character_id: self.identity(),
new_role: RoleTitle::Elder,
}
}));
}
Role::Militia { targeted: None } => ActionPrompt::Militia {
character_id: self.identity(),
living_players: village.living_players_excluding(self.character_id()),
},
Role::Werewolf => ActionPrompt::WolfPackKill {
living_villagers: village.living_players(),
},
Role::AlphaWolf { killed: None } => ActionPrompt::AlphaWolf {
character_id: self.identity(),
living_villagers: village.living_players_excluding(self.character_id()),
},
Role::DireWolf => ActionPrompt::DireWolf {
character_id: self.identity(),
living_players: village.living_players(),
},
Role::Shapeshifter { shifted_into: None } => ActionPrompt::Shapeshifter {
character_id: self.identity(),
},
Role::Gravedigger => ActionPrompt::Gravedigger {
character_id: self.identity(),
dead_players: village.dead_targets(),
},
Role::Hunter { target } => ActionPrompt::Hunter {
character_id: self.identity(),
current_target: target.as_ref().and_then(|t| village.target_by_id(t)),
living_players: village.living_players_excluding(self.character_id()),
},
Role::MapleWolf { last_kill_on_night } => ActionPrompt::MapleWolf {
character_id: self.identity(),
kill_or_die: last_kill_on_night + MAPLE_WOLF_ABSTAIN_LIMIT.get() == night,
living_players: village.living_players_excluding(self.character_id()),
},
Role::Guardian {
last_protected: Some(PreviousGuardianAction::Guard(prev_target)),
} => ActionPrompt::Guardian {
character_id: self.identity(),
previous: Some(PreviousGuardianAction::Guard(prev_target.clone())),
living_players: village.living_players_excluding(&prev_target.character_id),
},
Role::Guardian {
last_protected: Some(PreviousGuardianAction::Protect(prev_target)),
} => ActionPrompt::Guardian {
character_id: self.identity(),
previous: Some(PreviousGuardianAction::Protect(prev_target.clone())),
living_players: village.living_players(),
},
Role::Guardian {
last_protected: None,
} => ActionPrompt::Guardian {
character_id: self.identity(),
previous: None,
living_players: village.living_players(),
},
}))
}
}