Browse Source

clean up and set rare monster appear rate to 1/512

pull/60/head
andy 3 years ago
parent
commit
309b97cbc2
  1. 8
      data/battle_param/ep1_rare_monster.toml
  2. 6
      data/battle_param/ep2_rare_monster.toml
  3. 12
      data/battle_param/ep4_rare_monster.toml
  4. 2
      src/ship/drops/mod.rs
  5. 38
      src/ship/map/enemy.rs
  6. 104
      src/ship/map/maps.rs
  7. 1
      src/ship/packet/handler/quest.rs
  8. 9
      src/ship/room.rs

8
data/battle_param/ep1_rare_monster.toml

@ -3,7 +3,7 @@
# 1/256 = 0.00390625 # 1/256 = 0.00390625
# 1/512 = 0.001953125 # 1/512 = 0.001953125
Hildebear = 0.1
RagRappy = 0.2
PoisonLily = 0.3
PofuillySlime = 0.4
Hildebear = 0.001953125
RagRappy = 0.001953125
PoisonLily = 0.001953125
PofuillySlime = 0.001953125

6
data/battle_param/ep2_rare_monster.toml

@ -3,7 +3,7 @@
# 1/256 = 0.00390625 # 1/256 = 0.00390625
# 1/512 = 0.001953125 # 1/512 = 0.001953125
Hildebear = 0.01
RagRappy = 0.01
PoisonLily = 0.01
Hildebear = 0.001953125
RagRappy = 0.001953125
PoisonLily = 0.001953125

12
data/battle_param/ep4_rare_monster.toml

@ -3,11 +3,11 @@
# 1/256 = 0.00390625 # 1/256 = 0.00390625
# 1/512 = 0.001953125 # 1/512 = 0.001953125
SandRappyCrater = 0.01
ZuCrater = 0.01
Dorphon = 0.01
SandRappyDesert = 0.01
ZuDesert = 0.01
MerissaA = 0.01
SandRappyCrater = 0.001953125
ZuCrater = 0.001953125
Dorphon = 0.001953125
SandRappyDesert = 0.001953125
ZuDesert = 0.001953125
MerissaA = 0.001953125
Shambertin = 0.1 Shambertin = 0.1
SaintMillion = 0.1 SaintMillion = 0.1

2
src/ship/drops/mod.rs

@ -57,9 +57,9 @@ pub fn load_data_file<T: serde::de::DeserializeOwned>(episode: Episode, difficul
// this is just copypaste // this is just copypaste
pub fn load_rare_monster_file<T: serde::de::DeserializeOwned>(episode: Episode) -> T { pub fn load_rare_monster_file<T: serde::de::DeserializeOwned>(episode: Episode) -> T {
// TODO: where does the rare monster toml file actually live
let mut path = PathBuf::from("data/battle_param/"); let mut path = PathBuf::from("data/battle_param/");
path.push(episode.to_string().to_lowercase() + "_rare_monster.toml"); path.push(episode.to_string().to_lowercase() + "_rare_monster.toml");
println!("rare monster file path: {:?}", path);
let mut f = File::open(path).unwrap(); let mut f = File::open(path).unwrap();
let mut s = String::new(); let mut s = String::new();

38
src/ship/map/enemy.rs

@ -75,21 +75,15 @@ pub enum MapEnemyError {
MapAreaError(#[from] MapAreaError), MapAreaError(#[from] MapAreaError),
} }
// TODO: is this even needed/used?
// #[derive(Clone, Debug, Serialize, Deserialize)]
// pub type RareMonsterAppearRate: HashMap<MonsterType, f32>,
// making this `pub type` doesn't allow `impl`s to be defined? // making this `pub type` doesn't allow `impl`s to be defined?
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RareMonsterAppearTable { pub struct RareMonsterAppearTable {
appear_rate: HashMap<MonsterType, f32>, appear_rate: HashMap<MonsterType, f32>,
seed: u32
} }
impl RareMonsterAppearTable { impl RareMonsterAppearTable {
pub fn new(episode: Episode, room_seed: u32) -> RareMonsterAppearTable {
pub fn new(episode: Episode) -> RareMonsterAppearTable {
let cfg: HashMap<String, f32> = load_rare_monster_file(episode); let cfg: HashMap<String, f32> = load_rare_monster_file(episode);
println!("got cfg: {:?}", cfg);
let appear_rates: HashMap<MonsterType, f32> = cfg let appear_rates: HashMap<MonsterType, f32> = cfg
.into_iter() .into_iter()
@ -102,23 +96,16 @@ impl RareMonsterAppearTable {
RareMonsterAppearTable { RareMonsterAppearTable {
appear_rate: appear_rates, appear_rate: appear_rates,
seed: room_seed,
} }
} }
pub fn roll_appearance(&self, monster: &MonsterType) -> bool { pub fn roll_appearance(&self, monster: &MonsterType) -> bool {
println!("rolling appearance for {:?} with seed {:?}", monster, self.seed);
// let mut rng = rand_chacha::ChaChaRng::from_entropy();
if rand_chacha::ChaChaRng::from_entropy().gen::<f32>() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) { if rand_chacha::ChaChaRng::from_entropy().gen::<f32>() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) {
println!("its a rare!");
true
return true
} }
else {
println!("lol sucker");
false false
} }
} }
}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -135,7 +122,6 @@ pub struct MapEnemy {
impl MapEnemy { impl MapEnemy {
pub fn from_raw(enemy: RawMapEnemy, episode: &Episode, map_area: &MapArea /*, battleparam */) -> Result<MapEnemy, MapEnemyError> { pub fn from_raw(enemy: RawMapEnemy, episode: &Episode, map_area: &MapArea /*, battleparam */) -> Result<MapEnemy, MapEnemyError> {
println!("enemy.rs::from_raw - {:?}", enemy);
// TODO: rare enemies ep1-4, tower lilys, event rappies, ult variants? // TODO: rare enemies ep1-4, tower lilys, event rappies, ult variants?
// TODO: check what "skin" actually does. some unexpected enemies have many (panarms, slimes, lilys) // TODO: check what "skin" actually does. some unexpected enemies have many (panarms, slimes, lilys)
let monster = match map_area { let monster = match map_area {
@ -337,7 +323,6 @@ impl MapEnemy {
} }
} }
// TODO: does this actually do anything useful?
pub fn has_rare_appearance(self) -> bool { pub fn has_rare_appearance(self) -> bool {
match self.monster { match self.monster {
MonsterType::RagRappy | MonsterType::Hildebear | MonsterType::RagRappy | MonsterType::Hildebear |
@ -349,21 +334,10 @@ impl MapEnemy {
} }
} }
// // TODO: does `shiny` need to be set here?
// // TODO: distinguish between a `random` rare monster and a `set/guaranteed` rare monster? (does any acceptable quest even have this?)
// pub fn set_rare_appearance(self) -> MapEnemy {
// match self.monster {
// MonsterType::RagRappy | MonsterType::Hildebear |
// MonsterType::PoisonLily | MonsterType::PofuillySlime |
// MonsterType::SandRappyCrater | MonsterType::ZuCrater | MonsterType::Dorphon |
// MonsterType::SandRappyDesert | MonsterType::ZuDesert | MonsterType::MerissaA |
// MonsterType::SaintMillion | MonsterType::Shambertin => self.set_shiny(),
// _ => self,
// }
// }
// TODO: does `shiny` need to be set here?
// TODO: distinguish between a `random` rare monster and a `set/guaranteed` rare monster? (does any acceptable quest even have this?)
/*
TODO: distinguish between a `random` rare monster and a `set/guaranteed` rare monster? (does any acceptable quest even have this?)
guaranteed rare monsters don't count towards the limit
*/
pub fn set_rare_appearance(self) -> MapEnemy { pub fn set_rare_appearance(self) -> MapEnemy {
match (self.monster, self.map_area.to_episode()) { match (self.monster, self.map_area.to_episode()) {
(MonsterType::RagRappy, Episode::One) => {MapEnemy {monster: MonsterType::AlRappy, shiny:true, ..self}}, (MonsterType::RagRappy, Episode::One) => {MapEnemy {monster: MonsterType::AlRappy, shiny:true, ..self}},

104
src/ship/map/maps.rs

@ -12,8 +12,6 @@ use crate::ship::room::{Episode, RoomMode};
// TODO: don't use * // TODO: don't use *
use crate::ship::map::*; use crate::ship::map::*;
// use rand::{Rng};
pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec<Option<MapObject>> { pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec<Option<MapObject>> {
let mut object_data = Vec::new(); let mut object_data = Vec::new();
@ -31,99 +29,14 @@ fn objects_from_map_data(path: PathBuf, episode: &Episode, map_area: &MapArea) -
} }
fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> Vec<Option<MapEnemy>> { fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> Vec<Option<MapEnemy>> {
// fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy, rare_monster_table: RareMonsterAppearTable) -> Vec<Option<MapEnemy>> {
let enemy = MapEnemy::from_raw(raw_enemy, episode, map_area); let enemy = MapEnemy::from_raw(raw_enemy, episode, map_area);
// TODO: load rare monster rates config
enemy enemy
.map_or(vec![None], |monster| { .map_or(vec![None], |monster| {
let mut monsters = Vec::new(); let mut monsters = Vec::new();
monsters.push(Some(monster)); monsters.push(Some(monster));
match monster.monster { match monster.monster {
// TODO: make real spawn rates
// TODO: specific ep 2 event rappies
// MonsterType::RagRappy => {if rand::thread_rng().gen_range(0, 100) < 11 {
/* MonsterType::RagRappy => {if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
match episode {
Episode::One => {monsters.push(Some(MapEnemy::new(MonsterType::AlRappy, monster.map_area).set_shiny()))},
Episode::Two => {monsters.push(Some(MapEnemy::new(MonsterType::EventRappy, monster.map_area).set_shiny()))},
_ => {unreachable!()},
}
}},
// MonsterType::Hildebear => {if rand::thread_rng().gen_range(0, 100) < 11 {
MonsterType::Hildebear => {if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
monsters.push(Some(MapEnemy::new(MonsterType::Hildeblue, monster.map_area).set_shiny()))
}},
// MonsterType::PoisonLily => {if rand::thread_rng().gen_range(0, 100) < 11 {
MonsterType::PoisonLily => {if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
monsters.push(Some(MapEnemy::new(MonsterType::NarLily, monster.map_area).set_shiny()))
}},
// TODO: client increments by 5 for slimes instead of 4 segawhyyyyy?????
MonsterType::PofuillySlime => {
monsters.pop();
for _ in 0..5 {
// if rand::thread_rng().gen_range(0, 100) < 11 {
if rare_monster_table.roll_appearance(&monster.monster) {
monsters.push(Some(MapEnemy::new(MonsterType::PouillySlime, monster.map_area).set_shiny()))
} else {
monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime, monster.map_area)))
}
}
},
MonsterType::PouillySlime => {
// guaranteed rare slime already pushed
// roll for the other 3 (4?) copies
for _ in 0..4 {
// if rand::thread_rng().gen_range(0, 100) < 11 {
if rare_monster_table.roll_appearance(&monster.monster) {
monsters.push(Some(MapEnemy::new(MonsterType::PouillySlime, monster.map_area).set_shiny()))
} else {
monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime, monster.map_area)))
}
}
},
// MonsterType::SandRappyCrater => {if rand::thread_rng().gen_range(0, 100) < 11 {
MonsterType::SandRappyCrater => {if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
monsters.push(Some(MapEnemy::new(MonsterType::DelRappyCrater, monster.map_area).set_shiny()))
}},
// MonsterType::ZuCrater => {if rand::thread_rng().gen_range(0, 100) < 11 {
MonsterType::ZuCrater => {if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
monsters.push(Some(MapEnemy::new(MonsterType::PazuzuCrater, monster.map_area).set_shiny()))
}},
// MonsterType::Dorphon => {if rand::thread_rng().gen_range(0, 100) < 11 {
MonsterType::Dorphon => {if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
monsters.push(Some(MapEnemy::new(MonsterType::DorphonEclair, monster.map_area).set_shiny()))
}},
// MonsterType::SandRappyDesert => {if rand::thread_rng().gen_range(0, 100) < 11 {
MonsterType::SandRappyDesert => {if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
monsters.push(Some(MapEnemy::new(MonsterType::DelRappyDesert, monster.map_area).set_shiny()))
}},
// MonsterType::ZuDesert => {if rand::thread_rng().gen_range(0, 100) < 11 {
MonsterType::ZuDesert => {if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
monsters.push(Some(MapEnemy::new(MonsterType::PazuzuDesert, monster.map_area).set_shiny()))
}},
// MonsterType::MerissaA => {if rand::thread_rng().gen_range(0, 100) < 11 {
MonsterType::MerissaA => {if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
monsters.push(Some(MapEnemy::new(MonsterType::MerissaAA, monster.map_area).set_shiny()))
}},
// MonsterType::SaintMillion | MonsterType::Shambertin => { if rand::thread_rng().gen_range(0, 100) < 11 {
MonsterType::SaintMillion | MonsterType::Shambertin => { if rare_monster_table.roll_appearance(&monster.monster) {
monsters.pop();
monsters.push(Some(MapEnemy::new(MonsterType::Kondrieu, monster.map_area).set_shiny()))
}},
*/
MonsterType::Monest => { MonsterType::Monest => {
for _ in 0..30 { for _ in 0..30 {
monsters.push(Some(MapEnemy::new(MonsterType::Mothmant, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::Mothmant, monster.map_area)));
@ -235,21 +148,17 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) ->
pub fn enemy_data_from_stream(cursor: &mut impl Read, map_area: &MapArea, episode: &Episode) -> Vec<Option<MapEnemy>> { pub fn enemy_data_from_stream(cursor: &mut impl Read, map_area: &MapArea, episode: &Episode) -> Vec<Option<MapEnemy>> {
// pub fn enemy_data_from_stream(cursor: &mut impl Read, map_area: &MapArea, episode: &Episode, rare_monster_table: enemy::RareMonsterAppearTable) -> Vec<Option<MapEnemy>> {
let mut enemy_data = Vec::new(); let mut enemy_data = Vec::new();
while let Ok(enemy) = RawMapEnemy::from_byte_stream(cursor) { while let Ok(enemy) = RawMapEnemy::from_byte_stream(cursor) {
enemy_data.append(&mut parse_enemy(episode, map_area, enemy)); enemy_data.append(&mut parse_enemy(episode, map_area, enemy));
// enemy_data.append(&mut parse_enemy(episode, map_area, enemy, rare_monster_table));
} }
enemy_data enemy_data
} }
fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode) -> Vec<Option<MapEnemy>> { fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode) -> Vec<Option<MapEnemy>> {
// fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode, rare_monster_table: enemy::RareMonsterAppearTable) -> Vec<Option<MapEnemy>> {
let path = map_variant.dat_file(); let path = map_variant.dat_file();
let mut cursor = File::open(path).unwrap(); let mut cursor = File::open(path).unwrap();
enemy_data_from_stream(&mut cursor, &map_variant.map, episode) enemy_data_from_stream(&mut cursor, &map_variant.map, episode)
// enemy_data_from_stream(&mut cursor, &map_variant.map, episode, rare_monster_table)
} }
@ -268,7 +177,6 @@ pub struct Maps {
} }
impl Maps { impl Maps {
// pub fn new(room_mode: RoomMode) -> Maps {
pub fn new(room_mode: RoomMode, rare_monster_table: &enemy::RareMonsterAppearTable) -> Maps { pub fn new(room_mode: RoomMode, rare_monster_table: &enemy::RareMonsterAppearTable) -> Maps {
let map_variants = match (room_mode.episode(), room_mode.single_player()) { let map_variants = match (room_mode.episode(), room_mode.single_player()) {
(Episode::One, 0) => { (Episode::One, 0) => {
@ -365,7 +273,6 @@ impl Maps {
enemy_data: map_variants.iter() enemy_data: map_variants.iter()
.fold(Vec::new(), |mut enemy_data, map_variant| { .fold(Vec::new(), |mut enemy_data, map_variant| {
enemy_data.append(&mut enemy_data_from_map_data(map_variant, &room_mode.episode())); enemy_data.append(&mut enemy_data_from_map_data(map_variant, &room_mode.episode()));
// enemy_data.append(&mut enemy_data_from_map_data(map_variant, &room_mode.episode(), rare_monster_table));
enemy_data enemy_data
}), }),
object_data: map_variants.iter() object_data: map_variants.iter()
@ -398,20 +305,9 @@ impl Maps {
}) })
} }
// pub fn set_quest_data(&mut self, enemies: Vec<Option<MapEnemy>>, objects: Vec<Option<MapObject>>) {
pub fn set_quest_data(&mut self, enemies: Vec<Option<MapEnemy>>, objects: Vec<Option<MapObject>>, rare_monster_appear_table: &RareMonsterAppearTable) { pub fn set_quest_data(&mut self, enemies: Vec<Option<MapEnemy>>, objects: Vec<Option<MapObject>>, rare_monster_appear_table: &RareMonsterAppearTable) {
self.enemy_data = enemies; self.enemy_data = enemies;
self.roll_monster_appearance(rare_monster_appear_table); self.roll_monster_appearance(rare_monster_appear_table);
// self.enemy_data = enemies
// .iter()
// .map(|&x| if x.is_some() {
// Some(x.unwrap().roll_appearance_for_quest(rare_monster_table))
// } else {
// x
// })
// .collect();
self.object_data = objects; self.object_data = objects;
} }

1
src/ship/packet/handler/quest.rs

@ -83,7 +83,6 @@ pub fn player_chose_quest(id: ClientId, questmenuselect: &QuestMenuSelect, quest
let room = rooms.get_mut(room_id.0) let room = rooms.get_mut(room_id.0)
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?.as_mut() .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?.as_mut()
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?; .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?;
// room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone());
room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &room.rare_monster_table); room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &room.rare_monster_table);
room.map_areas = quest.map_areas.clone(); room.map_areas = quest.map_areas.clone();

9
src/ship/room.rs

@ -233,19 +233,16 @@ impl RoomState {
} }
}; };
let random_seed = rand::thread_rng().gen();
let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode(), random_seed);
let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode());
Ok(RoomState { Ok(RoomState {
monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(room_mode))?), monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(room_mode))?),
mode: room_mode, mode: room_mode,
// random_seed: rand::thread_rng().gen(),
random_seed: random_seed,
random_seed: rand::thread_rng().gen(),
rare_monster_table: Box::new(rare_monster_table.clone()), rare_monster_table: Box::new(rare_monster_table.clone()),
name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(), name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(),
password: create_room.password, password: create_room.password,
// maps: Maps::new(room_mode),
maps: Maps::new(room_mode, &rare_monster_table),
maps: Maps::new(room_mode, &rare_monster_table), // TODO: rare_monster_table here feels janky. is there some way to call the the RoomState.rare_monster_table we already created?
section_id, section_id,
drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)), drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)),
bursting: false, bursting: false,

Loading…
Cancel
Save