jake
4 years ago
4 changed files with 569 additions and 0 deletions
-
299data/shops/techniques.toml
-
1data/shops/tools.toml
-
1src/ship/shops/mod.rs
-
268src/ship/shops/tool.rs
@ -0,0 +1,299 @@ |
|||
[[techniques]] |
|||
level = 0 |
|||
|
|||
[techniques.techs.Foie] |
|||
probability = 18 |
|||
level_divisor = 3 |
|||
[techniques.techs.Barta] |
|||
probability = 18 |
|||
level_divisor = 4 |
|||
[techniques.techs.Zonde] |
|||
probability = 18 |
|||
level_divisor = 6 |
|||
[techniques.techs.Resta] |
|||
probability = 18 |
|||
level_divisor = 3 |
|||
[techniques.techs.Anti] |
|||
probability = 18 |
|||
level_divisor = 35 |
|||
[techniques.techs.Shifta] |
|||
probability = 2 |
|||
set_level = 1 |
|||
[techniques.techs.Deband] |
|||
probability = 3 |
|||
set_level = 1 |
|||
[techniques.techs.Jellen] |
|||
probability = 3 |
|||
set_level = 1 |
|||
[techniques.techs.Zalure] |
|||
probability = 2 |
|||
set_level = 1 |
|||
[techniques.techs.Gifoie] |
|||
probability = 0 |
|||
set_level = 1 |
|||
[techniques.techs.Gibarta] |
|||
probability = 0 |
|||
set_level = 1 |
|||
[techniques.techs.Gizonde] |
|||
probability = 0 |
|||
set_level = 1 |
|||
[techniques.techs.Ryuker] |
|||
probability = 0 |
|||
set_level = 1 |
|||
[techniques.techs.Reverser] |
|||
probability = 0 |
|||
set_level = 1 |
|||
[techniques.techs.Rafoie] |
|||
probability = 0 |
|||
set_level = 1 |
|||
[techniques.techs.Rabarta] |
|||
probability = 0 |
|||
set_level = 1 |
|||
[techniques.techs.Razonde] |
|||
probability = 0 |
|||
set_level = 1 |
|||
[[techniques]] |
|||
level = 10 |
|||
|
|||
[techniques.techs.Foie] |
|||
probability = 7 |
|||
level_divisor = 6 |
|||
[techniques.techs.Barta] |
|||
probability = 7 |
|||
level_divisor = 7 |
|||
[techniques.techs.Zonde] |
|||
probability = 7 |
|||
level_divisor = 8 |
|||
[techniques.techs.Resta] |
|||
probability = 7 |
|||
level_divisor = 6 |
|||
[techniques.techs.Anti] |
|||
probability = 7 |
|||
level_divisor = 35 |
|||
[techniques.techs.Shifta] |
|||
probability = 6 |
|||
level_divisor = 9 |
|||
[techniques.techs.Deband] |
|||
probability = 6 |
|||
level_divisor = 7 |
|||
[techniques.techs.Jellen] |
|||
probability = 6 |
|||
level_divisor = 6 |
|||
[techniques.techs.Zalure] |
|||
probability = 7 |
|||
level_divisor = 8 |
|||
[techniques.techs.Gifoie] |
|||
probability = 8 |
|||
level_divisor = 9 |
|||
[techniques.techs.Gibarta] |
|||
probability = 8 |
|||
level_divisor = 12 |
|||
[techniques.techs.Gizonde] |
|||
probability = 8 |
|||
level_divisor = 8 |
|||
[techniques.techs.Ryuker] |
|||
probability = 5 |
|||
set_level = 1 |
|||
[techniques.techs.Reverser] |
|||
probability = 5 |
|||
set_level = 1 |
|||
[techniques.techs.Rafoie] |
|||
probability = 2 |
|||
set_level = 1 |
|||
[techniques.techs.Rabarta] |
|||
probability = 2 |
|||
set_level = 1 |
|||
[techniques.techs.Razonde] |
|||
probability = 2 |
|||
set_level = 1 |
|||
[[techniques]] |
|||
level = 25 |
|||
|
|||
[techniques.techs.Foie] |
|||
probability = 5 |
|||
min = 3 |
|||
max = 10 |
|||
[techniques.techs.Barta] |
|||
probability = 5 |
|||
min = 3 |
|||
max = 10 |
|||
[techniques.techs.Zonde] |
|||
probability = 5 |
|||
min = 3 |
|||
max = 10 |
|||
[techniques.techs.Resta] |
|||
probability = 6 |
|||
min = 3 |
|||
max = 10 |
|||
[techniques.techs.Anti] |
|||
probability = 5 |
|||
level_divisor = 35 |
|||
[techniques.techs.Shifta] |
|||
probability = 5 |
|||
level_divisor = 8 |
|||
[techniques.techs.Deband] |
|||
probability = 5 |
|||
level_divisor = 7 |
|||
[techniques.techs.Jellen] |
|||
probability = 5 |
|||
level_divisor = 5 |
|||
[techniques.techs.Zalure] |
|||
probability = 5 |
|||
level_divisor = 6 |
|||
[techniques.techs.Gifoie] |
|||
probability = 5 |
|||
level_divisor = 8 |
|||
[techniques.techs.Gibarta] |
|||
probability = 5 |
|||
level_divisor = 9 |
|||
[techniques.techs.Gizonde] |
|||
probability = 5 |
|||
level_divisor = 10 |
|||
[techniques.techs.Ryuker] |
|||
probability = 6 |
|||
set_level = 1 |
|||
[techniques.techs.Reverser] |
|||
probability = 6 |
|||
set_level = 1 |
|||
[techniques.techs.Rafoie] |
|||
probability = 9 |
|||
level_divisor = 10 |
|||
[techniques.techs.Rabarta] |
|||
probability = 9 |
|||
level_divisor = 11 |
|||
[techniques.techs.Razonde] |
|||
probability = 9 |
|||
level_divisor = 12 |
|||
[[techniques]] |
|||
level = 42 |
|||
|
|||
[techniques.techs.Foie] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 12 |
|||
[techniques.techs.Barta] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 12 |
|||
[techniques.techs.Zonde] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 12 |
|||
[techniques.techs.Resta] |
|||
probability = 5 |
|||
min = 4 |
|||
max = 12 |
|||
[techniques.techs.Anti] |
|||
probability = 5 |
|||
level_divisor = 35 |
|||
[techniques.techs.Shifta] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 12 |
|||
[techniques.techs.Deband] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 12 |
|||
[techniques.techs.Jellen] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 12 |
|||
[techniques.techs.Zalure] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 12 |
|||
[techniques.techs.Gifoie] |
|||
probability = 6 |
|||
min = 3 |
|||
max = 10 |
|||
[techniques.techs.Gibarta] |
|||
probability = 6 |
|||
min = 3 |
|||
max = 10 |
|||
[techniques.techs.Gizonde] |
|||
probability = 6 |
|||
min = 3 |
|||
max = 10 |
|||
[techniques.techs.Ryuker] |
|||
probability = 6 |
|||
set_level = 1 |
|||
[techniques.techs.Reverser] |
|||
probability = 6 |
|||
set_level = 1 |
|||
[techniques.techs.Rafoie] |
|||
probability = 6 |
|||
level_divisor = 12 |
|||
[techniques.techs.Rabarta] |
|||
probability = 6 |
|||
level_divisor = 13 |
|||
[techniques.techs.Razonde] |
|||
probability = 6 |
|||
level_divisor = 11 |
|||
[[techniques]] |
|||
level = 60 |
|||
|
|||
[techniques.techs.Foie] |
|||
probability = 6 |
|||
min = 5 |
|||
max = 14 |
|||
[techniques.techs.Barta] |
|||
probability = 6 |
|||
min = 5 |
|||
max = 14 |
|||
[techniques.techs.Zonde] |
|||
probability = 6 |
|||
min = 5 |
|||
max = 14 |
|||
[techniques.techs.Resta] |
|||
probability = 5 |
|||
min = 5 |
|||
max = 14 |
|||
[techniques.techs.Anti] |
|||
probability = 5 |
|||
level_divisor = 35 |
|||
[techniques.techs.Shifta] |
|||
probability = 6 |
|||
min = 5 |
|||
max = 14 |
|||
[techniques.techs.Deband] |
|||
probability = 6 |
|||
min = 5 |
|||
max = 14 |
|||
[techniques.techs.Jellen] |
|||
probability = 6 |
|||
min = 5 |
|||
max = 14 |
|||
[techniques.techs.Zalure] |
|||
probability = 6 |
|||
min = 5 |
|||
max = 14 |
|||
[techniques.techs.Gifoie] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 13 |
|||
[techniques.techs.Gibarta] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 13 |
|||
[techniques.techs.Gizonde] |
|||
probability = 6 |
|||
min = 4 |
|||
max = 13 |
|||
[techniques.techs.Ryuker] |
|||
probability = 6 |
|||
set_level = 1 |
|||
[techniques.techs.Reverser] |
|||
probability = 6 |
|||
set_level = 1 |
|||
[techniques.techs.Rafoie] |
|||
probability = 6 |
|||
min = 3 |
|||
max = 13 |
|||
[techniques.techs.Rabarta] |
|||
probability = 6 |
|||
min = 3 |
|||
max = 13 |
|||
[techniques.techs.Razonde] |
|||
probability = 6 |
|||
min = 3 |
|||
max = 13 |
@ -0,0 +1 @@ |
|||
tools = [ "Monomate", "Dimate", "Trimate", "Monofluid", "Difluid", "Trifluid", "Antidote", "Antiparalysis", "SolAtomizer", "MoonAtomizer", "StarAtomizer", "Telepipe",] |
@ -1 +1,2 @@ |
|||
pub mod weapon;
|
|||
pub mod tool;
|
@ -0,0 +1,268 @@ |
|||
use std::collections::HashMap;
|
|||
use std::fs::File;
|
|||
use std::io::Read;
|
|||
use std::path::PathBuf;
|
|||
use serde::Deserialize;
|
|||
use rand::{Rng, SeedableRng};
|
|||
use rand::distributions::{WeightedIndex, Distribution};
|
|||
use rand::seq::{SliceRandom, IteratorRandom};
|
|||
use crate::entity::character::SectionID;
|
|||
use crate::ship::room::Difficulty;
|
|||
use crate::entity::item::tool::{Tool, ToolType};
|
|||
use crate::entity::item::tech::{Technique, TechniqueDisk};
|
|||
|
|||
|
|||
#[derive(Debug, PartialEq, Eq)]
|
|||
pub enum ShopTool {
|
|||
Tool(ToolType),
|
|||
Tech(TechniqueDisk),
|
|||
}
|
|||
|
|||
impl Ord for ShopTool {
|
|||
fn cmp(&self, other: &ShopTool) -> std::cmp::Ordering {
|
|||
let a = match self {
|
|||
ShopTool::Tool(t) => Tool{tool : *t}.as_individual_bytes(),
|
|||
ShopTool::Tech(t) => t.as_bytes(),
|
|||
};
|
|||
let b = match other {
|
|||
ShopTool::Tool(t) => Tool{tool : *t}.as_individual_bytes(),
|
|||
ShopTool::Tech(t) => t.as_bytes(),
|
|||
};
|
|||
|
|||
a.cmp(&b)
|
|||
}
|
|||
}
|
|||
|
|||
impl PartialOrd for ShopTool {
|
|||
fn partial_cmp(&self, other: &ShopTool) -> Option<std::cmp::Ordering> {
|
|||
let a = match self {
|
|||
ShopTool::Tool(t) => Tool{tool : *t}.as_individual_bytes(),
|
|||
ShopTool::Tech(t) => t.as_bytes(),
|
|||
};
|
|||
let b = match other {
|
|||
ShopTool::Tool(t) => Tool{tool : *t}.as_individual_bytes(),
|
|||
ShopTool::Tech(t) => t.as_bytes(),
|
|||
};
|
|||
|
|||
a.partial_cmp(&b)
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Debug, Deserialize)]
|
|||
struct ToolTable(Vec<ToolType>);
|
|||
|
|||
#[derive(Debug, Deserialize, Clone)]
|
|||
#[serde(untagged)]
|
|||
enum TechLevel {
|
|||
Set {
|
|||
set_level: usize,
|
|||
},
|
|||
Level {
|
|||
level_divisor: usize,
|
|||
},
|
|||
Range {
|
|||
min: usize,
|
|||
max: usize,
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Debug, Deserialize, Clone)]
|
|||
struct TechEntry {
|
|||
probability: usize,
|
|||
#[serde(flatten)]
|
|||
level: TechLevel,
|
|||
}
|
|||
|
|||
#[derive(Debug, Deserialize)]
|
|||
struct TechTierDeserialize {
|
|||
level: usize,
|
|||
techs: HashMap<String, TechEntry>,
|
|||
}
|
|||
|
|||
#[derive(Debug, Clone)]
|
|||
struct TechTier {
|
|||
level: usize,
|
|||
techs: HashMap<Technique, TechEntry>,
|
|||
}
|
|||
|
|||
#[derive(Debug)]
|
|||
struct TechTable(Vec<TechTier>);
|
|||
|
|||
|
|||
fn load_tool_table() -> ToolTable {
|
|||
let path = PathBuf::from("data/shops/tools.toml");
|
|||
let mut f = File::open(path).unwrap();
|
|||
let mut s = String::new();
|
|||
f.read_to_string(&mut s).unwrap();
|
|||
|
|||
let mut table: HashMap<String, Vec<ToolType>> = toml::from_str(s.as_str()).unwrap();
|
|||
|
|||
ToolTable(table.remove("tools".into()).unwrap())
|
|||
}
|
|||
|
|||
fn load_tech_table() -> TechTable {
|
|||
let path = PathBuf::from("data/shops/techniques.toml");
|
|||
let mut f = File::open(path).unwrap();
|
|||
let mut s = String::new();
|
|||
f.read_to_string(&mut s).unwrap();
|
|||
|
|||
let mut table: HashMap<String, Vec<TechTierDeserialize>> = toml::from_str(s.as_str()).unwrap();
|
|||
let techniques = table.remove("techniques".into()).unwrap();
|
|||
let techniques = techniques.into_iter()
|
|||
.map(|tech_tier| {
|
|||
TechTier {
|
|||
level: tech_tier.level,
|
|||
techs: tech_tier.techs.into_iter()
|
|||
.map(|(tech_name, tech_entry)| {
|
|||
(tech_name.parse().unwrap(), tech_entry)
|
|||
}).collect()
|
|||
}
|
|||
}).collect();
|
|||
|
|||
TechTable(techniques)
|
|||
}
|
|||
|
|||
fn number_of_techs_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
|
|||
}
|
|||
}
|
|||
|
|||
|
|||
#[derive(Debug)]
|
|||
struct ToolShop<R: Rng + SeedableRng> {
|
|||
tools: ToolTable,
|
|||
techs: TechTable,
|
|||
rng: R,
|
|||
}
|
|||
|
|||
impl<R: Rng + SeedableRng> ToolShop<R> {
|
|||
pub fn new() -> ToolShop<R> {
|
|||
ToolShop {
|
|||
tools: load_tool_table(),
|
|||
techs: load_tech_table(),
|
|||
rng: R::from_entropy(),
|
|||
}
|
|||
}
|
|||
|
|||
fn generate_tech(&mut self, character_level: usize) -> ShopTool {
|
|||
let tier = self.techs.0.iter()
|
|||
.filter(|t| t.level <= character_level)
|
|||
.last()
|
|||
.unwrap();
|
|||
|
|||
let mut tier = tier.techs.iter()
|
|||
.map(|(tech, entry)| {
|
|||
(tech, entry)
|
|||
});
|
|||
|
|||
|
|||
let tech_choice = WeightedIndex::new(tier.clone().map(|(_, e)| e.probability)).unwrap();
|
|||
let tech_detail = tier.nth(tech_choice.sample(&mut self.rng)).unwrap();
|
|||
let tech_level = match tech_detail.1.level {
|
|||
TechLevel::Set{set_level} => set_level,
|
|||
TechLevel::Level{level_divisor} => std::cmp::max(std::cmp::min(character_level, 99)/level_divisor, 1),
|
|||
TechLevel::Range{min, max} => self.rng.gen_range(min, max+1),
|
|||
};
|
|||
|
|||
ShopTool::Tech(
|
|||
TechniqueDisk {
|
|||
tech: *tech_detail.0,
|
|||
level: tech_level as u32,
|
|||
}
|
|||
)
|
|||
}
|
|||
|
|||
fn generate_tech_types(&mut self, character_level: usize) -> Vec<Technique> {
|
|||
let tier = self.techs.0.iter()
|
|||
.filter(|t| t.level <= character_level)
|
|||
.last()
|
|||
.unwrap();
|
|||
|
|||
let possible_techs = tier.techs.iter()
|
|||
.filter(|(_, entry)| entry.probability > 0)
|
|||
.collect::<Vec<_>>();
|
|||
|
|||
let number_of_techs = std::cmp::min(possible_techs.len(), number_of_techs_to_generate(character_level));
|
|||
if number_of_techs == possible_techs.len() {
|
|||
possible_techs.into_iter()
|
|||
.map(|(tech, _entry)| {
|
|||
tech
|
|||
})
|
|||
.cloned()
|
|||
.collect()
|
|||
}
|
|||
else {
|
|||
let mut techs = Vec::new();
|
|||
let tier = tier.techs.iter()
|
|||
.map(|(tech, entry)| {
|
|||
(tech, entry)
|
|||
})
|
|||
.collect::<Vec<_>>();
|
|||
|
|||
let tech_choice = WeightedIndex::new(tier.iter().map(|(_, e)| e.probability)).unwrap();
|
|||
while techs.len() < number_of_techs {
|
|||
let tech_detail = tier.get(tech_choice.sample(&mut self.rng)).unwrap();
|
|||
if techs.iter().find(|t| *t == tech_detail.0).is_none() {
|
|||
techs.push(*tech_detail.0);
|
|||
}
|
|||
}
|
|||
techs
|
|||
}
|
|||
|
|||
}
|
|||
|
|||
fn generate_techs(&mut self, character_level: usize) -> Vec<ShopTool> {
|
|||
let tier = self.techs.0.iter()
|
|||
.filter(|t| t.level <= character_level)
|
|||
.last()
|
|||
.cloned()
|
|||
.unwrap();
|
|||
|
|||
let tech_types = self.generate_tech_types(character_level);
|
|||
tech_types.into_iter()
|
|||
.map(|tech| {
|
|||
let tech_detail = tier.techs.get(&tech).unwrap().clone();
|
|||
let level = match tech_detail.level {
|
|||
TechLevel::Set{set_level} => set_level,
|
|||
TechLevel::Level{level_divisor} => std::cmp::max(std::cmp::min(character_level, 99)/level_divisor, 1),
|
|||
TechLevel::Range{min, max} => self.rng.gen_range(min, max+1),
|
|||
};
|
|||
|
|||
ShopTool::Tech(TechniqueDisk {
|
|||
tech: tech,
|
|||
level: level as u32,
|
|||
})
|
|||
})
|
|||
.collect()
|
|||
}
|
|||
|
|||
pub fn generate_tool_list(&mut self, character_level: usize) -> Vec<ShopTool> {
|
|||
let mut tools = Vec::new().into_iter()
|
|||
.chain(self.tools.0.clone().into_iter().map(|t| ShopTool::Tool(t)))
|
|||
.chain(self.generate_techs(character_level).into_iter())
|
|||
.collect::<Vec<_>>();
|
|||
tools.sort();
|
|||
tools
|
|||
}
|
|||
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use super::*;
|
|||
|
|||
#[test]
|
|||
fn test_loading_tool_shop() {
|
|||
ToolShop::<rand_chacha::ChaCha20Rng>::new();
|
|||
}
|
|||
}
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue