From 2f6a618d5af563d73e55bafa16162c843345d28a Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 5 Jul 2020 00:55:13 +0100 Subject: [PATCH] Added bombs, throwable items, more block kinds, weaker explosions --- CHANGELOG.md | 2 + assets/common/items/bomb.ron | 7 +++ assets/voxygen/item_image_manifest.ron | 35 ++++++++------ common/src/comp/inventory/item/mod.rs | 23 ++++++---- common/src/comp/inventory/mod.rs | 44 ++++++++++++++++++ common/src/comp/misc.rs | 16 +++++++ common/src/comp/mod.rs | 2 + common/src/state.rs | 1 + common/src/terrain/block.rs | 11 ++++- server/src/events/entity_manipulation.rs | 14 ++++-- server/src/events/inventory_manip.rs | 58 +++++++++++++++++++++++- server/src/sys/mod.rs | 3 ++ server/src/sys/object.rs | 51 +++++++++++++++++++++ voxygen/src/hud/item_imgs.rs | 4 +- voxygen/src/hud/slots.rs | 2 + world/src/block/mod.rs | 29 ++++++------ 16 files changed, 256 insertions(+), 46 deletions(-) create mode 100644 assets/common/items/bomb.ron create mode 100644 common/src/comp/misc.rs create mode 100644 server/src/sys/object.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index c20a476054..1d78c775b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New quadruped_low body for reptile-likes - Added new animals - Better pathfinding +- Bombs ### Changed @@ -57,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Switch to a new network backend that will allow several improvements in the future - Connection screen fails after 4 minutes if it can't connect to the server instead of 80 minutes - Rebuilt quadruped_medium animation and assets +- Disabled destruction of most blocks by explosions ### Removed diff --git a/assets/common/items/bomb.ron b/assets/common/items/bomb.ron new file mode 100644 index 0000000000..bfe33b06b9 --- /dev/null +++ b/assets/common/items/bomb.ron @@ -0,0 +1,7 @@ +Item( + name: "Bomb", + description: "Boom!\n\n", + kind: Throwable( + kind: Bomb, + ), +) diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index ddd80ea625..6363740403 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -200,7 +200,7 @@ Tool(Staff(Sceptre)): VoxTrans( "voxel.weapon.staff.wood-nature", (1.0, -1.0, 0.0), (-310., 90.0, 0.0), 1.2, - ), + ), // Shields Tool(Shield(BasicShield)): VoxTrans( "voxel.weapon.shield.wood-0", @@ -219,11 +219,11 @@ Lantern(Red0): Png( "element.icons.lantern_red-0", ), - // Farming Equipment + // Farming Equipment Tool(Farming(Broom)): VoxTrans( "voxel.weapon.tool.broom-0", (0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.1, - ), + ), Tool(Farming(Hoe0)): VoxTrans( "voxel.weapon.tool.hoe_green", (0.0, 0.0, 0.0), (130.0, 35.0, 180.0), 1.0, @@ -231,7 +231,7 @@ Tool(Farming(Hoe1)): VoxTrans( "voxel.weapon.tool.hoe_blue", (0.0, 0.0, 0.0), (130.0, 35.0, 180.0), 1.0, - ), + ), Tool(Farming(Pitchfork)): VoxTrans( "voxel.weapon.tool.pitchfork-0", (0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.1, @@ -255,7 +255,7 @@ Tool(Farming(Shovel0)): VoxTrans( "voxel.weapon.tool.shovel_green", (0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.2, - ), + ), Tool(Farming(Shovel1)): VoxTrans( "voxel.weapon.tool.shovel_gold", (0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.2, @@ -264,7 +264,7 @@ // Other Utility(Collar): Png( "element.icons.collar", - ), + ), // Armor // Starter Parts Armor(Foot(Sandal0)): VoxTrans( @@ -311,7 +311,7 @@ Armor(Pants(CultistBlue)): VoxTrans( "voxel.armor.pants.cultist", (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.2, - ), + ), Armor(Hand(CultistBlue)): VoxTrans( "voxel.armor.hand.cultist_right", (0.0, -1.0, 0.0), (-90.0, 135.0, 0.0), 1.0, @@ -680,7 +680,7 @@ Armor(Shoulder(LeafyShoulder)): VoxTrans( "voxel.armor.shoulder.twigsleaves_shoulder_right", (0.0, 0.0, 0.0), (-90.0, 130.0, 0.0), 1.2, - ), + ), //TwigsFlowers Set Armor(Chest(Twigsflowers)): VoxTrans( "voxel.armor.chest.twigsflowers_chest", @@ -710,7 +710,7 @@ Armor(Pants(Hunting)): VoxTrans( "voxel.armor.pants.grayscale", (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.2, - ), + ), // Backs Armor(Back(Short0)): VoxTrans( "voxel.armor.back.short-0", @@ -722,15 +722,15 @@ ), // Rings Armor(Ring(Ring0)): Png( - "element.icons.ring-0", + "element.icons.ring-0", ), // Necks Armor(Neck(Neck0)): Png( - "element.icons.neck-0", + "element.icons.neck-0", ), // Tabards Armor(Tabard(Admin)): Png( - "element.icons.tabard_admin", + "element.icons.tabard_admin", ), // Heads Armor(Head(Leather0)): VoxTrans( @@ -746,7 +746,7 @@ VoxTrans( "element.icons.item_apple", (0.0, 0.0, 0.0), (-90.0, 90.0, 0.0), 1.0, - ), + ), Consumable(Coconut): Png( "element.icons.item_coconut", ), @@ -761,7 +761,7 @@ Consumable(Cheese): VoxTrans( "element.icons.item_cheese", (0.0, 0.0, 0.0), (-90.0, 90.0, 0.0), 0.9, - ), + ), Consumable(Potion): VoxTrans( "voxel.object.potion_red", (0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 1.0, @@ -777,6 +777,11 @@ Consumable(VeloriteFrag): VoxTrans( "voxel.sprite.velorite.velorite_1", (0.0, -1.0, 0.0), (-50.0, 40.0, 20.0), 0.8, + ), + // Throwables + Throwable(Bomb): VoxTrans( + "voxel.object.bomb", + (0.0, -1.0, 0.0), (-50.0, 40.0, 20.0), 0.8, ), // Ingredients Ingredient(Flower): VoxTrans( @@ -791,5 +796,5 @@ Tool(Debug(Boost)): VoxTrans( "voxel.weapon.tool.broom_belzeshrub_purple", (0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.1, - ), + ), }) diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 4e1ea47338..fd90138a0c 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -28,6 +28,11 @@ pub enum Consumable { PotionExp, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Throwable { + Bomb, +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Utility { Collar, @@ -86,6 +91,11 @@ pub enum ItemKind { #[serde(default = "default_amount")] amount: u32, }, + Throwable { + kind: Throwable, + #[serde(default = "default_amount")] + amount: u32, + }, Utility { kind: Utility, #[serde(default = "default_amount")] @@ -129,15 +139,10 @@ impl Item { pub fn set_amount(&mut self, give_amount: u32) -> Result<(), assets::Error> { use ItemKind::*; match self.kind { - Consumable { ref mut amount, .. } => { - *amount = give_amount; - Ok(()) - }, - Utility { ref mut amount, .. } => { - *amount = give_amount; - Ok(()) - }, - Ingredient { ref mut amount, .. } => { + Consumable { ref mut amount, .. } + | Throwable { ref mut amount, .. } + | Utility { ref mut amount, .. } + | Ingredient { ref mut amount, .. } => { *amount = give_amount; Ok(()) }, diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 4ff88d819b..0bbfa14d1e 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -102,6 +102,37 @@ impl Inventory { // It didn't work self.add_to_first_empty(item) }, + ItemKind::Throwable { + kind: item_kind, + amount: new_amount, + .. + } => { + for slot in &mut self.slots { + if slot + .as_ref() + .map(|s| s.name() == item.name()) + .unwrap_or(false) + && slot + .as_ref() + .map(|s| s.description() == item.description()) + .unwrap_or(false) + { + if let Some(Item { + kind: ItemKind::Throwable { kind, amount, .. }, + .. + }) = slot + { + if item_kind == *kind { + *amount += new_amount; + self.recount_items(); + return None; + } + } + } + } + // It didn't work + self.add_to_first_empty(item) + }, ItemKind::Ingredient { kind: item_kind, amount: new_amount, @@ -277,6 +308,19 @@ impl Inventory { Some(return_item) } }, + ItemKind::Throwable { kind, amount } => { + if *amount <= 1 { + self.remove(cell) + } else { + *amount -= 1; + return_item.kind = ItemKind::Throwable { + kind: *kind, + amount: 1, + }; + self.recount_items(); + Some(return_item) + } + }, ItemKind::Ingredient { kind, amount } => { if *amount <= 1 { self.remove(cell) diff --git a/common/src/comp/misc.rs b/common/src/comp/misc.rs new file mode 100644 index 0000000000..22951df4b8 --- /dev/null +++ b/common/src/comp/misc.rs @@ -0,0 +1,16 @@ +use crate::sync::Uid; +use specs::Component; +use specs_idvs::IDVStorage; +use std::time::Duration; + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Object { + Bomb { + timeout: Duration, + owner: Option, + }, +} + +impl Component for Object { + type Storage = IDVStorage; +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 809cfcd17d..3efd7e9381 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -10,6 +10,7 @@ mod inputs; mod inventory; mod last; mod location; +mod misc; mod phys; mod player; pub mod projectile; @@ -40,6 +41,7 @@ pub use inventory::{ }; pub use last::Last; pub use location::{Waypoint, WaypointArea}; +pub use misc::Object; pub use phys::{Collider, ForceUpdate, Gravity, Mass, Ori, PhysicsState, Pos, Scale, Sticky, Vel}; pub use player::Player; pub use projectile::Projectile; diff --git a/common/src/state.rs b/common/src/state.rs index ce8834a179..99792738ef 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -122,6 +122,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); // Register components send from clients -> server ecs.register::(); diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 2b8b96a174..c80a7e3bf4 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -9,6 +9,9 @@ pub enum BlockKind { Air, Normal, Dense, + Rock, + Grass, + Leaves, Water, LargeCactus, BarrelCactus, @@ -32,7 +35,6 @@ pub enum BlockKind { Velorite, VeloriteFrag, Chest, - Leaves, Pumpkin, Welwitch, LingonBerry, @@ -325,6 +327,13 @@ impl BlockKind { } } + pub fn is_explodable(&self) -> bool { + match self { + BlockKind::Leaves | BlockKind::Grass | BlockKind::Rock => true, + _ => false, + } + } + // TODO: Integrate this into `is_solid` by returning an `Option` pub fn get_height(&self) -> f32 { // Beware: the height *must* be <= `MAX_HEIGHT` or the collision system will not diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 1bb3934325..0e77c1f000 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -292,11 +292,19 @@ pub fn handle_explosion(server: &Server, pos: Vec3, power: f32, owner: Opti ) .normalized(); - let _ = ecs - .read_resource::() + let terrain = ecs.read_resource::(); + let _ = terrain .ray(pos, pos + dir * power) .until(|_| rand::random::() < 0.05) - .for_each(|pos| block_change.set(pos, Block::empty())) + .for_each(|pos| { + if terrain + .get(pos) + .map(|block| block.is_explodable()) + .unwrap_or(false) + { + block_change.set(pos, Block::empty()); + } + }) .cast(); } } diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 236573bbe8..86cbf38ffb 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -5,12 +5,13 @@ use common::{ slot::{self, Slot}, Pos, MAX_PICKUP_RANGE_SQR, }, - sync::WorldSyncExt, + sync::{Uid, WorldSyncExt}, terrain::block::Block, vol::{ReadVol, Vox}, }; use rand::Rng; use specs::{join::Join, world::WorldExt, Builder, Entity as EcsEntity, WriteStorage}; +use std::time::Duration; use tracing::{debug, error}; use vek::Vec3; @@ -33,6 +34,7 @@ pub fn snuff_lantern(storage: &mut WriteStorage, entity: Ecs pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::InventoryManip) { let state = server.state_mut(); let mut dropped_items = Vec::new(); + let mut thrown_items = Vec::new(); match manip { comp::InventoryManip::Pickup(uid) => { @@ -157,6 +159,29 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv maybe_effect = Some(*effect); Some(comp::InventoryUpdateEvent::Consumed(*kind)) }, + ItemKind::Throwable { .. } => { + if let Some(pos) = + state.ecs().read_storage::().get(entity) + { + thrown_items.push(( + *pos, + state + .ecs() + .read_storage::() + .get(entity) + .copied() + .unwrap_or_default(), + state + .ecs() + .read_storage::() + .get(entity) + .copied() + .unwrap_or_default(), + item, + )); + } + Some(comp::InventoryUpdateEvent::Used) + }, ItemKind::Utility { kind: comp::item::Utility::Collar, .. @@ -318,6 +343,37 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv .with(comp::Vel(vel)) .build(); } + + // Drop items + for (pos, vel, ori, item) in thrown_items { + let vel = vel.0 + + *ori.0 * 20.0 + + Vec3::unit_z() * 15.0 + + Vec3::::zero().map(|_| rand::thread_rng().gen::() - 0.5) * 4.0; + + let uid = state.read_component_cloned::(entity); + + let mut entity = state + .create_object(Default::default(), comp::object::Body::Bomb) + .with(comp::Pos(pos.0 + Vec3::unit_z() * 0.25)) + .with(comp::Vel(vel)); + + #[allow(clippy::single-match)] + match item.kind { + item::ItemKind::Throwable { + kind: item::Throwable::Bomb, + .. + } => { + entity = entity.with(comp::Object::Bomb { + timeout: Duration::from_secs_f32(1.0), + owner: uid, + }) + }, + _ => {}, + } + + entity.build(); + } } fn within_pickup_range(player_position: Option<&Pos>, item_position: Option<&Pos>) -> bool { diff --git a/server/src/sys/mod.rs b/server/src/sys/mod.rs index 41a7227bb2..252c05591a 100644 --- a/server/src/sys/mod.rs +++ b/server/src/sys/mod.rs @@ -1,5 +1,6 @@ pub mod entity_sync; pub mod message; +pub mod object; pub mod persistence; pub mod sentinel; pub mod subscription; @@ -32,11 +33,13 @@ pub type PersistenceScheduler = SysScheduler; const TERRAIN_SYS: &str = "server_terrain_sys"; const WAYPOINT_SYS: &str = "waypoint_sys"; const PERSISTENCE_SYS: &str = "persistence_sys"; +const OBJECT_SYS: &str = "object_sys"; pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(terrain::Sys, TERRAIN_SYS, &[]); dispatch_builder.add(waypoint::Sys, WAYPOINT_SYS, &[]); dispatch_builder.add(persistence::Sys, PERSISTENCE_SYS, &[]); + dispatch_builder.add(object::Sys, OBJECT_SYS, &[]); } pub fn run_sync_systems(ecs: &mut specs::World) { diff --git a/server/src/sys/object.rs b/server/src/sys/object.rs new file mode 100644 index 0000000000..36ed8371eb --- /dev/null +++ b/server/src/sys/object.rs @@ -0,0 +1,51 @@ +use common::{ + comp::{HealthSource, Object, PhysicsState, Pos}, + event::{EventBus, ServerEvent}, + state::DeltaTime, +}; +use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; +use std::time::Duration; + +/// This system is responsible for handling projectile effect triggers +pub struct Sys; +impl<'a> System<'a> for Sys { + #[allow(clippy::type_complexity)] + type SystemData = ( + Entities<'a>, + Read<'a, DeltaTime>, + Read<'a, EventBus>, + ReadStorage<'a, Pos>, + ReadStorage<'a, PhysicsState>, + WriteStorage<'a, Object>, + ); + + fn run( + &mut self, + (entities, dt, server_bus, positions, physics_states, mut objects): Self::SystemData, + ) { + let mut server_emitter = server_bus.emitter(); + + // Objects + for (entity, pos, _physics, object) in + (&entities, &positions, &physics_states, &mut objects).join() + { + match object { + Object::Bomb { owner, timeout } => { + if let Some(t) = timeout.checked_sub(Duration::from_secs_f32(dt.0)) { + *timeout = t; + } else { + server_emitter.emit(ServerEvent::Destroy { + entity, + cause: HealthSource::Suicide, + }); + server_emitter.emit(ServerEvent::Explosion { + pos: pos.0, + power: 4.0, + owner: *owner, + }); + } + }, + } + } + } +} diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index e6383e0c81..8661e911fe 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -4,7 +4,7 @@ use common::{ comp::item::{ armor::Armor, tool::{Tool, ToolKind}, - Consumable, Ingredient, Item, ItemKind, Lantern, LanternKind, Utility, + Consumable, Ingredient, Item, ItemKind, Lantern, LanternKind, Throwable, Utility, }, figure::Segment, }; @@ -24,6 +24,7 @@ pub enum ItemKey { Armor(Armor), Utility(Utility), Consumable(Consumable), + Throwable(Throwable), Ingredient(Ingredient), Empty, } @@ -35,6 +36,7 @@ impl From<&Item> for ItemKey { ItemKind::Armor { kind, .. } => ItemKey::Armor(*kind), ItemKind::Utility { kind, .. } => ItemKey::Utility(*kind), ItemKind::Consumable { kind, .. } => ItemKey::Consumable(*kind), + ItemKind::Throwable { kind, .. } => ItemKey::Throwable(*kind), ItemKind::Ingredient { kind, .. } => ItemKey::Ingredient(*kind), } } diff --git a/voxygen/src/hud/slots.rs b/voxygen/src/hud/slots.rs index 19f17ceea4..b8279eaf3f 100644 --- a/voxygen/src/hud/slots.rs +++ b/voxygen/src/hud/slots.rs @@ -42,6 +42,7 @@ impl SlotKey for InventorySlot { ItemKind::Tool { .. } | ItemKind::Lantern(_) | ItemKind::Armor { .. } => None, ItemKind::Utility { amount, .. } | ItemKind::Consumable { amount, .. } + | ItemKind::Throwable { amount, .. } | ItemKind::Ingredient { amount, .. } => Some(amount), }) .filter(|amount| *amount > 1) @@ -139,6 +140,7 @@ impl<'a> SlotKey, HotbarImageSource<'a>> for HotbarSlot { ItemKind::Tool { .. } | ItemKind::Lantern(_) | ItemKind::Armor { .. } => None, ItemKind::Utility { amount, .. } | ItemKind::Consumable { amount, .. } + | ItemKind::Throwable { amount, .. } | ItemKind::Ingredient { amount, .. } => Some(amount), }) .filter(|amount| *amount > 1) diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index f00f615297..5f1479f48e 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -254,22 +254,19 @@ impl<'a> BlockGen<'a> { .map(|e| (e * 255.0) as u8); // Underground - if (wposf.z as f32) > alt - 32.0 * chaos { - Some(Block::new(BlockKind::Normal, col)) - } else { - Some(Block::new(BlockKind::Dense, col)) - } + Some(Block::new(BlockKind::Normal, col)) } else if (wposf.z as f32) < height { - let col = Lerp::lerp( - sub_surface_color, - surface_color, - (wposf.z as f32 - (height - grass_depth)) - .div(grass_depth) - .powf(0.5), - ); + let grass_factor = (wposf.z as f32 - (height - grass_depth)) + .div(grass_depth) + .powf(0.5); + let col = Lerp::lerp(sub_surface_color, surface_color, grass_factor); // Surface Some(Block::new( - BlockKind::Normal, + if grass_factor > 0.7 { + BlockKind::Grass + } else { + BlockKind::Normal + }, col.map(|e| (e * 255.0) as u8), )) } else if (wposf.z as f32) < height + 0.9 @@ -349,7 +346,7 @@ impl<'a> BlockGen<'a> { let field2 = RandomField::new(world.seed + 2); Some(Block::new( - BlockKind::Normal, + BlockKind::Rock, stone_col - Rgb::new( field0.get(wpos) as u8 % 16, @@ -596,7 +593,7 @@ pub fn block_from_structure( StructureBlock::Water => Some(Block::new(BlockKind::Water, Rgb::new(100, 150, 255))), StructureBlock::GreenSludge => Some(Block::new(BlockKind::Water, Rgb::new(30, 126, 23))), StructureBlock::Acacia => Some(Block::new( - BlockKind::Normal, + BlockKind::Leaves, Lerp::lerp( Rgb::new(15.0, 126.0, 50.0), Rgb::new(30.0, 180.0, 10.0), @@ -629,7 +626,7 @@ pub fn block_from_structure( .map(|e| e as u8), )), StructureBlock::Mangrove => Some(Block::new( - BlockKind::Normal, + BlockKind::Leaves, Lerp::lerp(Rgb::new(32.0, 56.0, 22.0), Rgb::new(57.0, 69.0, 27.0), lerp) .map(|e| e as u8), )),