Added one-way wall sprites

This commit is contained in:
Joshua Barretto 2023-05-19 13:24:45 +01:00
parent eda84a6569
commit c94d6c502a
9 changed files with 136 additions and 47 deletions

View File

@ -81,6 +81,7 @@ const int BARRELORGAN = 40;
const int POTION_SICKNESS = 41;
const int GIGA_SNOW = 42;
const int CYCLOPS_CHARGE = 43;
const int PORTAL_FIZZ = 45;
// meters per second squared (acceleration)
const float earth_gravity = 9.807;
@ -677,6 +678,18 @@ void main() {
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
);
break;
case PORTAL_FIZZ:
attr = Attr(
vec3(0, 0, lower * -0.5) + vec3(
sin(lifetime * 0.5 + rand0 * 10) + sin(lifetime * 0.3 + rand3 * 10),
sin(lifetime * 0.9 + rand1 * 10) + sin(lifetime * 0.4 + rand4 * 10),
sin(lifetime * 2 + rand2) * 0.2
) * 0.25,
vec3(pow(1.0 - abs(percent() - 0.5) * 2.0, 0.5)),
vec4(vec3(0.3, 3.0 + rand6, 2.5), 1),
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 1)
);
break;
default:
attr = Attr(
linear_motion(

View File

@ -4416,4 +4416,8 @@ GlowIceCrystal: Some((
],
wind_sway: 0.0,
)),
OneWayWall: Some((
variations: [],
wind_sway: 0.0,
)),
}

View File

@ -45,6 +45,8 @@ make_case_elim!(
// 0x12 <= x < 0x20 is reserved for future rocks
Grass = 0x20, // Note: *not* the same as grass sprites
Snow = 0x21,
// Snow to use with sites, to not attract snowfall particles
ArtSnow = 0x22,
// 0x21 <= x < 0x30 is reserved for future grasses
Earth = 0x30,
Sand = 0x31,
@ -58,8 +60,6 @@ make_case_elim!(
// often want to experiment with new kinds of block without allocating them a
// dedicated block kind.
Misc = 0xFE,
// Snow to use with sites, to not attract snowfall particles
ArtSnow = 0xFF,
}
);
@ -368,6 +368,17 @@ impl Block {
.unwrap_or(!matches!(self.kind, BlockKind::Lava))
}
pub fn valid_collision_dir(
&self,
entity_aabb: Aabb<f32>,
block_aabb: Aabb<f32>,
move_dir: Vec3<f32>,
) -> bool {
self.get_sprite().map_or(true, |sprite| {
sprite.valid_collision_dir(entity_aabb, block_aabb, move_dir, self)
})
}
/// Can this block be exploded? If so, what 'power' is required to do so?
/// Note that we don't really define what 'power' is. Consider the units
/// arbitrary and only important when compared to one-another.

View File

@ -5,6 +5,7 @@ use crate::{
},
lottery::LootSpec,
make_case_elim,
terrain::Block,
};
use hashbrown::HashMap;
use lazy_static::lazy_static;
@ -12,7 +13,7 @@ use num_derive::FromPrimitive;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt};
use strum::EnumIter;
use vek::Vec3;
use vek::*;
make_case_elim!(
sprite_kind,
@ -250,6 +251,7 @@ make_case_elim!(
FireBlock = 0xDF,
IceCrystal = 0xE0,
GlowIceCrystal = 0xE1,
OneWayWall = 0xE2,
}
);
@ -343,7 +345,8 @@ impl SpriteKind {
| SpriteKind::KeyDoor
| SpriteKind::BoneKeyhole
| SpriteKind::BoneKeyDoor
| SpriteKind::Bomb => 1.0,
| SpriteKind::Bomb
| SpriteKind::OneWayWall => 1.0,
// TODO: Figure out if this should be solid or not.
SpriteKind::Shelf => 1.0,
SpriteKind::Lantern => 0.9,
@ -390,6 +393,44 @@ impl SpriteKind {
})
}
pub fn valid_collision_dir(
&self,
entity_aabb: Aabb<f32>,
block_aabb: Aabb<f32>,
move_dir: Vec3<f32>,
parent: &Block,
) -> bool {
match self {
SpriteKind::OneWayWall => {
// Find the intrusion vector of the collision
let dir = entity_aabb.collision_vector_with_aabb(block_aabb);
// Determine an appropriate resolution vector (i.e: the minimum distance
// needed to push out of the block)
let max_axis = dir.map(|e| e.abs()).reduce_partial_min();
let resolve_dir = -dir.map(|e| {
if e.abs().to_bits() == max_axis.to_bits() {
e.signum()
} else {
0.0
}
});
let is_moving_into = move_dir.dot(resolve_dir) <= 0.0;
is_moving_into
&& parent.get_ori().map_or(false, |ori| {
Vec2::unit_y()
// .rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
.with_z(0.0)
.map2(resolve_dir, |e, r| (e - r).abs() < 0.1)
.reduce_and()
})
},
_ => true,
}
}
/// What loot table does collecting this sprite draw from?
/// None = block cannot be collected
/// Some(None) = block can be collected, but does not give back an item
@ -661,7 +702,8 @@ impl SpriteKind {
| SpriteKind::BoneKeyhole
| SpriteKind::BoneKeyDoor
| SpriteKind::IceCrystal
| SpriteKind::GlowIceCrystal,
| SpriteKind::GlowIceCrystal
| SpriteKind::OneWayWall,
)
}
}

View File

@ -1402,7 +1402,7 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
let mut collision = false;
// TODO: could short-circuit here
terrain.for_each_in(near_aabb, |block_pos, block| {
if block.is_solid() && hit(&block) {
if hit(&block) && block.is_solid() {
let block_aabb = Aabb {
min: block_pos.map(|e| e as f32),
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.solid_height()),
@ -1459,6 +1459,7 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
const MAX_ATTEMPTS: usize = 16;
pos.0 += pos_delta / increments as f32;
let vel2 = *vel;
let try_colliding_block = |pos: &Pos| {
//prof_span!("most colliding check");
// Calculate the player's AABB
@ -1487,16 +1488,14 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
};
// Determine whether the block's AABB collides with the player's AABB
if block_aabb.collides_with_aabb(player_aabb) {
if player_aabb.collides_with_aabb(block_aabb)
&& block.valid_collision_dir(player_aabb, block_aabb, vel2.0)
{
match &most_colliding {
// Select the minimum of the value from `player_overlap`
Some((_, other_block_aabb, _))
if {
// TODO: comment below is outdated (as of ~1 year ago)
// Find the maximum of the minimum collision axes (this bit
// is weird, trust me that it works)
player_overlap(block_aabb) >= player_overlap(*other_block_aabb)
} => {},
if player_overlap(block_aabb)
>= player_overlap(*other_block_aabb) => {},
_ => most_colliding = Some((block_pos, block_aabb, block)),
}
}
@ -1694,7 +1693,9 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
};
for dir in 0..4 {
if player_wall_aabbs[dir].collides_with_aabb(block_aabb) {
if player_wall_aabbs[dir].collides_with_aabb(block_aabb)
&& block.valid_collision_dir(player_wall_aabbs[dir], block_aabb, vel.0)
{
wall_dir_collisions[dir] = true;
}
}

View File

@ -96,6 +96,7 @@ pub enum ParticleMode {
GigaSnow = 42,
CyclopsCharge = 43,
SnowStorm = 44,
PortalFizz = 45,
}
impl ParticleMode {

View File

@ -1534,6 +1534,14 @@ impl ParticleMgr {
mode: ParticleMode::Snow,
cond: |_| true,
},
BlockParticles {
blocks: |boi| &boi.one_way_walls,
range: 2,
rate: 8.0,
lifetime: 3.0,
mode: ParticleMode::PortalFizz,
cond: |_| true,
},
];
let ecs = scene_data.state.ecs();

View File

@ -270,41 +270,43 @@ pub fn get_sprite_instances<'a, I: 'a>(
.0; // Awful PRNG
let ori = (block.get_ori().unwrap_or((seed % 4) as u8 * 2)) & 0b111;
let variation = seed as usize % cfg.variations.len();
let key = (sprite, variation);
if !cfg.variations.is_empty() {
let variation = seed as usize % cfg.variations.len();
let key = (sprite, variation);
// NOTE: Safe because we called sprite_config_for already.
// NOTE: Safe because 0 ≤ ori < 8
let light = light_map(wpos);
let glow = glow_map(wpos);
// NOTE: Safe because we called sprite_config_for already.
// NOTE: Safe because 0 ≤ ori < 8
let light = light_map(wpos);
let glow = glow_map(wpos);
for (lod_level, sprite_data) in lod_levels.iter_mut().zip(&sprite_data[&key]) {
let mat = Mat4::identity()
// Scaling for different LOD resolutions
.scaled_3d(sprite_data.scale)
// Offset
.translated_3d(sprite_data.offset)
.scaled_3d(SPRITE_SCALE)
.rotated_z(f32::consts::PI * 0.25 * ori as f32)
.translated_3d(
rel_pos + Vec3::new(0.5, 0.5, 0.0)
);
// Add an instance for each page in the sprite model
for page in sprite_data.vert_pages.clone() {
// TODO: could be more efficient to create once and clone while
// modifying vert_page
let instance = SpriteInstance::new(
mat,
cfg.wind_sway,
sprite_data.scale.z,
rel_pos.as_(),
ori,
light,
glow,
page,
sprite.is_door(),
);
set_instance(lod_level, instance, wpos);
for (lod_level, sprite_data) in lod_levels.iter_mut().zip(&sprite_data[&key]) {
let mat = Mat4::identity()
// Scaling for different LOD resolutions
.scaled_3d(sprite_data.scale)
// Offset
.translated_3d(sprite_data.offset)
.scaled_3d(SPRITE_SCALE)
.rotated_z(f32::consts::PI * 0.25 * ori as f32)
.translated_3d(
rel_pos + Vec3::new(0.5, 0.5, 0.0)
);
// Add an instance for each page in the sprite model
for page in sprite_data.vert_pages.clone() {
// TODO: could be more efficient to create once and clone while
// modifying vert_page
let instance = SpriteInstance::new(
mat,
cfg.wind_sway,
sprite_data.scale.z,
rel_pos.as_(),
ori,
light,
glow,
page,
sprite.is_door(),
);
set_instance(lod_level, instance, wpos);
}
}
}
}

View File

@ -48,6 +48,7 @@ pub struct BlocksOfInterest {
pub cricket2: Vec<Vec3<i32>>,
pub cricket3: Vec<Vec3<i32>>,
pub frogs: Vec<Vec3<i32>>,
pub one_way_walls: Vec<Vec3<i32>>, // Vec<(Vec3<i32>, Vec3<f32>)>
// Note: these are only needed for chunks within the iteraction range so this is a potential
// area for optimization
pub interactables: Vec<(Vec3<i32>, Interaction)>,
@ -87,6 +88,7 @@ impl BlocksOfInterest {
let mut cricket2 = Vec::new();
let mut cricket3 = Vec::new();
let mut frogs = Vec::new();
let mut one_way_walls = Vec::new();
let mut rng = ChaCha8Rng::from_seed(thread_rng().gen());
@ -172,6 +174,10 @@ impl BlocksOfInterest {
Some(SpriteKind::RepairBench) => {
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
},
Some(SpriteKind::OneWayWall) => one_way_walls.push(pos),/*one_way_walls.push((
pos,
Vec3::unit_y().rotated_z(std::f32::consts::PI * 0.25 * block.get_ori().unwrap_or(0) as f32),
)),*/
_ if block.is_mountable() => interactables.push((pos, Interaction::Mount)),
_ => {},
},
@ -217,6 +223,7 @@ impl BlocksOfInterest {
cricket2,
cricket3,
frogs,
one_way_walls,
interactables,
lights,
temperature,