diff --git a/raws/spawns.json b/raws/spawns.json index e44b217..d8900d1 100644 --- a/raws/spawns.json +++ b/raws/spawns.json @@ -264,7 +264,8 @@ "defense": 1, "power": 4 }, - "vision_range": 8 + "vision_range": 8, + "ai": "melee" }, { "name": "Goblin", @@ -281,7 +282,8 @@ "defense": 1, "power": 3 }, - "vision_range": 8 + "vision_range": 8, + "ai": "melee" }, { "name": "Kobold", @@ -298,7 +300,62 @@ "defense": 0, "power": 2 }, - "vision_range": 4 + "vision_range": 4, + "ai": "melee" + }, + { + "name": "Barkeep", + "renderable": { + "glyph": "☺", + "fg": "#EE82EE", + "bg": "#000000", + "order": 1 + }, + "blocks_tile": true, + "stats": { + "max_hp": 16, + "hp": 16, + "defense": 1, + "power": 4 + }, + "vision_range": 4, + "ai": "bystander" + }, + { + "name": "Shady Salesman", + "renderable": { + "glyph": "h", + "fg": "#EE82EE", + "bg": "#000000", + "order": 1 + }, + "blocks_tile": true, + "stats": { + "max_hp": 16, + "hp": 16, + "defense": 1, + "power": 4 + }, + "vision_range": 4, + "ai": "bystander" + }, + { + "name": "Patron", + "renderable": { + "glyph": "☺", + "fg": "#AAAAAA", + "bg": "#000000", + "order": 1 + }, + "blocks_tile": true, + "stats": { + "max_hp": 16, + "hp": 16, + "defense": 1, + "power": 4 + }, + "vision_range": 4, + "ai": "bystander" } ], "props": [ @@ -330,6 +387,36 @@ "blocks_tile": true, "blocks_visibility": true, "door_open": true + }, + { + "name": "Keg", + "renderable": { + "glyph": "φ", + "fg": "#AAAAAA", + "bg": "#000000", + "order": 2 + }, + "hidden": false + }, + { + "name": "Table", + "renderable": { + "glyph": "╦", + "fg": "#AAAAAA", + "bg": "#000000", + "order": 2 + }, + "hidden": false + }, + { + "name": "Chair", + "renderable": { + "glyph": "└", + "fg": "#AAAAAA", + "bg": "#000000", + "order": 2 + }, + "hidden": false } ] } \ No newline at end of file diff --git a/src/components.rs b/src/components.rs index 58afb4f..effaa4a 100644 --- a/src/components.rs +++ b/src/components.rs @@ -237,6 +237,9 @@ pub struct Door { pub open: bool, } +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Bystander {} + // Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an // Entity. diff --git a/src/main.rs b/src/main.rs index 99b2661..40e0003 100644 --- a/src/main.rs +++ b/src/main.rs @@ -532,6 +532,7 @@ fn main() -> ::rltk::BError { SingleActivation, BlocksVisibility, Door, + Bystander, ); gs.ecs.insert(SimpleMarkerAllocator::::new()); diff --git a/src/map_builders/town.rs b/src/map_builders/town.rs index 9a7d102..1c930f1 100644 --- a/src/map_builders/town.rs +++ b/src/map_builders/town.rs @@ -17,6 +17,18 @@ pub fn town_builder( chain } +enum BuildingTag { + Pub, + Temple, + Blacksmith, + Clothier, + Alchemist, + PlayerHouse, + Hovel, + Abandoned, + Unassigned, +} + pub struct TownBuilder {} impl InitialMapBuilder for TownBuilder { @@ -50,19 +62,8 @@ impl TownBuilder { let exit_idx = build_data.map.xy_idx(build_data.width - 5, wall_gap_y); build_data.map.tiles[exit_idx] = TileType::DownStairs; - // Sort buildings by size - let mut building_size: Vec<(usize, i32)> = Vec::new(); - for (i, building) in buildings.iter().enumerate() { - building_size.push((i, building.2 * building.3)); - } - building_size.sort_by(|a, b| b.1.cmp(&a.1)); - - // Start in the pub - let the_pub = &buildings[building_size[0].0]; - build_data.starting_position = Some(Position { - x: the_pub.0 + (the_pub.2 / 2), - y: the_pub.1 + (the_pub.3 / 2), - }) + let building_size = self.sort_buildings(&buildings); + self.building_factory(rng, build_data, &buildings, &building_size); } fn grass_layer(&mut self, build_data: &mut BuilderMap) { @@ -299,4 +300,88 @@ impl TownBuilder { build_data.take_snapshot(); } } + + fn sort_buildings( + &mut self, + buildings: &[(i32, i32, i32, i32)], + ) -> Vec<(usize, i32, BuildingTag)> { + // Sort buildings by size + let mut building_size: Vec<(usize, i32, BuildingTag)> = Vec::new(); + for (i, building) in buildings.iter().enumerate() { + building_size.push((i, building.2 * building.3, BuildingTag::Unassigned)); + } + building_size.sort_by(|a, b| b.1.cmp(&a.1)); + building_size[0].2 = BuildingTag::Pub; + building_size[1].2 = BuildingTag::Temple; + building_size[2].2 = BuildingTag::Blacksmith; + building_size[3].2 = BuildingTag::Clothier; + building_size[4].2 = BuildingTag::Alchemist; + building_size[5].2 = BuildingTag::PlayerHouse; + for b in building_size.iter_mut().skip(6) { + b.2 = BuildingTag::Hovel; + } + + let last_index = building_size.len() - 1; + building_size[last_index].2 = BuildingTag::Abandoned; + + building_size + } + + fn building_factory( + &mut self, + rng: &mut RandomNumberGenerator, + build_data: &mut BuilderMap, + buildings: &[(i32, i32, i32, i32)], + building_index: &[(usize, i32, BuildingTag)], + ) { + for (i, building) in buildings.iter().enumerate() { + let build_type = &building_index[i].2; + match build_type { + BuildingTag::Pub => self.build_pub(building, build_data, rng), + _ => {} + } + } + } + + fn build_pub( + &mut self, + building: &(i32, i32, i32, i32), + build_data: &mut BuilderMap, + rng: &mut RandomNumberGenerator, + ) { + // Place the player + build_data.starting_position = Some(Position { + x: building.0 + (building.2 / 2), + y: building.1 + (building.3 / 2), + }); + let player_idx = build_data + .map + .xy_idx(building.0 + (building.2 / 2), building.1 + (building.3 / 2)); + + // Place other items + let mut to_place: Vec<&str> = vec![ + "Barkeep", + "Shady Salesman", + "Patron", + "Patron", + "Table", + "Chair", + "Table", + "Chair", + ]; + for y in building.1..building.1 + building.3 { + for x in building.0..building.0 + building.2 { + let idx = build_data.map.xy_idx(x, y); + if build_data.map.tiles[idx] == TileType::WoodFloor + && idx != player_idx + && rng.roll_dice(1, 3) == 1 + && !to_place.is_empty() + { + let entity_tag = to_place[0]; + to_place.remove(0); + build_data.spawn_list.push((idx, entity_tag.to_string())); + } + } + } + } } diff --git a/src/raws/mob_structs.rs b/src/raws/mob_structs.rs index 61a570e..0dfd2ea 100644 --- a/src/raws/mob_structs.rs +++ b/src/raws/mob_structs.rs @@ -9,6 +9,7 @@ pub struct Mob { pub blocks_tile: bool, pub stats: MobStats, pub vision_range: i32, + pub ai: String, } #[derive(Deserialize, Debug)] diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 53f424e..4620970 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -212,7 +212,12 @@ pub fn spawn_named_mob( eb = eb.with(Name::from(&mob_template.name)); - eb = eb.with(Monster {}); + match mob_template.ai.as_ref() { + "melee" => eb = eb.with(Monster {}), + "bystander" => eb = eb.with(Bystander {}), + _ => {} + }; + if mob_template.blocks_tile { eb = eb.with(BlocksTile {}); } diff --git a/src/saveload_system.rs b/src/saveload_system.rs index d57db50..b46aea5 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -89,6 +89,7 @@ pub fn save_game(ecs: &mut World) { SingleActivation, BlocksVisibility, Door, + Bystander, ); } @@ -180,6 +181,7 @@ pub fn load_game(ecs: &mut World) { SingleActivation, BlocksVisibility, Door, + Bystander, ); }