mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'aweinstock/lava' into 'master'
Lava See merge request veloren/veloren!2482
This commit is contained in:
commit
8356e2ffca
@ -64,6 +64,7 @@
|
||||
cave_roof: (38, 21, 79),
|
||||
dirt: (69, 48, 15),
|
||||
scaffold: (195, 190, 212),
|
||||
lava: (184, 39, 0),
|
||||
vein: (222, 140, 39),
|
||||
),
|
||||
site: (
|
||||
|
@ -557,7 +557,7 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<AVERAGE_
|
||||
} else {
|
||||
use BlockKind::*;
|
||||
match kind {
|
||||
Air | Water => Rgb { r: 0, g: 0, b: 0 },
|
||||
Air | Water | Lava => Rgb { r: 0, g: 0, b: 0 },
|
||||
Rock => Rgb {
|
||||
r: 93,
|
||||
g: 110,
|
||||
|
@ -2,18 +2,45 @@
|
||||
use super::body::{object, Body};
|
||||
use super::{Density, Ori, Vel};
|
||||
use crate::{
|
||||
consts::{AIR_DENSITY, WATER_DENSITY},
|
||||
consts::{AIR_DENSITY, LAVA_DENSITY, WATER_DENSITY},
|
||||
util::{Dir, Plane, Projection},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::f32::consts::PI;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum LiquidKind {
|
||||
Water,
|
||||
Lava,
|
||||
}
|
||||
|
||||
impl LiquidKind {
|
||||
/// If an entity is in multiple overlapping liquid blocks, which one takes
|
||||
/// precedence? (should be a rare edge case, since checkerboard patterns of
|
||||
/// water and lava shouldn't show up in worldgen)
|
||||
pub fn merge(self, other: LiquidKind) -> LiquidKind {
|
||||
use LiquidKind::{Lava, Water};
|
||||
match (self, other) {
|
||||
(Water, Water) => Water,
|
||||
(Water, Lava) => Lava,
|
||||
(Lava, _) => Lava,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fluid medium in which the entity exists
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Fluid {
|
||||
Air { vel: Vel, elevation: f32 },
|
||||
Water { vel: Vel, depth: f32 },
|
||||
Air {
|
||||
vel: Vel,
|
||||
elevation: f32,
|
||||
},
|
||||
Liquid {
|
||||
kind: LiquidKind,
|
||||
vel: Vel,
|
||||
depth: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl Fluid {
|
||||
@ -21,7 +48,14 @@ impl Fluid {
|
||||
pub fn density(&self) -> Density {
|
||||
match self {
|
||||
Self::Air { .. } => Density(AIR_DENSITY),
|
||||
Self::Water { .. } => Density(WATER_DENSITY),
|
||||
Self::Liquid {
|
||||
kind: LiquidKind::Water,
|
||||
..
|
||||
} => Density(WATER_DENSITY),
|
||||
Self::Liquid {
|
||||
kind: LiquidKind::Lava,
|
||||
..
|
||||
} => Density(LAVA_DENSITY),
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,14 +87,14 @@ impl Fluid {
|
||||
pub fn flow_vel(&self) -> Vel {
|
||||
match self {
|
||||
Self::Air { vel, .. } => *vel,
|
||||
Self::Water { vel, .. } => *vel,
|
||||
Self::Liquid { vel, .. } => *vel,
|
||||
}
|
||||
}
|
||||
|
||||
// Very simple but useful in reducing mental overhead
|
||||
pub fn relative_flow(&self, vel: &Vel) -> Vel { Vel(self.flow_vel().0 - vel.0) }
|
||||
|
||||
pub fn is_liquid(&self) -> bool { matches!(self, Fluid::Water { .. }) }
|
||||
pub fn is_liquid(&self) -> bool { matches!(self, Fluid::Liquid { .. }) }
|
||||
|
||||
pub fn elevation(&self) -> Option<f32> {
|
||||
match self {
|
||||
@ -71,7 +105,7 @@ impl Fluid {
|
||||
|
||||
pub fn depth(&self) -> Option<f32> {
|
||||
match self {
|
||||
Fluid::Water { depth, .. } => Some(*depth),
|
||||
Fluid::Liquid { depth, .. } => Some(*depth),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,9 @@ pub const FRIC_GROUND: f32 = 0.15;
|
||||
// kg/m³
|
||||
pub const AIR_DENSITY: f32 = 1.225;
|
||||
pub const WATER_DENSITY: f32 = 999.1026;
|
||||
// LAVA_DENSITY is unsourced, estimated as "roughly three times higher" than
|
||||
// water
|
||||
pub const LAVA_DENSITY: f32 = 3000.0;
|
||||
pub const IRON_DENSITY: f32 = 7870.0;
|
||||
// pub const HUMAN_DENSITY: f32 = 1010.0; // real value
|
||||
pub const HUMAN_DENSITY: f32 = 990.0; // value we use to make humanoids gently float
|
||||
|
@ -1,5 +1,8 @@
|
||||
use super::SpriteKind;
|
||||
use crate::{comp::tool::ToolKind, make_case_elim};
|
||||
use crate::{
|
||||
comp::{fluid_dynamics::LiquidKind, tool::ToolKind},
|
||||
make_case_elim,
|
||||
};
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
@ -33,6 +36,7 @@ make_case_elim!(
|
||||
// being *very* fast).
|
||||
Rock = 0x10,
|
||||
WeakRock = 0x11, // Explodable
|
||||
Lava = 0x12,
|
||||
// 0x12 <= x < 0x20 is reserved for future rocks
|
||||
Grass = 0x20, // Note: *not* the same as grass sprites
|
||||
Snow = 0x21,
|
||||
@ -63,6 +67,15 @@ impl BlockKind {
|
||||
#[inline]
|
||||
pub const fn is_liquid(&self) -> bool { self.is_fluid() && !self.is_air() }
|
||||
|
||||
#[inline]
|
||||
pub const fn liquid_kind(&self) -> Option<LiquidKind> {
|
||||
Some(match self {
|
||||
BlockKind::Water => LiquidKind::Water,
|
||||
BlockKind::Lava => LiquidKind::Lava,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Determine whether the block is filled (i.e: fully solid). Right now,
|
||||
/// this is the opposite of being a fluid.
|
||||
#[inline]
|
||||
@ -168,6 +181,9 @@ impl Block {
|
||||
|
||||
#[inline]
|
||||
pub fn get_glow(&self) -> Option<u8> {
|
||||
if matches!(self.kind, BlockKind::Lava) {
|
||||
return Some(24);
|
||||
}
|
||||
match self.get_sprite()? {
|
||||
SpriteKind::StreetLamp | SpriteKind::StreetLampTall => Some(24),
|
||||
SpriteKind::Ember => Some(20),
|
||||
@ -217,7 +233,7 @@ impl Block {
|
||||
pub fn is_solid(&self) -> bool {
|
||||
self.get_sprite()
|
||||
.map(|s| s.solid_height().is_some())
|
||||
.unwrap_or(true)
|
||||
.unwrap_or(!matches!(self.kind, BlockKind::Lava))
|
||||
}
|
||||
|
||||
/// Can this block be exploded? If so, what 'power' is required to do so?
|
||||
|
@ -1,8 +1,8 @@
|
||||
use common::{
|
||||
comp::{
|
||||
fluid_dynamics::Fluid, Buff, BuffCategory, BuffChange, BuffEffect, BuffId, BuffKind,
|
||||
BuffSource, Buffs, Energy, Health, HealthChange, HealthSource, Inventory, ModifierKind,
|
||||
PhysicsState, Stats,
|
||||
fluid_dynamics::{Fluid, LiquidKind},
|
||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
||||
Energy, Health, HealthChange, HealthSource, Inventory, ModifierKind, PhysicsState, Stats,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
resources::DeltaTime,
|
||||
@ -45,34 +45,47 @@ impl<'a> System<'a> for Sys {
|
||||
// Set to false to avoid spamming server
|
||||
buffs.set_event_emission(false);
|
||||
stats.set_event_emission(false);
|
||||
for (entity, mut buff_comp, energy, mut stat, health) in (
|
||||
for (entity, mut buff_comp, energy, mut stat, health, physics_state) in (
|
||||
&read_data.entities,
|
||||
&mut buffs,
|
||||
&read_data.energies,
|
||||
&mut stats,
|
||||
&read_data.healths,
|
||||
read_data.physics_states.maybe(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let in_fluid = physics_state.and_then(|p| p.in_fluid);
|
||||
|
||||
if matches!(
|
||||
in_fluid,
|
||||
Some(Fluid::Liquid {
|
||||
kind: LiquidKind::Lava,
|
||||
..
|
||||
})
|
||||
) && !buff_comp.contains(BuffKind::Burning)
|
||||
{
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Burning,
|
||||
BuffData::new(200.0, None),
|
||||
vec![BuffCategory::Natural],
|
||||
BuffSource::World,
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
||||
let (buff_comp_kinds, buff_comp_buffs): (
|
||||
&HashMap<BuffKind, Vec<BuffId>>,
|
||||
&mut HashMap<BuffId, Buff>,
|
||||
) = buff_comp.parts();
|
||||
let mut expired_buffs = Vec::<BuffId>::new();
|
||||
|
||||
// For each buff kind present on entity, if the buff kind queues, only ticks
|
||||
// duration of strongest buff of that kind, else it ticks durations of all buffs
|
||||
// of that kind. Any buffs whose durations expire are marked expired.
|
||||
for (kind, ids) in buff_comp_kinds.iter() {
|
||||
// Only get the physics state component if the entity has the burning buff, as
|
||||
// we don't need it for any other conditions yet
|
||||
let in_fluid = if matches!(kind, BuffKind::Burning) {
|
||||
read_data
|
||||
.physics_states
|
||||
.get(entity)
|
||||
.and_then(|p| p.in_fluid)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if kind.queues() {
|
||||
if let Some((Some(buff), id)) =
|
||||
ids.get(0).map(|id| (buff_comp_buffs.get_mut(id), id))
|
||||
@ -257,7 +270,15 @@ fn tick_buff(
|
||||
}
|
||||
if let Some(remaining_time) = &mut buff.time {
|
||||
// Extinguish Burning buff when in water
|
||||
if matches!(buff.kind, BuffKind::Burning) && matches!(in_fluid, Some(Fluid::Water { .. })) {
|
||||
if matches!(buff.kind, BuffKind::Burning)
|
||||
&& matches!(
|
||||
in_fluid,
|
||||
Some(Fluid::Liquid {
|
||||
kind: LiquidKind::Water,
|
||||
..
|
||||
})
|
||||
)
|
||||
{
|
||||
*remaining_time = Duration::default();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use common::{
|
||||
comp::{
|
||||
body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST},
|
||||
fluid_dynamics::{Fluid, Wings},
|
||||
fluid_dynamics::{Fluid, LiquidKind, Wings},
|
||||
BeamSegment, Body, CharacterState, Collider, Density, Mass, Mounting, Ori, PhysicsState,
|
||||
Pos, PosVelDefer, PreviousPhysCache, Projectile, Scale, Shockwave, Stats, Sticky, Vel,
|
||||
},
|
||||
@ -905,13 +905,15 @@ impl<'a> PhysicsData<'a> {
|
||||
.terrain
|
||||
.get(pos.0.map(|e| e.floor() as i32))
|
||||
.ok()
|
||||
.and_then(|vox| vox.is_liquid().then_some(1.0))
|
||||
.map(|depth| Fluid::Water {
|
||||
depth,
|
||||
vel: Vel::zero(),
|
||||
.and_then(|vox| {
|
||||
vox.liquid_kind().map(|kind| Fluid::Liquid {
|
||||
kind,
|
||||
depth: 1.0,
|
||||
vel: Vel::zero(),
|
||||
})
|
||||
})
|
||||
.or_else(|| match physics_state.in_fluid {
|
||||
Some(Fluid::Water { .. }) | None => Some(Fluid::Air {
|
||||
Some(Fluid::Liquid { .. }) | None => Some(Fluid::Air {
|
||||
elevation: pos.0.z,
|
||||
vel: Vel::default(),
|
||||
}),
|
||||
@ -1541,24 +1543,27 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
});
|
||||
|
||||
// Find liquid immersion and wall collision all in one round of iteration
|
||||
let mut max_liquid_z = None::<f32>;
|
||||
let mut liquid = None::<(LiquidKind, f32)>;
|
||||
let mut wall_dir_collisions = [false; 4];
|
||||
near_iter.for_each(|(i, j, k)| {
|
||||
let block_pos = player_voxel_pos + Vec3::new(i, j, k);
|
||||
|
||||
if let Some(block) = terrain.get(block_pos).ok().copied() {
|
||||
// Check for liquid blocks
|
||||
if block.is_liquid() {
|
||||
if let Some(block_liquid) = block.liquid_kind() {
|
||||
let liquid_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
// The liquid part of a liquid block always extends 1 block high.
|
||||
max: block_pos.map(|e| e as f32) + Vec3::one(),
|
||||
};
|
||||
if player_aabb.collides_with_aabb(liquid_aabb) {
|
||||
max_liquid_z = Some(match max_liquid_z {
|
||||
Some(z) => z.max(liquid_aabb.max.z),
|
||||
None => liquid_aabb.max.z,
|
||||
});
|
||||
liquid = match liquid {
|
||||
Some((kind, max_liquid_z)) => Some((
|
||||
kind.merge(block_liquid),
|
||||
max_liquid_z.max(liquid_aabb.max.z),
|
||||
)),
|
||||
None => Some((block_liquid, liquid_aabb.max.z)),
|
||||
};
|
||||
}
|
||||
}
|
||||
// Check for walls
|
||||
@ -1594,23 +1599,24 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
physics_state.ground_vel = ground_vel;
|
||||
}
|
||||
|
||||
physics_state.in_fluid = max_liquid_z
|
||||
.map(|max_z| max_z - pos.0.z) // NOTE: assumes min_z == 0.0
|
||||
.map(|depth| {
|
||||
physics_state
|
||||
physics_state.in_fluid = liquid
|
||||
.map(|(kind, max_z)| (kind, max_z - pos.0.z)) // NOTE: assumes min_z == 0.0
|
||||
.map(|(kind, depth)| {
|
||||
(kind, physics_state
|
||||
.in_liquid()
|
||||
// This is suboptimal because it doesn't check for true depth,
|
||||
// so it can cause problems for situations like swimming down
|
||||
// a river and spawning or teleporting in(/to) water
|
||||
.map(|old_depth| (old_depth + old_pos.z - pos.0.z).max(depth))
|
||||
.unwrap_or(depth)
|
||||
.unwrap_or(depth))
|
||||
})
|
||||
.map(|depth| Fluid::Water {
|
||||
.map(|(kind, depth)| Fluid::Liquid {
|
||||
kind,
|
||||
depth,
|
||||
vel: Vel::zero(),
|
||||
})
|
||||
.or_else(|| match physics_state.in_fluid {
|
||||
Some(Fluid::Water { .. }) | None => Some(Fluid::Air {
|
||||
Some(Fluid::Liquid { .. }) | None => Some(Fluid::Air {
|
||||
elevation: pos.0.z,
|
||||
vel: Vel::default(),
|
||||
}),
|
||||
|
@ -34,6 +34,7 @@ pub struct Colors {
|
||||
pub cave_roof: (u8, u8, u8),
|
||||
pub dirt: (u8, u8, u8),
|
||||
pub scaffold: (u8, u8, u8),
|
||||
pub lava: (u8, u8, u8),
|
||||
pub vein: (u8, u8, u8),
|
||||
}
|
||||
|
||||
@ -210,12 +211,14 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
//make pits
|
||||
for z in cave_base - pit_depth..cave_base {
|
||||
if pit_condition && (cave_roof - cave_base) > 10 {
|
||||
let kind = if z < (cave_base - pit_depth) + (3 * pit_depth / 4) {
|
||||
BlockKind::Lava
|
||||
} else {
|
||||
BlockKind::Air
|
||||
};
|
||||
canvas.set(
|
||||
Vec3::new(wpos2d.x, wpos2d.y, z),
|
||||
Block::new(
|
||||
BlockKind::Air,
|
||||
noisy_color(info.index().colors.layer.scaffold.into(), 8),
|
||||
),
|
||||
Block::new(kind, noisy_color(info.index().colors.layer.lava.into(), 8)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user