From e2d45f159959ed17ce89d85f37737d7fe0decd15 Mon Sep 17 00:00:00 2001 From: andy Date: Sat, 20 Feb 2021 22:29:40 +0000 Subject: [PATCH 01/15] rare monsters get --- src/ship/map/enemy.rs | 71 +++++++++++++-------- src/ship/map/maps.rs | 109 +++++++++++++++++++++++++++++--- src/ship/packet/builder/room.rs | 6 ++ src/ship/packet/handler/room.rs | 19 +++++- src/ship/ship.rs | 3 +- 5 files changed, 168 insertions(+), 40 deletions(-) diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs index 32e53df..5f9a60e 100644 --- a/src/ship/map/enemy.rs +++ b/src/ship/map/enemy.rs @@ -37,7 +37,7 @@ pub struct RawMapEnemy { impl RawMapEnemy { pub fn from_byte_stream(cursor: &mut R) -> Result { Ok(RawMapEnemy { - id: cursor.read_u32::()?, + id: cursor.read_u32::()?, // TODO: is this really u32? shiny monsters are referred to by u16 in the client _unknown1: cursor.read_u16::()?, children: cursor.read_u16::()?, map_area: cursor.read_u16::()?, @@ -80,21 +80,24 @@ pub struct MapEnemy { pub player_hit: [bool; 4], pub dropped_item: bool, pub gave_exp: bool, + pub shiny: bool, } impl MapEnemy { pub fn from_raw(enemy: RawMapEnemy, episode: &Episode, map_area: &MapArea /*, battleparam */) -> Result { + println!("enemy.rs::from_raw - {:?}", enemy); // 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) let monster = match map_area { MapArea::Forest1 | MapArea::Forest2 | MapArea::Dragon | MapArea::Caves1 | MapArea::Caves2 | MapArea::Caves3 | MapArea::DeRolLe | MapArea::Mines1 | MapArea::Mines2 | MapArea::VolOpt | MapArea::Ruins1 | MapArea::Ruins2 | MapArea::Ruins3 | MapArea::DarkFalz => { match (enemy, episode) { - (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildebear, - // (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildeblue, - (RawMapEnemy {id: 65, ..}, _) => MonsterType::RagRappy, - // (RawMapEnemy {id: 65, ..}, _) => MonsterType::AlRappy, + (RawMapEnemy {id: 64, skin: 0, ..}, _) => MonsterType::Hildebear, + (RawMapEnemy {id: 64, skin: 1, ..}, _) => MonsterType::Hildeblue, + (RawMapEnemy {id: 65, skin: 0, ..}, _) => MonsterType::RagRappy, + (RawMapEnemy {id: 65, skin: 1, ..}, _) => MonsterType::AlRappy, (RawMapEnemy {id: 66, ..}, _) => MonsterType::Monest, (RawMapEnemy {id: 67, field2: 0, ..}, _) => MonsterType::SavageWolf, (RawMapEnemy {id: 67, ..}, _) => MonsterType::BarbarousWolf, @@ -103,13 +106,16 @@ impl MapEnemy { (RawMapEnemy {id: 68, skin: 2, ..}, _) => MonsterType::Gigobooma, (RawMapEnemy {id: 96, ..}, _) => MonsterType::GrassAssassin, (RawMapEnemy {id: 97, ..}, _) => MonsterType::PoisonLily, - // (RawMapEnemy {id: 97, ..}, _) => MonsterType::NarLily, + // (RawMapEnemy {id: 97, skin: 0, ..}, _) => MonsterType::PoisonLily, + // (RawMapEnemy {id: 97, skin: 1, ..}, _) => MonsterType::NarLily, (RawMapEnemy {id: 98, ..}, _) => MonsterType::NanoDragon, (RawMapEnemy {id: 99, skin: 0, ..}, _) => MonsterType::EvilShark, (RawMapEnemy {id: 99, skin: 1, ..}, _) => MonsterType::PalShark, (RawMapEnemy {id: 99, skin: 2, ..}, _) => MonsterType::GuilShark, (RawMapEnemy {id: 100, ..}, _) => MonsterType::PofuillySlime, - // (RawMapEnemy {id: 100, ..}, _) => MonsterType::PouillySlime, + // (RawMapEnemy {id: 100, skin: 0, ..}, _) => MonsterType::PofuillySlime, + // (RawMapEnemy {id: 100, skin: 1, ..}, _) => MonsterType::PouillySlime, + // (RawMapEnemy {id: 100, skin: 2, ..}, _) => MonsterType::PofuillySlime, (RawMapEnemy {id: 101, ..}, _) => MonsterType::PanArms, (RawMapEnemy {id: 128, skin: 0, ..}, _) => MonsterType::Dubchic, (RawMapEnemy {id: 128, skin: 1, ..}, _) => MonsterType::Gillchic, @@ -122,6 +128,7 @@ impl MapEnemy { (RawMapEnemy {id: 160, ..}, _) => MonsterType::Delsaber, (RawMapEnemy {id: 161, ..}, _) => MonsterType::ChaosSorcerer, (RawMapEnemy {id: 162, ..}, _) => MonsterType::DarkGunner, + (RawMapEnemy {id: 163, ..}, _) => MonsterType::DeathGunner, (RawMapEnemy {id: 164, ..}, _) => MonsterType::ChaosBringer, (RawMapEnemy {id: 165, ..}, _) => MonsterType::DarkBelra, (RawMapEnemy {id: 166, skin: 0, ..}, _) => MonsterType::Dimenian, @@ -143,17 +150,16 @@ impl MapEnemy { MapArea::JungleAreaNorth | MapArea::JungleAreaEast | MapArea::Mountain | MapArea::Seaside | MapArea::SeasideNight | MapArea::Cca | MapArea::GalGryphon | MapArea::SeabedUpper | MapArea::SeabedLower | MapArea::OlgaFlow => { match (enemy, episode) { - (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildebear, - // (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildeblue, - (RawMapEnemy {id: 65, ..}, _) => MonsterType::RagRappy, - // (RawMapEnemy {id: 65, ..}, _) => MonsterType::EventRappy, + (RawMapEnemy {id: 64, skin: 0, ..}, _) => MonsterType::Hildebear, + (RawMapEnemy {id: 64, skin: 1, ..}, _) => MonsterType::Hildeblue, + (RawMapEnemy {id: 65, skin: 0, ..}, _) => MonsterType::RagRappy, + (RawMapEnemy {id: 65, skin: 1, ..}, _) => MonsterType::EventRappy, (RawMapEnemy {id: 66, ..}, _) => MonsterType::Monest, (RawMapEnemy {id: 67, field2: 0, ..}, _) => MonsterType::SavageWolf, (RawMapEnemy {id: 67, ..}, _) => MonsterType::BarbarousWolf, (RawMapEnemy {id: 96, ..}, _) => MonsterType::GrassAssassin, - (RawMapEnemy {id: 97, ..}, _) => MonsterType::PoisonLily, - // (RawMapEnemy {id: 97, ..}, _) => MonsterType::NarLily, - // (RawMapEnemy {id: 97, ..}, _) => MonsterType::DelLily, + (RawMapEnemy {id: 97, skin: 0, ..}, _) => MonsterType::PoisonLily, + (RawMapEnemy {id: 97, skin: 1, ..}, _) => MonsterType::NarLily, (RawMapEnemy {id: 101, ..}, _) => MonsterType::PanArms, (RawMapEnemy {id: 128, skin: 0, ..}, _) => MonsterType::Dubchic, (RawMapEnemy {id: 128, skin: 1, ..}, _) => MonsterType::Gillchic, @@ -213,38 +219,38 @@ impl MapEnemy { MapArea::CraterEast | MapArea::CraterWest | MapArea::CraterSouth | MapArea::CraterNorth | MapArea::CraterInterior => { match (enemy, episode) { - (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::SandRappyCrater, - // (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::DelRappyCrater, + (RawMapEnemy {id: 65, skin: 0, ..}, Episode::Four) => MonsterType::SandRappyCrater, + (RawMapEnemy {id: 65, skin: 1, ..}, Episode::Four) => MonsterType::DelRappyCrater, (RawMapEnemy {id: 272, ..}, _) => MonsterType::Astark, (RawMapEnemy {id: 273, field2: 0, ..}, _) => MonsterType::SatelliteLizardCrater, (RawMapEnemy {id: 273, ..}, _) => MonsterType::YowieCrater, - (RawMapEnemy {id: 276, ..}, _) => MonsterType::ZuCrater, - // (RawMapEnemy {id: 276, ..}, _) => MonsterType::PazuzuCrater, + (RawMapEnemy {id: 276, skin: 0, ..}, _) => MonsterType::ZuCrater, + (RawMapEnemy {id: 276, skin: 1, ..}, _) => MonsterType::PazuzuCrater, (RawMapEnemy {id: 277, skin: 0, ..}, _) => MonsterType::Boota, (RawMapEnemy {id: 277, skin: 1, ..}, _) => MonsterType::ZeBoota, (RawMapEnemy {id: 277, skin: 2, ..}, _) => MonsterType::BaBoota, - (RawMapEnemy {id: 278, ..}, _) => MonsterType::Dorphon, - // (RawMapEnemy {id: 278, ..}, _) => MonsterType::DorphonEclair, + (RawMapEnemy {id: 278, skin: 0, ..}, _) => MonsterType::Dorphon, + (RawMapEnemy {id: 278, skin: 1, ..}, _) => MonsterType::DorphonEclair, _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) } }, MapArea::SubDesert1 | MapArea::SubDesert2 | MapArea::SubDesert3 | MapArea::SaintMillion => { match (enemy, episode) { - (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::SandRappyDesert, - // (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::DelRappyDesert, + (RawMapEnemy {id: 65, skin: 0, ..}, Episode::Four) => MonsterType::SandRappyDesert, + (RawMapEnemy {id: 65, skin: 1, ..}, Episode::Four) => MonsterType::DelRappyDesert, (RawMapEnemy {id: 273, field2: 0, ..}, _) => MonsterType::SatelliteLizardDesert, (RawMapEnemy {id: 273, ..}, _) => MonsterType::YowieDesert, - (RawMapEnemy {id: 274, ..}, _) => MonsterType::MerissaA, - // (RawMapEnemy {id: 274, ..}, _) => MonsterType::MerissaAA, + (RawMapEnemy {id: 274, skin: 0, ..}, _) => MonsterType::MerissaA, + (RawMapEnemy {id: 274, skin: 1, ..}, _) => MonsterType::MerissaAA, (RawMapEnemy {id: 275, ..}, _) => MonsterType::Girtablulu, - (RawMapEnemy {id: 276, ..}, _) => MonsterType::ZuDesert, - // (RawMapEnemy {id: 276, ..}, _) => MonsterType::PazuzuDesert, + (RawMapEnemy {id: 276, skin: 0, ..}, _) => MonsterType::ZuDesert, + (RawMapEnemy {id: 276, skin: 1, ..}, _) => MonsterType::PazuzuDesert, (RawMapEnemy {id: 279, skin: 0, ..}, _) => MonsterType::Goran, (RawMapEnemy {id: 279, skin: 1, ..}, _) => MonsterType::PyroGoran, (RawMapEnemy {id: 279, skin: 2, ..}, _) => MonsterType::GoranDetonator, (RawMapEnemy {id: 281, skin: 0, ..}, _) => MonsterType::SaintMillion, - (RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Shambertin, - // (RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Kondrieu, + (RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Shambertin, // TODO: don't guess the skin + (RawMapEnemy {id: 281, skin: 2, ..}, _) => MonsterType::Kondrieu, // TODO: don't guess the skin _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) } }, @@ -258,6 +264,7 @@ impl MapEnemy { dropped_item: false, gave_exp: false, player_hit: [false; 4], + shiny: false, }) } @@ -269,6 +276,14 @@ impl MapEnemy { dropped_item: false, gave_exp: false, player_hit: [false; 4], + shiny: false, + } + } + + pub fn set_shiny(self) -> MapEnemy { + MapEnemy { + shiny: true, + ..self } } } diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs index e1558d8..7f18e50 100644 --- a/src/ship/map/maps.rs +++ b/src/ship/map/maps.rs @@ -12,6 +12,8 @@ use crate::ship::room::{Episode, RoomMode}; // TODO: don't use * use crate::ship::map::*; +use rand::{Rng}; + pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec> { let mut object_data = Vec::new(); @@ -30,19 +32,89 @@ fn objects_from_map_data(path: PathBuf, episode: &Episode, map_area: &MapArea) - fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> Vec> { let enemy = MapEnemy::from_raw(raw_enemy, episode, map_area); + /* + TODO: load rare monster rates config + */ enemy .map_or(vec![None], |monster| { - let mut monsters = vec![Some(monster)]; - + let mut monsters = Vec::new(); + monsters.push(Some(monster)); + match monster.monster { - MonsterType::Monest => { - for _ in 0..30 { - monsters.push(Some(MapEnemy::new(MonsterType::Mothmant, monster.map_area))); + // TODO: make real spawn rates + // TODO: specific ep 2 event rappies + MonsterType::RagRappy => {if rand::thread_rng().gen_range(0, 100) < 11 { + 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 { + 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 { + 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 { + 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 copies for _ in 0..4 { - monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime, monster.map_area))); + if rand::thread_rng().gen_range(0, 100) < 11 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + monsters.pop(); + monsters.push(Some(MapEnemy::new(MonsterType::Kondrieu, monster.map_area).set_shiny())) + }}, + + + MonsterType::Monest => { + for _ in 0..30 { + monsters.push(Some(MapEnemy::new(MonsterType::Mothmant, monster.map_area))); } }, MonsterType::PanArms => { @@ -309,5 +381,26 @@ impl Maps { self.enemy_data = enemies; self.object_data = objects; } -} + pub fn get_rare_monster_list(&self) -> Vec { + let mut rare_monsters = vec![0xFFFF; 16]; + let shiny: Vec<(usize, &Option)> = self.enemy_data.iter() + .enumerate() + .filter(|(_,m)| { + if m.is_some() { + m.unwrap().shiny + } else { + false + } + }) + .collect(); + for i in 0..shiny.len() { + if let Some(j) = rare_monsters.iter().position(|&x| x == 0xFFFF) { + rare_monsters[j] = shiny[i].0 as u16; + } else { + break + } + } + rare_monsters + } +} diff --git a/src/ship/packet/builder/room.rs b/src/ship/packet/builder/room.rs index 1effe5d..3fe196a 100644 --- a/src/ship/packet/builder/room.rs +++ b/src/ship/packet/builder/room.rs @@ -6,6 +6,7 @@ use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationEr use crate::ship::room::RoomState; use crate::ship::items::ItemManager; use crate::ship::packet::builder::{player_header, player_info}; +use std::convert::TryInto; pub fn join_room(id: ClientId, clients: &Clients, @@ -71,3 +72,8 @@ pub fn add_to_room(_id: ClientId, }) } +pub fn build_rare_monster_list(rare_monster_vec: Vec) -> Result { + Ok(RareMonsterList { + ids: rare_monster_vec.try_into().unwrap_or([0xFFFFu16; 16]), + }) +} \ No newline at end of file diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index c3be9dc..9a85def 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -108,13 +108,17 @@ pub fn done_bursting(id: ClientId, rooms: &mut Rooms) -> Box + Send> { let area = client_location.get_area(id).unwrap(); + let mut rare_monster_list: Option> = None; if let RoomLobby::Room(room_id) = area { if let Some(room) = rooms.get_mut(room_id.0).unwrap().as_mut() { room.bursting = false; - } + rare_monster_list = Some(room.maps.get_rare_monster_list()); + }); } let area_client = client_location.get_local_client(id).unwrap(); // TODO: unwrap - Box::new(client_location.get_client_neighbors(id).unwrap().into_iter() // TODO: unwrap + + let mut result: Box + Send> = Box::new( + client_location.get_client_neighbors(id).unwrap().into_iter() // TODO: unwrap .map(move |client| { vec![ (client.client, SendShipPacket::Message(Message::new(GameMessage::BurstDone(BurstDone { @@ -122,7 +126,16 @@ pub fn done_bursting(id: ClientId, target: 0 })))), ] - }).flatten()) + }) + .flatten() + ); + + if rare_monster_list.is_some() { + let rare_monster_packet = SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_monster_list.unwrap()).unwrap()); // TODO: don't double unwrap + result = Box::new(result.chain(vec![(id, rare_monster_packet)])); + } + + result } pub fn request_room_list(id: ClientId, diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 8091647..250f2c0 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -184,6 +184,7 @@ pub enum SendShipPacket { DoneLoadingQuest(DoneLoadingQuest), BankItemList(BankItemList), RedirectClient(RedirectClient), + RareMonsterList(RareMonsterList), } impl SendServerPacket for SendShipPacket { @@ -221,6 +222,7 @@ impl SendServerPacket for SendShipPacket { SendShipPacket::DoneLoadingQuest(pkt) => pkt.as_bytes(), SendShipPacket::BankItemList(pkt) => pkt.as_bytes(), SendShipPacket::RedirectClient(pkt) => pkt.as_bytes(), + SendShipPacket::RareMonsterList(pkt) => pkt.as_bytes(), } } } @@ -384,7 +386,6 @@ impl ShipServerStateBuilder { } } - pub struct Block { client_location: Box, pub rooms: Box, From 2a7e793b23ba452cc550bf86a0c67c766dac2d14 Mon Sep 17 00:00:00 2001 From: andy Date: Fri, 25 Jun 2021 23:00:57 +0000 Subject: [PATCH 02/15] ep1 doesn't have easter rappies --- data/battle_param/ep1_multi_hard.toml | 2 +- data/battle_param/ep1_multi_normal.toml | 2 +- data/battle_param/ep1_multi_ultimate.toml | 2 +- data/battle_param/ep1_multi_veryhard.toml | 2 +- data/battle_param/ep1_solo_hard.toml | 2 +- data/battle_param/ep1_solo_normal.toml | 2 +- data/battle_param/ep1_solo_ultimate.toml | 2 +- data/battle_param/ep1_solo_veryhard.toml | 2 +- src/ship/packet/handler/room.rs | 2 +- src/ship/ship.rs | 1 - 10 files changed, 9 insertions(+), 10 deletions(-) diff --git a/data/battle_param/ep1_multi_hard.toml b/data/battle_param/ep1_multi_hard.toml index 97461cc..4dbc97d 100644 --- a/data/battle_param/ep1_multi_hard.toml +++ b/data/battle_param/ep1_multi_hard.toml @@ -229,7 +229,7 @@ lck = 20 esp = 15 exp = 40 -[EasterRappy] +[AlRappy] atp = 527 mst = 0 evp = 90 diff --git a/data/battle_param/ep1_multi_normal.toml b/data/battle_param/ep1_multi_normal.toml index db12b5a..4a529e1 100644 --- a/data/battle_param/ep1_multi_normal.toml +++ b/data/battle_param/ep1_multi_normal.toml @@ -229,7 +229,7 @@ lck = 10 esp = 0 exp = 4 -[EasterRappy] +[AlRappy] atp = 184 mst = 0 evp = 45 diff --git a/data/battle_param/ep1_multi_ultimate.toml b/data/battle_param/ep1_multi_ultimate.toml index ce9100c..c30e089 100644 --- a/data/battle_param/ep1_multi_ultimate.toml +++ b/data/battle_param/ep1_multi_ultimate.toml @@ -229,7 +229,7 @@ lck = 30 esp = 35 exp = 256 -[EasterRappy] +[AlRappy] atp = 2100 mst = 0 evp = 419 diff --git a/data/battle_param/ep1_multi_veryhard.toml b/data/battle_param/ep1_multi_veryhard.toml index e070e92..566334d 100644 --- a/data/battle_param/ep1_multi_veryhard.toml +++ b/data/battle_param/ep1_multi_veryhard.toml @@ -229,7 +229,7 @@ lck = 35 esp = 30 exp = 88 -[EasterRappy] +[AlRappy] atp = 913 mst = 0 evp = 194 diff --git a/data/battle_param/ep1_solo_hard.toml b/data/battle_param/ep1_solo_hard.toml index be12b97..469f988 100644 --- a/data/battle_param/ep1_solo_hard.toml +++ b/data/battle_param/ep1_solo_hard.toml @@ -229,7 +229,7 @@ lck = 20 esp = 15 exp = 40 -[EasterRappy] +[AlRappy] atp = 443 mst = 0 evp = 28 diff --git a/data/battle_param/ep1_solo_normal.toml b/data/battle_param/ep1_solo_normal.toml index 6a409c3..8012947 100644 --- a/data/battle_param/ep1_solo_normal.toml +++ b/data/battle_param/ep1_solo_normal.toml @@ -229,7 +229,7 @@ lck = 10 esp = 0 exp = 4 -[EasterRappy] +[AlRappy] atp = 150 mst = 0 evp = 5 diff --git a/data/battle_param/ep1_solo_ultimate.toml b/data/battle_param/ep1_solo_ultimate.toml index 3d3e0ef..2e076fb 100644 --- a/data/battle_param/ep1_solo_ultimate.toml +++ b/data/battle_param/ep1_solo_ultimate.toml @@ -229,7 +229,7 @@ lck = 30 esp = 30 exp = 256 -[EasterRappy] +[AlRappy] atp = 1800 mst = 0 evp = 276 diff --git a/data/battle_param/ep1_solo_veryhard.toml b/data/battle_param/ep1_solo_veryhard.toml index b6ecd53..306604c 100644 --- a/data/battle_param/ep1_solo_veryhard.toml +++ b/data/battle_param/ep1_solo_veryhard.toml @@ -229,7 +229,7 @@ lck = 35 esp = 30 exp = 88 -[EasterRappy] +[AlRappy] atp = 707 mst = 0 evp = 55 diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index 9a85def..283c2e4 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -113,7 +113,7 @@ pub fn done_bursting(id: ClientId, if let Some(room) = rooms.get_mut(room_id.0).unwrap().as_mut() { room.bursting = false; rare_monster_list = Some(room.maps.get_rare_monster_list()); - }); + }; } let area_client = client_location.get_local_client(id).unwrap(); // TODO: unwrap diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 250f2c0..09d89bb 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -376,7 +376,6 @@ impl ShipServerStateBuilder { ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)), port: self.port.unwrap_or(SHIP_PORT), shops: Box::new(ItemShops::default()), - blocks: Blocks(blocks), auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())), From 7c7cf95942ddf0ff803f2ecbe882a6c2e0e5bae5 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 28 Jun 2021 17:42:50 +0000 Subject: [PATCH 03/15] various config files --- data/battle_param/ep1_rare_monster.toml | 15 ++++++++++++ data/battle_param/ep2_rare_monster.toml | 13 +++++++++++ data/battle_param/ep4_rare_monster.toml | 27 ++++++++++++++++++++++ data/battle_param/global_rare_monster.toml | 11 +++++++++ src/ship/map/maps.rs | 2 +- 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 data/battle_param/ep1_rare_monster.toml create mode 100644 data/battle_param/ep2_rare_monster.toml create mode 100644 data/battle_param/ep4_rare_monster.toml create mode 100644 data/battle_param/global_rare_monster.toml diff --git a/data/battle_param/ep1_rare_monster.toml b/data/battle_param/ep1_rare_monster.toml new file mode 100644 index 0000000..b7e62c4 --- /dev/null +++ b/data/battle_param/ep1_rare_monster.toml @@ -0,0 +1,15 @@ +# 1/100 = 0.01 +# 1/256 = 0.00390625 +# 1/512 = 0.001953125 + +[[Hildebear]] +rate = 0.01 + +[[RagRappy]] +rate = 0.01 + +[[PoisonLily]] +rate = 0.01 + +[[PofuillySlime]] +rate = 0.01 \ No newline at end of file diff --git a/data/battle_param/ep2_rare_monster.toml b/data/battle_param/ep2_rare_monster.toml new file mode 100644 index 0000000..2b6f916 --- /dev/null +++ b/data/battle_param/ep2_rare_monster.toml @@ -0,0 +1,13 @@ +# 1/100 = 0.01 +# 1/256 = 0.00390625 +# 1/512 = 0.001953125 + +[[Hildebear]] +rate = 0.01 + +[[RagRappy]] +rate = 0.01 + +[[PoisonLily]] +rate = 0.01 + diff --git a/data/battle_param/ep4_rare_monster.toml b/data/battle_param/ep4_rare_monster.toml new file mode 100644 index 0000000..9a4b244 --- /dev/null +++ b/data/battle_param/ep4_rare_monster.toml @@ -0,0 +1,27 @@ +# 1/100 = 0.01 +# 1/256 = 0.00390625 +# 1/512 = 0.001953125 + +[[SandRappyCrater]] +rate = 0.01 + +[[ZuCrater]] +rate = 0.01 + +[[Dorphon]] +rate = 0.01 + +[[SandRappyDesert]] +rate = 0.01 + +[[ZuDesert]] +rate = 0.01 + +[[MerissaA]] +rate = 0.01 + +[[Shambertin]] +rate = 0.1 + +[[SaintMillion]] +rate = 0.1 \ No newline at end of file diff --git a/data/battle_param/global_rare_monster.toml b/data/battle_param/global_rare_monster.toml new file mode 100644 index 0000000..8a3ca93 --- /dev/null +++ b/data/battle_param/global_rare_monster.toml @@ -0,0 +1,11 @@ +# 1/100 = 0.01 +# 1/256 = 0.00390625 +# 1/512 = 0.001953125 + +# Everything that isn't Kondrieu +[[Grunt]] +rate = 0.01 + +# Kondrieu +[[Boss]] +rate = 0.1 \ No newline at end of file diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs index 7f18e50..866ae12 100644 --- a/src/ship/map/maps.rs +++ b/src/ship/map/maps.rs @@ -73,7 +73,7 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> }, MonsterType::PouillySlime => { // guaranteed rare slime already pushed - // roll for the other 3 copies + // roll for the other 3 (4?) copies for _ in 0..4 { if rand::thread_rng().gen_range(0, 100) < 11 { monsters.push(Some(MapEnemy::new(MonsterType::PouillySlime, monster.map_area).set_shiny())) From f5946013a142330a2466572dbea439ad87df9d79 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 12 Jul 2021 02:24:31 +0000 Subject: [PATCH 04/15] rare monster appear config files. init rare monster appear table stuff --- data/battle_param/ep1_rare_monster.toml | 8 ++--- data/battle_param/ep2_rare_monster.toml | 6 ++-- data/battle_param/ep4_rare_monster.toml | 16 ++++----- src/ship/drops/mod.rs | 11 ++++++ src/ship/map/enemy.rs | 45 +++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 15 deletions(-) diff --git a/data/battle_param/ep1_rare_monster.toml b/data/battle_param/ep1_rare_monster.toml index b7e62c4..b806f9a 100644 --- a/data/battle_param/ep1_rare_monster.toml +++ b/data/battle_param/ep1_rare_monster.toml @@ -3,13 +3,13 @@ # 1/512 = 0.001953125 [[Hildebear]] -rate = 0.01 +appear_rate = 0.01 [[RagRappy]] -rate = 0.01 +appear_rate = 0.01 [[PoisonLily]] -rate = 0.01 +appear_rate = 0.01 [[PofuillySlime]] -rate = 0.01 \ No newline at end of file +appear_rate = 0.01 \ No newline at end of file diff --git a/data/battle_param/ep2_rare_monster.toml b/data/battle_param/ep2_rare_monster.toml index 2b6f916..948b189 100644 --- a/data/battle_param/ep2_rare_monster.toml +++ b/data/battle_param/ep2_rare_monster.toml @@ -3,11 +3,11 @@ # 1/512 = 0.001953125 [[Hildebear]] -rate = 0.01 +appear_rate = 0.01 [[RagRappy]] -rate = 0.01 +appear_rate = 0.01 [[PoisonLily]] -rate = 0.01 +appear_rate = 0.01 diff --git a/data/battle_param/ep4_rare_monster.toml b/data/battle_param/ep4_rare_monster.toml index 9a4b244..9f807c2 100644 --- a/data/battle_param/ep4_rare_monster.toml +++ b/data/battle_param/ep4_rare_monster.toml @@ -3,25 +3,25 @@ # 1/512 = 0.001953125 [[SandRappyCrater]] -rate = 0.01 +appear_rate = 0.01 [[ZuCrater]] -rate = 0.01 +appear_rate = 0.01 [[Dorphon]] -rate = 0.01 +appear_rate = 0.01 [[SandRappyDesert]] -rate = 0.01 +appear_rate = 0.01 [[ZuDesert]] -rate = 0.01 +appear_rate = 0.01 [[MerissaA]] -rate = 0.01 +appear_rate = 0.01 [[Shambertin]] -rate = 0.1 +appear_rate = 0.1 [[SaintMillion]] -rate = 0.1 \ No newline at end of file +appear_rate = 0.1 \ No newline at end of file diff --git a/src/ship/drops/mod.rs b/src/ship/drops/mod.rs index 8de0408..4ada11e 100644 --- a/src/ship/drops/mod.rs +++ b/src/ship/drops/mod.rs @@ -55,6 +55,17 @@ pub fn load_data_file(episode: Episode, difficul toml::from_str::(s.as_str()).unwrap() } +// this is just copypaste +pub fn load_rare_monster_file(episode: Episode) -> T { + let mut path = PathBuf::from("data/battle_param/"); + path.push(episode.to_string().to_lowercase()); + path.push("_rare_monster.toml"); + + let mut f = File::open(path).unwrap(); + let mut s = String::new(); + f.read_to_string(&mut s); + toml::from_str::(s.as_str()).unwrap() +} #[derive(Debug, Serialize, Deserialize, Copy, Clone)] pub enum MonsterDropType { diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs index 5f9a60e..62c2155 100644 --- a/src/ship/map/enemy.rs +++ b/src/ship/map/enemy.rs @@ -1,5 +1,6 @@ // TOOD: `pub(super) for most of these?` use std::io::{Read}; +use std::collections::HashMap; use byteorder::{LittleEndian, ReadBytesExt}; use thiserror::Error; @@ -9,6 +10,10 @@ use crate::ship::room::Episode; use crate::ship::map::*; +use rand::{Rng, SeedableRng}; +use serde::{Serialize, Deserialize}; +use crate::ship::drops::{load_rare_monster_file}; + #[derive(Debug, Copy, Clone)] pub struct RawMapEnemy { id: u32, @@ -70,6 +75,46 @@ pub enum MapEnemyError { MapAreaError(#[from] MapAreaError), } +#[derive(Debug, Serialize, Deserialize)] +pub struct RareMonsterAppearRate(pub f32); + +// making this `pub type` doesn't allow `impl`s to be defined? +#[derive(Debug, Serialize, Deserialize)] +pub struct RareMonsterAppearTable { + appear_rate: HashMap, + seed: u32, +} + +impl RareMonsterAppearTable { + pub fn new(episode: Episode, room_seed: u32) -> RareMonsterAppearTable { + let cfg: HashMap = load_rare_monster_file(episode); + + let appear_rates: HashMap = cfg + .into_iter() + .map(|(monster, appear_rate)| { + let monster: MonsterType = monster.parse().unwrap(); + let appear_rate = RareMonsterAppearRate(appear_rate); + (monster, appear_rate) + }) + .collect(); + + RareMonsterAppearTable { + appear_rate: appear_rates, + seed: room_seed, + } + } + + pub fn roll_appearance(&self, monster: &MonsterType) -> bool { + let mut rng = rand_chacha::ChaChaRng::seed_from_u64(self.seed as u64); + if rng.gen::() < self.appear_rate.get(monster).unwrap_or(&RareMonsterAppearRate(0.0f32)).0 { + true + } + else { + false + } + } +} + #[derive(Debug, Copy, Clone)] pub struct MapEnemy { From 080e939e569b8c9978bd461dd003d0f7d7d723f4 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 12 Jul 2021 19:32:16 +0000 Subject: [PATCH 05/15] differentiate between player loading quest and server preloading all quests --- src/ship/packet/handler/quest.rs | 2 +- src/ship/ship.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ship/packet/handler/quest.rs b/src/ship/packet/handler/quest.rs index 41412ac..90359c3 100644 --- a/src/ship/packet/handler/quest.rs +++ b/src/ship/packet/handler/quest.rs @@ -68,7 +68,7 @@ pub fn quest_detail(id: ClientId, questdetailrequest: &QuestDetailRequest, quest Ok(Box::new(vec![(id, SendShipPacket::QuestDetail(qd))].into_iter())) } -pub fn load_quest(id: ClientId, questmenuselect: &QuestMenuSelect, quests: &QuestList, clients: &mut Clients, client_location: &ClientLocation, rooms: &mut Rooms) +pub fn player_chose_quest(id: ClientId, questmenuselect: &QuestMenuSelect, quests: &QuestList, clients: &mut Clients, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { let (_, category_quests) = quests.iter() .nth(questmenuselect.category as usize) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 09d89bb..09e0afa 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -595,7 +595,7 @@ impl ServerState for ShipServerState { }, RecvShipPacket::QuestMenuSelect(questmenuselect) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::quest::load_quest(id, questmenuselect, &self.quests, &mut self.clients, &block.client_location, &mut block.rooms)? + handler::quest::player_chose_quest(id, questmenuselect, &self.quests, &mut self.clients, &block.client_location, &mut block.rooms)? }, RecvShipPacket::MenuDetail(_menudetail) => { //unreachable!(); From 39660f0cb8cf0c7616e21fbae77e08c7819b9b1f Mon Sep 17 00:00:00 2001 From: andy Date: Sun, 18 Jul 2021 18:40:10 +0000 Subject: [PATCH 06/15] load rare monster config file each time a room is created --- data/battle_param/ep1_rare_monster.toml | 16 ++-- data/battle_param/ep2_rare_monster.toml | 12 +-- data/battle_param/ep4_rare_monster.toml | 32 +++----- src/ship/drops/mod.rs | 4 +- src/ship/map/area.rs | 48 ++++++++++++ src/ship/map/enemy.rs | 81 ++++++++++++++++++--- src/ship/map/maps.rs | 97 ++++++++++++++++++------- src/ship/map/mod.rs | 2 +- src/ship/packet/handler/room.rs | 3 +- src/ship/room.rs | 12 ++- 10 files changed, 221 insertions(+), 86 deletions(-) diff --git a/data/battle_param/ep1_rare_monster.toml b/data/battle_param/ep1_rare_monster.toml index b806f9a..1e1f3e0 100644 --- a/data/battle_param/ep1_rare_monster.toml +++ b/data/battle_param/ep1_rare_monster.toml @@ -1,15 +1,9 @@ +# 1/10 = 0.1 # 1/100 = 0.01 # 1/256 = 0.00390625 # 1/512 = 0.001953125 -[[Hildebear]] -appear_rate = 0.01 - -[[RagRappy]] -appear_rate = 0.01 - -[[PoisonLily]] -appear_rate = 0.01 - -[[PofuillySlime]] -appear_rate = 0.01 \ No newline at end of file +Hildebear = 1.0 +RagRappy = 0.1 +PoisonLily = 0.1 +PofuillySlime = 0.1 \ No newline at end of file diff --git a/data/battle_param/ep2_rare_monster.toml b/data/battle_param/ep2_rare_monster.toml index 948b189..290f727 100644 --- a/data/battle_param/ep2_rare_monster.toml +++ b/data/battle_param/ep2_rare_monster.toml @@ -1,13 +1,9 @@ +# 1/10 = 0.1 # 1/100 = 0.01 # 1/256 = 0.00390625 # 1/512 = 0.001953125 -[[Hildebear]] -appear_rate = 0.01 - -[[RagRappy]] -appear_rate = 0.01 - -[[PoisonLily]] -appear_rate = 0.01 +Hildebear = 0.01 +RagRappy = 0.01 +PoisonLily = 0.01 diff --git a/data/battle_param/ep4_rare_monster.toml b/data/battle_param/ep4_rare_monster.toml index 9f807c2..0ddddbf 100644 --- a/data/battle_param/ep4_rare_monster.toml +++ b/data/battle_param/ep4_rare_monster.toml @@ -1,27 +1,13 @@ +# 1/10 = 0.1 # 1/100 = 0.01 # 1/256 = 0.00390625 # 1/512 = 0.001953125 -[[SandRappyCrater]] -appear_rate = 0.01 - -[[ZuCrater]] -appear_rate = 0.01 - -[[Dorphon]] -appear_rate = 0.01 - -[[SandRappyDesert]] -appear_rate = 0.01 - -[[ZuDesert]] -appear_rate = 0.01 - -[[MerissaA]] -appear_rate = 0.01 - -[[Shambertin]] -appear_rate = 0.1 - -[[SaintMillion]] -appear_rate = 0.1 \ No newline at end of file +SandRappyCrater = 0.01 +ZuCrater = 0.01 +Dorphon = 0.01 +SandRappyDesert = 0.01 +ZuDesert = 0.01 +MerissaA = 0.01 +Shambertin = 0.1 +SaintMillion = 0.1 \ No newline at end of file diff --git a/src/ship/drops/mod.rs b/src/ship/drops/mod.rs index 4ada11e..427da74 100644 --- a/src/ship/drops/mod.rs +++ b/src/ship/drops/mod.rs @@ -58,8 +58,8 @@ pub fn load_data_file(episode: Episode, difficul // this is just copypaste pub fn load_rare_monster_file(episode: Episode) -> T { let mut path = PathBuf::from("data/battle_param/"); - path.push(episode.to_string().to_lowercase()); - path.push("_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 s = String::new(); diff --git a/src/ship/map/area.rs b/src/ship/map/area.rs index a26aa19..9da3374 100644 --- a/src/ship/map/area.rs +++ b/src/ship/map/area.rs @@ -208,6 +208,54 @@ impl MapArea { // MapArea::TestMapEp4 => 10, } } + + pub fn to_episode(self) -> Episode { + match self { + Pioneer2Ep1 => Episode::One, + Forest1 => Episode::One, + Forest2 => Episode::One, + Caves1 => Episode::One, + Caves2 => Episode::One, + Caves3 => Episode::One, + Mines1 => Episode::One, + Mines2 => Episode::One, + Ruins1 => Episode::One, + Ruins2 => Episode::One, + Ruins3 => Episode::One, + Dragon => Episode::One, + DeRolLe => Episode::One, + VolOpt => Episode::One, + DarkFalz => Episode::One, + Pioneer2Ep2 => Episode::Two, + VrTempleAlpha => Episode::Two, + VrTempleBeta => Episode::Two, + VrSpaceshipAlpha => Episode::Two, + VrSpaceshipBeta => Episode::Two, + Cca => Episode::Two, + JungleAreaNorth => Episode::Two, + JungleAreaEast => Episode::Two, + Mountain => Episode::Two, + Seaside => Episode::Two, + SeabedUpper => Episode::Two, + SeabedLower => Episode::Two, + GalGryphon => Episode::Two, + OlgaFlow => Episode::Two, + BarbaRay => Episode::Two, + GolDragon => Episode::Two, + SeasideNight => Episode::Two, + Tower => Episode::Two, + Pioneer2Ep4 => Episode::Four, + CraterEast => Episode::Four, + CraterWest => Episode::Four, + CraterSouth => Episode::Four, + CraterNorth => Episode::Four, + CraterInterior => Episode::Four, + SubDesert1 => Episode::Four, + SubDesert2 => Episode::Four, + SubDesert3 => Episode::Four, + SaintMillion => Episode::Four, + } + } } diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs index 62c2155..6d4d7c7 100644 --- a/src/ship/map/enemy.rs +++ b/src/ship/map/enemy.rs @@ -75,25 +75,27 @@ pub enum MapEnemyError { MapAreaError(#[from] MapAreaError), } -#[derive(Debug, Serialize, Deserialize)] -pub struct RareMonsterAppearRate(pub f32); +// TODO: is this even needed/used? +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub type RareMonsterAppearRate: HashMap, // making this `pub type` doesn't allow `impl`s to be defined? -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct RareMonsterAppearTable { - appear_rate: HashMap, - seed: u32, + appear_rate: HashMap, + seed: u32 } impl RareMonsterAppearTable { pub fn new(episode: Episode, room_seed: u32) -> RareMonsterAppearTable { let cfg: HashMap = load_rare_monster_file(episode); + println!("got cfg: {:?}", cfg); - let appear_rates: HashMap = cfg + let appear_rates: HashMap = cfg .into_iter() - .map(|(monster, appear_rate)| { - let monster: MonsterType = monster.parse().unwrap(); - let appear_rate = RareMonsterAppearRate(appear_rate); + .map(|(monster, rate)| { + let monster: MonsterType = monster.parse().unwrap(); // TODO: don't unwrap! + let appear_rate = rate; (monster, appear_rate) }) .collect(); @@ -105,11 +107,14 @@ impl RareMonsterAppearTable { } pub fn roll_appearance(&self, monster: &MonsterType) -> bool { - let mut rng = rand_chacha::ChaChaRng::seed_from_u64(self.seed as u64); - if rng.gen::() < self.appear_rate.get(monster).unwrap_or(&RareMonsterAppearRate(0.0f32)).0 { + let mut rng = rand_chacha::ChaChaRng::seed_from_u64(self.seed as u64); // TODO: figure out how to seed with a u32 + println!("rolling for {:?} appearance", monster); + if rng.gen::() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) { + println!("its a rare!"); true } else { + println!("lol sucker"); false } } @@ -331,5 +336,59 @@ impl MapEnemy { ..self } } + + // TODO: does this actually do anything useful? + pub fn has_rare_appearance(self) -> bool { + 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 => true, + _ => false + } + } + + // 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?) + // pub fn set_rare_appearance(self) -> MapEnemy { + // match (self.monster, self.map_area.to_episode()) { + // (MonsterType::RagRappy, Episode::One) => {MapEnemy {monster: MonsterType::AlRappy, ..self}}, + // (MonsterType::RagRappy, Episode::Two) => {MapEnemy {monster: MonsterType::EventRappy, ..self}}, + // (MonsterType::Hildebear, _) => {MapEnemy {monster: MonsterType::Hildeblue, ..self}}, + // (MonsterType::PoisonLily, _) => {MapEnemy {monster: MonsterType::NarLily, ..self}}, + // (MonsterType::PofuillySlime, _) => {MapEnemy {monster: MonsterType::PouillySlime, ..self}}, + // (MonsterType::SandRappyCrater, _) => {MapEnemy {monster: MonsterType::DelRappyCrater, ..self}}, + // (MonsterType::ZuCrater, _) => {MapEnemy {monster: MonsterType::PazuzuCrater, ..self}}, + // (MonsterType::Dorphon, _) => {MapEnemy {monster: MonsterType::DorphonEclair, ..self}}, + // (MonsterType::SandRappyDesert, _) => {MapEnemy {monster: MonsterType::DelRappyDesert, ..self}}, + // (MonsterType::ZuDesert, _) => {MapEnemy {monster: MonsterType::PazuzuDesert, ..self}}, + // (MonsterType::MerissaA, _) => {MapEnemy {monster: MonsterType::MerissaAA, ..self}}, + // (MonsterType::SaintMillion, _) => {MapEnemy {monster: MonsterType::Kondrieu, ..self}}, + // (MonsterType::Shambertin, _) => {MapEnemy {monster: MonsterType::Kondrieu, ..self}}, + // _ => {self}, + // } + // } + + // in theory this should only be called on monsters we know can have rare types + pub fn roll_appearance_for_quest(self, rare_monster_table: &RareMonsterAppearTable) -> MapEnemy { + if rare_monster_table.roll_appearance(&self.monster) { + return self.set_rare_appearance() + } + self + } } diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs index 866ae12..71ef38d 100644 --- a/src/ship/map/maps.rs +++ b/src/ship/map/maps.rs @@ -12,7 +12,7 @@ use crate::ship::room::{Episode, RoomMode}; // TODO: don't use * use crate::ship::map::*; -use rand::{Rng}; +// use rand::{Rng}; pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec> { @@ -31,10 +31,10 @@ fn objects_from_map_data(path: PathBuf, episode: &Episode, map_area: &MapArea) - } fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> Vec> { +// fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy, rare_monster_table: RareMonsterAppearTable) -> Vec> { let enemy = MapEnemy::from_raw(raw_enemy, episode, map_area); - /* - TODO: load rare monster rates config - */ + + // TODO: load rare monster rates config enemy .map_or(vec![None], |monster| { let mut monsters = Vec::new(); @@ -43,7 +43,8 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> 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 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()))}, @@ -52,11 +53,13 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> } }}, - MonsterType::Hildebear => {if rand::thread_rng().gen_range(0, 100) < 11 { + // 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 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())) }}, @@ -64,7 +67,8 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> MonsterType::PofuillySlime => { monsters.pop(); for _ in 0..5 { - if rand::thread_rng().gen_range(0, 100) < 11 { + // 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))) @@ -75,42 +79,50 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> // 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 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 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 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 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 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 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 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 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 => { for _ in 0..30 { @@ -223,17 +235,21 @@ 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> { +// pub fn enemy_data_from_stream(cursor: &mut impl Read, map_area: &MapArea, episode: &Episode, rare_monster_table: enemy::RareMonsterAppearTable) -> Vec> { let mut enemy_data = Vec::new(); 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, rare_monster_table)); } enemy_data } fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode) -> Vec> { +// fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode, rare_monster_table: enemy::RareMonsterAppearTable) -> Vec> { let path = map_variant.dat_file(); 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, rare_monster_table) } @@ -252,7 +268,8 @@ pub struct Maps { } impl Maps { - pub fn new(room_mode: RoomMode) -> Maps { + // pub fn new(room_mode: RoomMode) -> Maps { + pub fn new(room_mode: RoomMode, rare_monster_table: &enemy::RareMonsterAppearTable) -> Maps { let map_variants = match (room_mode.episode(), room_mode.single_player()) { (Episode::One, 0) => { vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Online), @@ -344,16 +361,20 @@ impl Maps { _ => unreachable!() }; - let maps = Maps { - enemy_data: map_variants.iter().fold(Vec::new(), |mut enemy_data, map_variant| { - enemy_data.append(&mut enemy_data_from_map_data(map_variant, &room_mode.episode())); - enemy_data - }), - object_data: map_variants.iter().map(|map_variant| { - objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map) - }).flatten().collect(), - map_variants, + let mut maps = Maps { + enemy_data: map_variants.iter() + .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(), rare_monster_table)); + enemy_data + }), + object_data: map_variants.iter() + .map(|map_variant| { + objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map) + }).flatten().collect(), + map_variants, }; + maps.roll_monster_appearance(rare_monster_table); maps } @@ -378,7 +399,18 @@ impl Maps { } pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>) { + // pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>, rare_monster_table: RareMonsterAppearTable) { self.enemy_data = enemies; + + // 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; } @@ -403,4 +435,15 @@ impl Maps { } rare_monsters } + + pub fn roll_monster_appearance(&mut self, rare_monster_table: &RareMonsterAppearTable) { + self.enemy_data = self.enemy_data + .iter() + .map(|&x| if x.is_some() { + Some(x.unwrap().roll_appearance_for_quest(&rare_monster_table)) + } else { + x + }) + .collect(); + } } diff --git a/src/ship/map/mod.rs b/src/ship/map/mod.rs index f57c80c..6c54343 100644 --- a/src/ship/map/mod.rs +++ b/src/ship/map/mod.rs @@ -1,5 +1,5 @@ pub mod area; -mod enemy; +pub mod enemy; mod object; mod variant; mod maps; diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index 283c2e4..8c2ba8c 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -130,9 +130,10 @@ pub fn done_bursting(id: ClientId, .flatten() ); + // TODO: check how often `done_bursting` is called. ie: make sure it's only used when joining a room and not each time a player warps in a pipe if rare_monster_list.is_some() { let rare_monster_packet = SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_monster_list.unwrap()).unwrap()); // TODO: don't double unwrap - result = Box::new(result.chain(vec![(id, rare_monster_packet)])); + result = Box::new(result.chain(vec![(id, rare_monster_packet)])); // TODO: make sure we arent clobbering `result` here } result diff --git a/src/ship/room.rs b/src/ship/room.rs index 3aae5da..3f0918a 100644 --- a/src/ship/room.rs +++ b/src/ship/room.rs @@ -7,6 +7,7 @@ use crate::ship::drops::DropTable; use crate::entity::character::SectionID; use crate::ship::monster::{load_monster_stats_table, MonsterType, MonsterStats}; use crate::ship::map::area::MapAreaLookup; +use crate::ship::map::enemy::RareMonsterAppearTable; #[derive(Debug)] pub enum RoomCreationError { @@ -167,6 +168,7 @@ pub struct RoomState { pub bursting: bool, pub monster_stats: Box>, pub map_areas: MapAreaLookup, + pub rare_monster_table: Box, // items on ground // enemy info } @@ -231,13 +233,19 @@ impl RoomState { } }; + let random_seed = rand::thread_rng().gen(); + let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode(), random_seed); + Ok(RoomState { monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(room_mode))?), mode: room_mode, - random_seed: rand::thread_rng().gen(), + // random_seed: rand::thread_rng().gen(), + random_seed: random_seed, + rare_monster_table: Box::new(rare_monster_table.clone()), name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(), password: create_room.password, - maps: Maps::new(room_mode), + // maps: Maps::new(room_mode), + maps: Maps::new(room_mode, &rare_monster_table), section_id, drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)), bursting: false, From 14e14639c8daa37e7f3ebf94aa2fdfae375cef01 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 19 Jul 2021 03:49:37 +0000 Subject: [PATCH 07/15] more testing/debugging. align monster appearance between client and server. --- data/battle_param/ep1_rare_monster.toml | 8 +-- src/ship/map/enemy.rs | 68 +++++++++++++------------ src/ship/map/maps.rs | 4 +- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/data/battle_param/ep1_rare_monster.toml b/data/battle_param/ep1_rare_monster.toml index 1e1f3e0..9de3518 100644 --- a/data/battle_param/ep1_rare_monster.toml +++ b/data/battle_param/ep1_rare_monster.toml @@ -3,7 +3,7 @@ # 1/256 = 0.00390625 # 1/512 = 0.001953125 -Hildebear = 1.0 -RagRappy = 0.1 -PoisonLily = 0.1 -PofuillySlime = 0.1 \ No newline at end of file +Hildebear = 0.1 +RagRappy = 0.2 +PoisonLily = 0.3 +PofuillySlime = 0.4 \ No newline at end of file diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs index 6d4d7c7..6ed160b 100644 --- a/src/ship/map/enemy.rs +++ b/src/ship/map/enemy.rs @@ -107,9 +107,13 @@ impl RareMonsterAppearTable { } pub fn roll_appearance(&self, monster: &MonsterType) -> bool { - let mut rng = rand_chacha::ChaChaRng::seed_from_u64(self.seed as u64); // TODO: figure out how to seed with a u32 - println!("rolling for {:?} appearance", monster); - if rng.gen::() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) { + println!("rolling appearance for {:?} with seed {:?}", monster, self.seed); + let mut rng = rand_chacha::ChaChaRng::from_entropy(); + let roll: f32 = rng.gen(); + let monster_rate = self.appear_rate.get(monster).unwrap_or(&0.0f32); + println!("rolled {:?} and {:?} has appear rate {:?}", roll, monster, monster_rate); + if roll < *monster_rate { + // if rng.gen::() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) { println!("its a rare!"); true } @@ -349,42 +353,42 @@ 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?) // pub fn set_rare_appearance(self) -> MapEnemy { - // match (self.monster, self.map_area.to_episode()) { - // (MonsterType::RagRappy, Episode::One) => {MapEnemy {monster: MonsterType::AlRappy, ..self}}, - // (MonsterType::RagRappy, Episode::Two) => {MapEnemy {monster: MonsterType::EventRappy, ..self}}, - // (MonsterType::Hildebear, _) => {MapEnemy {monster: MonsterType::Hildeblue, ..self}}, - // (MonsterType::PoisonLily, _) => {MapEnemy {monster: MonsterType::NarLily, ..self}}, - // (MonsterType::PofuillySlime, _) => {MapEnemy {monster: MonsterType::PouillySlime, ..self}}, - // (MonsterType::SandRappyCrater, _) => {MapEnemy {monster: MonsterType::DelRappyCrater, ..self}}, - // (MonsterType::ZuCrater, _) => {MapEnemy {monster: MonsterType::PazuzuCrater, ..self}}, - // (MonsterType::Dorphon, _) => {MapEnemy {monster: MonsterType::DorphonEclair, ..self}}, - // (MonsterType::SandRappyDesert, _) => {MapEnemy {monster: MonsterType::DelRappyDesert, ..self}}, - // (MonsterType::ZuDesert, _) => {MapEnemy {monster: MonsterType::PazuzuDesert, ..self}}, - // (MonsterType::MerissaA, _) => {MapEnemy {monster: MonsterType::MerissaAA, ..self}}, - // (MonsterType::SaintMillion, _) => {MapEnemy {monster: MonsterType::Kondrieu, ..self}}, - // (MonsterType::Shambertin, _) => {MapEnemy {monster: MonsterType::Kondrieu, ..self}}, - // _ => {self}, + // 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?) + pub fn set_rare_appearance(self) -> MapEnemy { + match (self.monster, self.map_area.to_episode()) { + (MonsterType::RagRappy, Episode::One) => {MapEnemy {monster: MonsterType::AlRappy, shiny:true, ..self}}, + (MonsterType::RagRappy, Episode::Two) => {MapEnemy {monster: MonsterType::EventRappy, shiny:true, ..self}}, + (MonsterType::Hildebear, _) => {MapEnemy {monster: MonsterType::Hildeblue, shiny:true, ..self}}, + (MonsterType::PoisonLily, _) => {MapEnemy {monster: MonsterType::NarLily, shiny:true, ..self}}, + (MonsterType::PofuillySlime, _) => {MapEnemy {monster: MonsterType::PouillySlime, shiny:true, ..self}}, + (MonsterType::SandRappyCrater, _) => {MapEnemy {monster: MonsterType::DelRappyCrater, shiny:true, ..self}}, + (MonsterType::ZuCrater, _) => {MapEnemy {monster: MonsterType::PazuzuCrater, shiny:true, ..self}}, + (MonsterType::Dorphon, _) => {MapEnemy {monster: MonsterType::DorphonEclair, shiny:true, ..self}}, + (MonsterType::SandRappyDesert, _) => {MapEnemy {monster: MonsterType::DelRappyDesert, shiny:true, ..self}}, + (MonsterType::ZuDesert, _) => {MapEnemy {monster: MonsterType::PazuzuDesert, shiny:true, ..self}}, + (MonsterType::MerissaA, _) => {MapEnemy {monster: MonsterType::MerissaAA, shiny:true, ..self}}, + (MonsterType::SaintMillion, _) => {MapEnemy {monster: MonsterType::Kondrieu, shiny:true, ..self}}, + (MonsterType::Shambertin, _) => {MapEnemy {monster: MonsterType::Kondrieu, shiny:true, ..self}}, + _ => {self}, + } + } + // in theory this should only be called on monsters we know can have rare types - pub fn roll_appearance_for_quest(self, rare_monster_table: &RareMonsterAppearTable) -> MapEnemy { + pub fn roll_appearance_for_mission(self, rare_monster_table: &RareMonsterAppearTable) -> MapEnemy { if rare_monster_table.roll_appearance(&self.monster) { return self.set_rare_appearance() } diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs index 71ef38d..0bd34b7 100644 --- a/src/ship/map/maps.rs +++ b/src/ship/map/maps.rs @@ -439,8 +439,8 @@ impl Maps { pub fn roll_monster_appearance(&mut self, rare_monster_table: &RareMonsterAppearTable) { self.enemy_data = self.enemy_data .iter() - .map(|&x| if x.is_some() { - Some(x.unwrap().roll_appearance_for_quest(&rare_monster_table)) + .map(|&x| if x.is_some() && x.unwrap().has_rare_appearance() { + Some(x.unwrap().roll_appearance_for_mission(&rare_monster_table)) } else { x }) From 5c49ded823165dd8f9bd23a1f268c935c8ee3407 Mon Sep 17 00:00:00 2001 From: andy Date: Tue, 20 Jul 2021 00:37:54 +0000 Subject: [PATCH 08/15] add best quest. add rare monsters to quests. dont use the same seed for random numbers if you want different results --- data/quests.toml | 6 +++++- src/ship/map/enemy.rs | 8 ++------ src/ship/map/maps.rs | 5 +++-- src/ship/packet/handler/quest.rs | 3 ++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/data/quests.toml b/data/quests.toml index a316475..d1d3298 100644 --- a/data/quests.toml +++ b/data/quests.toml @@ -32,4 +32,8 @@ dat = "q233-ext-bb.dat" [[Retrieval.quests]] bin = "q236-ext-bb.bin" dat = "q236-ext-bb.dat" -#drop_table = "q102-drops" \ No newline at end of file +#drop_table = "q102-drops" + +[[Retrieval.quests]] +bin = "q118-vr-bb.bin" +dat = "q118-vr-bb.dat" diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs index 6ed160b..e66cca4 100644 --- a/src/ship/map/enemy.rs +++ b/src/ship/map/enemy.rs @@ -108,12 +108,8 @@ impl RareMonsterAppearTable { 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(); - let roll: f32 = rng.gen(); - let monster_rate = self.appear_rate.get(monster).unwrap_or(&0.0f32); - println!("rolled {:?} and {:?} has appear rate {:?}", roll, monster, monster_rate); - if roll < *monster_rate { - // if rng.gen::() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) { + // let mut rng = rand_chacha::ChaChaRng::from_entropy(); + if rand_chacha::ChaChaRng::from_entropy().gen::() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) { println!("its a rare!"); true } diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs index 0bd34b7..ceebd65 100644 --- a/src/ship/map/maps.rs +++ b/src/ship/map/maps.rs @@ -398,9 +398,10 @@ impl Maps { }) } - pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>) { - // pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>, rare_monster_table: RareMonsterAppearTable) { + // pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>) { + pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>, rare_monster_appear_table: &RareMonsterAppearTable) { self.enemy_data = enemies; + self.roll_monster_appearance(rare_monster_appear_table); // self.enemy_data = enemies // .iter() diff --git a/src/ship/packet/handler/quest.rs b/src/ship/packet/handler/quest.rs index 90359c3..c993579 100644 --- a/src/ship/packet/handler/quest.rs +++ b/src/ship/packet/handler/quest.rs @@ -83,7 +83,8 @@ pub fn player_chose_quest(id: ClientId, questmenuselect: &QuestMenuSelect, quest 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))?; - room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone()); + // 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.map_areas = quest.map_areas.clone(); let bin = quest::quest_header(questmenuselect, &quest.bin_blob, "bin"); From 309b97cbc27f61fbd7a1d7dbf598efee813918d4 Mon Sep 17 00:00:00 2001 From: andy Date: Fri, 23 Jul 2021 01:45:02 +0000 Subject: [PATCH 09/15] clean up and set rare monster appear rate to 1/512 --- data/battle_param/ep1_rare_monster.toml | 8 +- data/battle_param/ep2_rare_monster.toml | 6 +- data/battle_param/ep4_rare_monster.toml | 12 +-- src/ship/drops/mod.rs | 2 +- src/ship/map/enemy.rs | 40 ++------- src/ship/map/maps.rs | 104 ------------------------ src/ship/packet/handler/quest.rs | 1 - src/ship/room.rs | 9 +- 8 files changed, 24 insertions(+), 158 deletions(-) diff --git a/data/battle_param/ep1_rare_monster.toml b/data/battle_param/ep1_rare_monster.toml index 9de3518..14a1d4a 100644 --- a/data/battle_param/ep1_rare_monster.toml +++ b/data/battle_param/ep1_rare_monster.toml @@ -3,7 +3,7 @@ # 1/256 = 0.00390625 # 1/512 = 0.001953125 -Hildebear = 0.1 -RagRappy = 0.2 -PoisonLily = 0.3 -PofuillySlime = 0.4 \ No newline at end of file +Hildebear = 0.001953125 +RagRappy = 0.001953125 +PoisonLily = 0.001953125 +PofuillySlime = 0.001953125 diff --git a/data/battle_param/ep2_rare_monster.toml b/data/battle_param/ep2_rare_monster.toml index 290f727..b56bee9 100644 --- a/data/battle_param/ep2_rare_monster.toml +++ b/data/battle_param/ep2_rare_monster.toml @@ -3,7 +3,7 @@ # 1/256 = 0.00390625 # 1/512 = 0.001953125 -Hildebear = 0.01 -RagRappy = 0.01 -PoisonLily = 0.01 +Hildebear = 0.001953125 +RagRappy = 0.001953125 +PoisonLily = 0.001953125 diff --git a/data/battle_param/ep4_rare_monster.toml b/data/battle_param/ep4_rare_monster.toml index 0ddddbf..d5b0fb2 100644 --- a/data/battle_param/ep4_rare_monster.toml +++ b/data/battle_param/ep4_rare_monster.toml @@ -3,11 +3,11 @@ # 1/256 = 0.00390625 # 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 SaintMillion = 0.1 \ No newline at end of file diff --git a/src/ship/drops/mod.rs b/src/ship/drops/mod.rs index 427da74..e5037af 100644 --- a/src/ship/drops/mod.rs +++ b/src/ship/drops/mod.rs @@ -57,9 +57,9 @@ pub fn load_data_file(episode: Episode, difficul // this is just copypaste pub fn load_rare_monster_file(episode: Episode) -> T { + // TODO: where does the rare monster toml file actually live let mut path = PathBuf::from("data/battle_param/"); 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 s = String::new(); diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs index e66cca4..2555024 100644 --- a/src/ship/map/enemy.rs +++ b/src/ship/map/enemy.rs @@ -75,21 +75,15 @@ pub enum MapEnemyError { MapAreaError(#[from] MapAreaError), } -// TODO: is this even needed/used? -// #[derive(Clone, Debug, Serialize, Deserialize)] -// pub type RareMonsterAppearRate: HashMap, - // making this `pub type` doesn't allow `impl`s to be defined? #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RareMonsterAppearTable { appear_rate: HashMap, - seed: u32 } impl RareMonsterAppearTable { - pub fn new(episode: Episode, room_seed: u32) -> RareMonsterAppearTable { + pub fn new(episode: Episode) -> RareMonsterAppearTable { let cfg: HashMap = load_rare_monster_file(episode); - println!("got cfg: {:?}", cfg); let appear_rates: HashMap = cfg .into_iter() @@ -102,21 +96,14 @@ impl RareMonsterAppearTable { RareMonsterAppearTable { appear_rate: appear_rates, - seed: room_seed, } } 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::() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) { - println!("its a rare!"); - true - } - else { - println!("lol sucker"); - false + return true } + false } } @@ -135,7 +122,6 @@ pub struct MapEnemy { impl MapEnemy { pub fn from_raw(enemy: RawMapEnemy, episode: &Episode, map_area: &MapArea /*, battleparam */) -> Result { - println!("enemy.rs::from_raw - {:?}", enemy); // 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) 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 { match self.monster { MonsterType::RagRappy | MonsterType::Hildebear | @@ -348,22 +333,11 @@ impl MapEnemy { _ => false } } - - // // 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 { match (self.monster, self.map_area.to_episode()) { (MonsterType::RagRappy, Episode::One) => {MapEnemy {monster: MonsterType::AlRappy, shiny:true, ..self}}, diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs index ceebd65..aca75b7 100644 --- a/src/ship/map/maps.rs +++ b/src/ship/map/maps.rs @@ -12,8 +12,6 @@ use crate::ship::room::{Episode, RoomMode}; // TODO: don't use * use crate::ship::map::*; -// use rand::{Rng}; - pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec> { 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> { -// fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy, rare_monster_table: RareMonsterAppearTable) -> Vec> { let enemy = MapEnemy::from_raw(raw_enemy, episode, map_area); - // TODO: load rare monster rates config enemy .map_or(vec![None], |monster| { let mut monsters = Vec::new(); monsters.push(Some(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 => { for _ in 0..30 { 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> { -// pub fn enemy_data_from_stream(cursor: &mut impl Read, map_area: &MapArea, episode: &Episode, rare_monster_table: enemy::RareMonsterAppearTable) -> Vec> { let mut enemy_data = Vec::new(); 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, rare_monster_table)); } enemy_data } fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode) -> Vec> { -// fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode, rare_monster_table: enemy::RareMonsterAppearTable) -> Vec> { let path = map_variant.dat_file(); 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, rare_monster_table) } @@ -268,7 +177,6 @@ pub struct Maps { } impl Maps { - // pub fn new(room_mode: RoomMode) -> Maps { pub fn new(room_mode: RoomMode, rare_monster_table: &enemy::RareMonsterAppearTable) -> Maps { let map_variants = match (room_mode.episode(), room_mode.single_player()) { (Episode::One, 0) => { @@ -365,7 +273,6 @@ impl Maps { enemy_data: map_variants.iter() .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(), rare_monster_table)); enemy_data }), object_data: map_variants.iter() @@ -398,20 +305,9 @@ impl Maps { }) } - // pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>) { pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>, rare_monster_appear_table: &RareMonsterAppearTable) { self.enemy_data = enemies; 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; } diff --git a/src/ship/packet/handler/quest.rs b/src/ship/packet/handler/quest.rs index c993579..3c4e7b4 100644 --- a/src/ship/packet/handler/quest.rs +++ b/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) .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?.as_mut() .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.map_areas = quest.map_areas.clone(); diff --git a/src/ship/room.rs b/src/ship/room.rs index 3f0918a..cfee400 100644 --- a/src/ship/room.rs +++ b/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 { monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(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()), name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(), 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, drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)), bursting: false, From b8534eb520ff0fca4014d66716572d0d3ce88c60 Mon Sep 17 00:00:00 2001 From: andy Date: Fri, 23 Jul 2021 01:48:20 +0000 Subject: [PATCH 10/15] push slime clones --- src/ship/map/maps.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs index aca75b7..8c11d21 100644 --- a/src/ship/map/maps.rs +++ b/src/ship/map/maps.rs @@ -46,6 +46,16 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> monsters.push(Some(MapEnemy::new(MonsterType::Hidoom, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::Migium, monster.map_area))); }, + MonsterType::PofuillySlime => { + for _ in 0..5 { + monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime, monster.map_area))); + } + }, + MonsterType::PouillySlime => { + for _ in 0..5 { + monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime, monster.map_area))); + } + }, MonsterType::SinowBeat => { for _ in 0..4 { monsters.push(Some(MapEnemy::new(MonsterType::SinowBeat, monster.map_area))); From e578d9bad5b2cbaa253b24ffdca3b4cd3d5e02a4 Mon Sep 17 00:00:00 2001 From: andy Date: Fri, 23 Jul 2021 02:57:53 +0000 Subject: [PATCH 11/15] imagine adding useless code --- src/ship/map/area.rs | 48 -------------------------------------------- 1 file changed, 48 deletions(-) diff --git a/src/ship/map/area.rs b/src/ship/map/area.rs index 9da3374..a26aa19 100644 --- a/src/ship/map/area.rs +++ b/src/ship/map/area.rs @@ -208,54 +208,6 @@ impl MapArea { // MapArea::TestMapEp4 => 10, } } - - pub fn to_episode(self) -> Episode { - match self { - Pioneer2Ep1 => Episode::One, - Forest1 => Episode::One, - Forest2 => Episode::One, - Caves1 => Episode::One, - Caves2 => Episode::One, - Caves3 => Episode::One, - Mines1 => Episode::One, - Mines2 => Episode::One, - Ruins1 => Episode::One, - Ruins2 => Episode::One, - Ruins3 => Episode::One, - Dragon => Episode::One, - DeRolLe => Episode::One, - VolOpt => Episode::One, - DarkFalz => Episode::One, - Pioneer2Ep2 => Episode::Two, - VrTempleAlpha => Episode::Two, - VrTempleBeta => Episode::Two, - VrSpaceshipAlpha => Episode::Two, - VrSpaceshipBeta => Episode::Two, - Cca => Episode::Two, - JungleAreaNorth => Episode::Two, - JungleAreaEast => Episode::Two, - Mountain => Episode::Two, - Seaside => Episode::Two, - SeabedUpper => Episode::Two, - SeabedLower => Episode::Two, - GalGryphon => Episode::Two, - OlgaFlow => Episode::Two, - BarbaRay => Episode::Two, - GolDragon => Episode::Two, - SeasideNight => Episode::Two, - Tower => Episode::Two, - Pioneer2Ep4 => Episode::Four, - CraterEast => Episode::Four, - CraterWest => Episode::Four, - CraterSouth => Episode::Four, - CraterNorth => Episode::Four, - CraterInterior => Episode::Four, - SubDesert1 => Episode::Four, - SubDesert2 => Episode::Four, - SubDesert3 => Episode::Four, - SaintMillion => Episode::Four, - } - } } From 7a1a20db532f6946427cf4adbab5ee31a9787ead Mon Sep 17 00:00:00 2001 From: andy Date: Sat, 24 Jul 2021 00:42:14 +0000 Subject: [PATCH 12/15] imagine reading the warning message wrongly. not me --- src/ship/map/area.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/ship/map/area.rs b/src/ship/map/area.rs index a26aa19..2b686ce 100644 --- a/src/ship/map/area.rs +++ b/src/ship/map/area.rs @@ -208,6 +208,54 @@ impl MapArea { // MapArea::TestMapEp4 => 10, } } + + pub fn to_episode(self) -> Episode { + match self { + MapArea::Pioneer2Ep1 => Episode::One, + MapArea::Forest1 => Episode::One, + MapArea::Forest2 => Episode::One, + MapArea::Caves1 => Episode::One, + MapArea::Caves2 => Episode::One, + MapArea::Caves3 => Episode::One, + MapArea::Mines1 => Episode::One, + MapArea::Mines2 => Episode::One, + MapArea::Ruins1 => Episode::One, + MapArea::Ruins2 => Episode::One, + MapArea::Ruins3 => Episode::One, + MapArea::Dragon => Episode::One, + MapArea::DeRolLe => Episode::One, + MapArea::VolOpt => Episode::One, + MapArea::DarkFalz => Episode::One, + MapArea::Pioneer2Ep2 => Episode::Two, + MapArea::VrTempleAlpha => Episode::Two, + MapArea::VrTempleBeta => Episode::Two, + MapArea::VrSpaceshipAlpha => Episode::Two, + MapArea::VrSpaceshipBeta => Episode::Two, + MapArea::Cca => Episode::Two, + MapArea::JungleAreaNorth => Episode::Two, + MapArea::JungleAreaEast => Episode::Two, + MapArea::Mountain => Episode::Two, + MapArea::Seaside => Episode::Two, + MapArea::SeabedUpper => Episode::Two, + MapArea::SeabedLower => Episode::Two, + MapArea::GalGryphon => Episode::Two, + MapArea::OlgaFlow => Episode::Two, + MapArea::BarbaRay => Episode::Two, + MapArea::GolDragon => Episode::Two, + MapArea::SeasideNight => Episode::Two, + MapArea::Tower => Episode::Two, + MapArea::Pioneer2Ep4 => Episode::Four, + MapArea::CraterEast => Episode::Four, + MapArea::CraterWest => Episode::Four, + MapArea::CraterSouth => Episode::Four, + MapArea::CraterNorth => Episode::Four, + MapArea::CraterInterior => Episode::Four, + MapArea::SubDesert1 => Episode::Four, + MapArea::SubDesert2 => Episode::Four, + MapArea::SubDesert3 => Episode::Four, + MapArea::SaintMillion => Episode::Four, + } + } } From 3faca14883455915abc4a718d5f2645b7ac487b8 Mon Sep 17 00:00:00 2001 From: andy Date: Sat, 24 Jul 2021 01:32:58 +0000 Subject: [PATCH 13/15] andy vs. clippy round 4 and some cleanup --- src/ship/map/enemy.rs | 7 +++---- src/ship/map/maps.rs | 24 +++++++++++++++--------- src/ship/packet/builder/room.rs | 6 +++--- src/ship/packet/handler/room.rs | 10 ++++++++-- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs index 2555024..6aed225 100644 --- a/src/ship/map/enemy.rs +++ b/src/ship/map/enemy.rs @@ -324,14 +324,13 @@ impl MapEnemy { } pub fn has_rare_appearance(self) -> bool { - match self.monster { + matches!(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 => true, - _ => false - } + MonsterType::SaintMillion | MonsterType::Shambertin + ) } /* diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs index 8c11d21..1f85124 100644 --- a/src/ship/map/maps.rs +++ b/src/ship/map/maps.rs @@ -33,8 +33,7 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> enemy .map_or(vec![None], |monster| { - let mut monsters = Vec::new(); - monsters.push(Some(monster)); + let mut monsters = vec![Some(monster)]; match monster.monster { MonsterType::Monest => { @@ -333,9 +332,9 @@ impl Maps { } }) .collect(); - for i in 0..shiny.len() { + for monster in &shiny { if let Some(j) = rare_monsters.iter().position(|&x| x == 0xFFFF) { - rare_monsters[j] = shiny[i].0 as u16; + rare_monsters[j] = monster.0 as u16; } else { break } @@ -346,11 +345,18 @@ impl Maps { pub fn roll_monster_appearance(&mut self, rare_monster_table: &RareMonsterAppearTable) { self.enemy_data = self.enemy_data .iter() - .map(|&x| if x.is_some() && x.unwrap().has_rare_appearance() { - Some(x.unwrap().roll_appearance_for_mission(&rare_monster_table)) - } else { - x - }) + // .map(|&x| if x.is_some() && x.unwrap().has_rare_appearance() { + .map(|&x| + if let Some(monster) = x { + if monster.has_rare_appearance() { + Some(monster.roll_appearance_for_mission(rare_monster_table)) + } else { + Some(monster) + } + } else { + x + } + ) .collect(); } } diff --git a/src/ship/packet/builder/room.rs b/src/ship/packet/builder/room.rs index 3fe196a..c5bd11c 100644 --- a/src/ship/packet/builder/room.rs +++ b/src/ship/packet/builder/room.rs @@ -72,8 +72,8 @@ pub fn add_to_room(_id: ClientId, }) } -pub fn build_rare_monster_list(rare_monster_vec: Vec) -> Result { - Ok(RareMonsterList { +pub fn build_rare_monster_list(rare_monster_vec: Vec) -> RareMonsterList { + RareMonsterList { ids: rare_monster_vec.try_into().unwrap_or([0xFFFFu16; 16]), - }) + } } \ No newline at end of file diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index 8c2ba8c..854d24f 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -130,9 +130,15 @@ pub fn done_bursting(id: ClientId, .flatten() ); + // // TODO: check how often `done_bursting` is called. ie: make sure it's only used when joining a room and not each time a player warps in a pipe + // if rare_monster_list.is_some() { + // let rare_monster_packet = SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_monster_list.unwrap()).unwrap()); // TODO: don't double unwrap + // result = Box::new(result.chain(vec![(id, rare_monster_packet)])); // TODO: make sure we arent clobbering `result` here + // } + // TODO: check how often `done_bursting` is called. ie: make sure it's only used when joining a room and not each time a player warps in a pipe - if rare_monster_list.is_some() { - let rare_monster_packet = SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_monster_list.unwrap()).unwrap()); // TODO: don't double unwrap + if let Some(rare_list) = rare_monster_list { + let rare_monster_packet = SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_list)); result = Box::new(result.chain(vec![(id, rare_monster_packet)])); // TODO: make sure we arent clobbering `result` here } From 00cff460774ca1c32bce5ef6b3e8438e36ce0d34 Mon Sep 17 00:00:00 2001 From: andy Date: Sat, 24 Jul 2021 01:34:26 +0000 Subject: [PATCH 14/15] oops forgot to delete this --- src/ship/packet/handler/room.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index 854d24f..f912d18 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -130,12 +130,6 @@ pub fn done_bursting(id: ClientId, .flatten() ); - // // TODO: check how often `done_bursting` is called. ie: make sure it's only used when joining a room and not each time a player warps in a pipe - // if rare_monster_list.is_some() { - // let rare_monster_packet = SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_monster_list.unwrap()).unwrap()); // TODO: don't double unwrap - // result = Box::new(result.chain(vec![(id, rare_monster_packet)])); // TODO: make sure we arent clobbering `result` here - // } - // TODO: check how often `done_bursting` is called. ie: make sure it's only used when joining a room and not each time a player warps in a pipe if let Some(rare_list) = rare_monster_list { let rare_monster_packet = SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_list)); From 899527d3e7e5e520ac8cf4f021d1aa6bb6888faf Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 26 Jul 2021 01:21:29 +0000 Subject: [PATCH 15/15] basic test --- src/ship/map/enemy.rs | 2 +- tests/test_rooms.rs | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs index 6aed225..52092d4 100644 --- a/src/ship/map/enemy.rs +++ b/src/ship/map/enemy.rs @@ -78,7 +78,7 @@ pub enum MapEnemyError { // making this `pub type` doesn't allow `impl`s to be defined? #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RareMonsterAppearTable { - appear_rate: HashMap, + pub appear_rate: HashMap, } impl RareMonsterAppearTable { diff --git a/tests/test_rooms.rs b/tests/test_rooms.rs index 45f8ace..61ec3c1 100644 --- a/tests/test_rooms.rs +++ b/tests/test_rooms.rs @@ -101,3 +101,23 @@ async fn test_item_ids_reset_when_rejoining_rooms() { _ => panic!(), } } + +#[async_std::test] +async fn test_load_rare_monster_default_appear_rates() { + let mut entity_gateway = InMemoryGateway::default(); + let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await; + let mut ship = Box::new(ShipServerState::builder() + .gateway(entity_gateway.clone()) + .build()); + log_in_char(&mut ship, ClientId(1), "a1", "a").await; + join_lobby(&mut ship, ClientId(1)).await; + create_room(&mut ship, ClientId(1), "room", "").await; + + // assume episode 1 + let room = ship.blocks.0[0].rooms[0].as_ref().unwrap(); + println!("rare monster table: {:?}", room.rare_monster_table); + let rates = &*room.rare_monster_table; + for (_monster, rate) in rates.clone().appear_rate { + assert_eq!(rate, 0.001953125f32); // 1/512 = 0.001953125 + } +} \ No newline at end of file