use crate::constants::save_data::{SaveData, SaveDataArgs};
use crate::constants::status::Flags;
use crate::constants::status::Status;
use crate::constants::text::DEFAULT_NAME;
use crate::growth_type::{
GrowthModifiers, calculate_abc, calculate_name_total, get_adjusted_status_by_name_lv,
};
use crate::load::decode_from_password_string;
use crate::utility::status_utils::{get_level_by_exp, get_status_by_level, resolve_experience};
use crate::utility::string_utils::name_normalize;
#[derive(Debug)]
pub struct Player {
pub name: String,
pub hp: u16,
pub mp: u16,
pub exp: u16,
pub gold: u16,
pub weapon: u8,
pub armor: u8,
pub shield: u8,
pub items: [u8; 8],
pub herbs: u8,
pub keys: u8,
pub flags: Flags,
}
#[derive(Default)]
pub struct PlayerArgs {
pub name: Option<String>,
pub level: Option<u8>,
pub exp: Option<u16>,
pub gold: Option<u16>,
pub weapon: Option<u8>,
pub armor: Option<u8>,
pub shield: Option<u8>,
pub items: Option<[u8; 8]>,
pub herbs: Option<u8>,
pub keys: Option<u8>,
pub flags: Option<Flags>,
}
impl PlayerArgs {
pub fn from_save_data(save: &SaveData) -> Self {
Self {
name: Some(save.name.clone()),
exp: Some(save.experience),
gold: Some(save.gold),
weapon: Some(save.weapon),
armor: Some(save.armor),
shield: Some(save.shield),
items: Some(save.items),
herbs: Some(save.herbs),
keys: Some(save.keys),
flags: Some(Flags {
has_dragon_scale: save.flags.has_dragon_scale,
has_warrior_ring: save.flags.has_warrior_ring,
has_cursed_necklace: save.flags.has_cursed_necklace,
defeated_dragon: save.flags.defeated_dragon,
defeated_golem: save.flags.defeated_golem,
})
.into(),
..Default::default()
}
}
}
impl Player {
pub fn new(name: &str) -> Self {
Self::new_with(PlayerArgs {
name: Some(name.to_string()),
..Default::default()
})
}
pub fn new_with(args: PlayerArgs) -> Self {
let name = name_normalize(&args.name.unwrap_or_else(|| DEFAULT_NAME.to_string()));
let base_level = args.level.unwrap_or(1);
let final_exp = resolve_experience(base_level, args.exp);
let level = get_level_by_exp(final_exp);
let adjusted = get_adjusted_status_by_name_lv(&name, level);
Self {
name,
hp: adjusted.max_hp,
mp: adjusted.max_mp,
exp: final_exp,
gold: args.gold.unwrap_or(0),
weapon: args.weapon.unwrap_or(0),
armor: args.armor.unwrap_or(0),
shield: args.shield.unwrap_or(0),
items: args.items.unwrap_or([0; 8]),
herbs: args.herbs.unwrap_or(0),
keys: args.keys.unwrap_or(0),
flags: args.flags.unwrap_or_default(),
}
}
pub fn from_password_string(s: &str) -> Result<Self, String> {
let save = decode_from_password_string(s)?;
Ok(Self::from_save_data(&save))
}
pub fn from_save_data(save: &SaveData) -> Self {
Self::new_with(PlayerArgs::from_save_data(save))
}
pub fn level(&self) -> u8 {
get_level_by_exp(self.exp)
}
pub fn name_total(&self) -> u16 {
calculate_name_total(&self.name)
}
pub fn strength(&self) -> Option<u16> {
self.status().map(|s| s.strength)
}
pub fn agility(&self) -> Option<u16> {
self.status().map(|s| s.agility)
}
pub fn abc(&self) -> GrowthModifiers {
calculate_abc(self.name_total())
}
pub fn base_status(&self) -> Option<&Status> {
get_status_by_level(self.level())
}
pub fn status(&self) -> Option<Status> {
self.base_status()
.map(|base| base.apply_abc_modifiers(&self.abc()))
}
pub fn to_password_string(&self) -> Result<String, String> {
let save = SaveData::new_with(SaveDataArgs {
name: Some(self.name.clone()),
experience: Some(self.exp),
gold: Some(self.gold),
weapon: Some(self.weapon),
armor: Some(self.armor),
shield: Some(self.shield),
items: Some(self.items),
herbs: Some(self.herbs),
keys: Some(self.keys),
flags: Some(self.flags.clone()),
pattern: None,
..Default::default()
});
save.to_password_string()
}
pub fn maximize(&mut self) {
self.exp = 65535;
self.gold = 65535;
self.weapon = 7;
self.armor = 7;
self.shield = 3;
self.items = [4, 6, 7, 8, 10, 12, 13, 14]; self.herbs = 6;
self.keys = 6;
self.flags = Flags {
has_dragon_scale: true,
has_warrior_ring: true,
has_cursed_necklace: true,
defeated_dragon: true,
defeated_golem: true,
};
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_player_default_level() {
let player = Player::new_with(PlayerArgs {
name: Some(DEFAULT_NAME.to_string()),
..Default::default()
});
assert_eq!(player.name, DEFAULT_NAME);
assert_eq!(player.level(), 1);
assert_eq!(player.exp, 0);
assert!(player.hp > 0);
}
#[test]
fn test_player_with_level() {
let player = Player::new_with(PlayerArgs {
name: Some("みやおう".to_string()),
level: Some(10),
..Default::default()
});
assert_eq!(player.level(), 10);
assert_eq!(player.exp, get_status_by_level(10).unwrap().required_exp);
}
#[test]
fn test_player_with_exp() {
let player = Player::new_with(PlayerArgs {
name: Some("たけし".to_string()),
exp: Some(8000),
..Default::default()
});
let expected_level = get_level_by_exp(8000);
assert_eq!(player.level(), expected_level);
assert_eq!(player.exp, 8000);
}
#[test]
fn test_player_with_level_and_exp() {
let player = Player::new_with(PlayerArgs {
name: Some("こういち".to_string()),
level: Some(5),
exp: Some(10000),
gold: Some(20000),
..Default::default()
});
assert!(player.level() >= 5);
assert_eq!(player.exp, 10000);
assert_eq!(player.gold, 20000);
}
#[test]
fn test_player_maximize_password() {
let mut player = Player::new("だい");
player.maximize();
let password = player.to_password_string().unwrap();
assert_eq!(password, "へへみぞあうぞてえきいおくらちきこぜくゆ");
}
}