You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
8.8 KiB

use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::convert::TryInto;
use serde::Deserialize;
use rand::{Rng, SeedableRng};
use rand::distributions::{WeightedIndex, Distribution};
use crate::entity::item::ItemDetail;
use crate::entity::item::armor::{Armor, ArmorType};
use crate::entity::item::shield::{Shield, ShieldType};
use crate::entity::item::unit::{Unit, UnitType};
use crate::ship::shops::ShopItem;
use crate::ship::item_stats::{ARMOR_STATS, SHIELD_STATS, UNIT_STATS};
#[derive(Debug)]
pub enum ArmorShopItem {
Frame(ArmorType, usize),
Barrier(ShieldType),
Unit(UnitType),
}
const ARMOR_MULTIPLIER: f32 = 0.799999952;
const SHIELD_MULTIPLIER: f32 = 1.5;
const UNIT_MULTIPLIER: f32 = 1000.0;
impl ShopItem for ArmorShopItem {
fn price(&self) -> usize {
match self {
ArmorShopItem::Frame(frame, slot) => {
ARMOR_STATS.get(&frame)
.map(|frame_stats| {
let mut price = (frame_stats.dfp + frame_stats.evp) as f32;
price *= price;
price /= ARMOR_MULTIPLIER;
price += 70.0 * frame_stats.level_req as f32;
price += 70.0 * frame_stats.level_req as f32 * *slot as f32;
price as usize
})
.unwrap_or(0xFFFF)
},
ArmorShopItem::Barrier(barrier) => {
SHIELD_STATS.get(&barrier)
.map(|barrier_stats| {
let mut price = (barrier_stats.dfp + barrier_stats.evp) as f32;
price *= price;
price /= SHIELD_MULTIPLIER;
price += 70.0 * barrier_stats.level_req as f32;
price as usize
})
.unwrap_or(0xFFFF)
},
ArmorShopItem::Unit(unit) => {
UNIT_STATS.get(&unit)
.map(|unit_stats| {
(unit_stats.stars as f32 * UNIT_MULTIPLIER) as usize
})
.unwrap_or(0xFFFF)
}
}
}
fn as_bytes(&self) -> [u8; 12] {
self.as_item().as_client_bytes()[0..12].try_into().unwrap()
}
fn as_item(&self) -> ItemDetail {
match self {
ArmorShopItem::Frame(frame, slot) => {
ItemDetail::Armor(Armor {
armor: *frame,
dfp: 0,
evp: 0,
slots: *slot as u8,
wrapping: None,
})
},
ArmorShopItem::Barrier(barrier) => {
ItemDetail::Shield(Shield {
shield: *barrier,
dfp: 0,
evp: 0,
wrapping: None,
})
},
ArmorShopItem::Unit(unit) => {
ItemDetail::Unit(Unit {
unit: *unit,
modifier: None,
wrapping: None,
})
},
}
}
}
#[derive(Debug, Deserialize, Clone)]
struct FrameTierItem {
item: ArmorType,
probability: usize,
}
#[derive(Debug, Deserialize, Clone)]
struct FrameTier {
level: usize,
item: Vec<FrameTierItem>,
}
#[derive(Debug, Deserialize)]
struct SlotRate {
slot: usize,
probability: usize,
}
#[derive(Debug, Deserialize)]
struct FrameTable {
frame: Vec<FrameTier>,
slot_rate: Vec<SlotRate>,
}
#[derive(Debug, Deserialize, Clone)]
struct BarrierTierItem {
item: ShieldType,
probability: usize,
}
#[derive(Debug, Deserialize, Clone)]
struct BarrierTier {
level: usize,
item: Vec<BarrierTierItem>,
}
#[derive(Debug, Deserialize)]
struct BarrierTable {
barrier: Vec<BarrierTier>,
}
#[derive(Debug, Deserialize, Clone)]
struct UnitTierItem {
item: UnitType,
probability: usize,
}
#[derive(Debug, Deserialize, Clone)]
struct UnitTier {
level: usize,
item: Vec<UnitTierItem>,
}
#[derive(Debug, Deserialize)]
struct UnitTable {
unit: Vec<UnitTier>,
}
fn load_frame_table() -> FrameTable {
let path = PathBuf::from("data/shops/frame.toml");
let mut f = File::open(path).unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
let table: FrameTable = toml::from_str(s.as_str()).unwrap();
table
}
fn load_barrier_table() -> BarrierTable {
let path = PathBuf::from("data/shops/barrier.toml");
let mut f = File::open(path).unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
let table: BarrierTable = toml::from_str(s.as_str()).unwrap();
table
}
fn load_unit_table() -> UnitTable {
let path = PathBuf::from("data/shops/unit.toml");
let mut f = File::open(path).unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
let table: UnitTable = toml::from_str(s.as_str()).unwrap();
table
}
fn number_of_frames_to_generate(character_level: usize) -> usize {
if character_level <= 10 {
4
}
else if character_level <= 25 {
6
}
else if character_level <= 42 {
7
}
else {
8
}
}
fn number_of_barriers_to_generate(character_level: usize) -> usize {
if character_level <= 10 {
4
}
else if character_level <= 25 {
5
}
else if character_level <= 42 {
6
}
else {
7
}
}
fn number_of_units_to_generate(character_level: usize) -> usize {
if character_level <= 10 {
0
}
else if character_level <= 25 {
3
}
else if character_level <= 42 {
5
}
else {
6
}
}
#[derive(Debug)]
pub struct ArmorShop<R: Rng + SeedableRng> {
frame: FrameTable,
barrier: BarrierTable,
unit: UnitTable,
rng: R,
}
impl<R: Rng + SeedableRng> ArmorShop<R> {
pub fn new() -> ArmorShop<R> {
ArmorShop {
frame: load_frame_table(),
barrier: load_barrier_table(),
unit: load_unit_table(),
rng: R::from_entropy(),
}
}
fn generate_frame_list(&mut self, character_level: usize) -> Vec<ArmorShopItem> {
let tier = self.frame.frame.iter()
.filter(|t| t.level <= character_level)
.last()
.cloned()
.unwrap();
let frame_choice = WeightedIndex::new(tier.item.iter().map(|f| f.probability)).unwrap();
let slot_choice = WeightedIndex::new(self.frame.slot_rate.iter().map(|sr| sr.probability)).unwrap();
(0..number_of_frames_to_generate(character_level))
.map(|_| {
let frame_detail = tier.item.get(frame_choice.sample(&mut self.rng)).unwrap();
let slot = self.frame.slot_rate.get(slot_choice.sample(&mut self.rng)).unwrap();
ArmorShopItem::Frame(frame_detail.item, slot.slot)
})
.collect()
}
fn generate_barrier_list(&mut self, character_level: usize) -> Vec<ArmorShopItem> {
let tier = self.barrier.barrier.iter()
.filter(|t| t.level <= character_level)
.last()
.cloned()
.unwrap();
let barrier_choice = WeightedIndex::new(tier.item.iter().map(|b| b.probability)).unwrap();
(0..number_of_barriers_to_generate(character_level))
.map(|_| {
let barrier_detail = tier.item.get(barrier_choice.sample(&mut self.rng)).unwrap();
ArmorShopItem::Barrier(barrier_detail.item)
})
.collect()
}
fn generate_unit_list(&mut self, character_level: usize) -> Vec<ArmorShopItem> {
self.unit.unit.iter()
.filter(|t| t.level <= character_level)
.last()
.cloned()
.map(|tier| {
let unit_choice = WeightedIndex::new(tier.item.iter().map(|u| u.probability)).unwrap();
(0..number_of_units_to_generate(character_level))
.map(|_| {
let unit_detail = tier.item.get(unit_choice.sample(&mut self.rng)).unwrap();
ArmorShopItem::Unit(unit_detail.item)
})
.collect()
})
.unwrap_or(Vec::new())
}
pub fn generate_armor_list(&mut self, character_level: usize) -> Vec<ArmorShopItem> {
self.generate_frame_list(character_level).into_iter()
.chain(self.generate_barrier_list(character_level).into_iter())
.chain(self.generate_unit_list(character_level).into_iter())
.collect()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_loading_tool_shop() {
ArmorShop::<rand_chacha::ChaCha20Rng>::new();
}
#[test]
fn test_generating_some_armor() {
let mut fs = ArmorShop::<rand_chacha::ChaCha20Rng>::new();
for i in 0..200 {
fs.generate_armor_list(i);
}
}
}