mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/toggle-light-sprites' into 'master'
Added the ability to toggle light sources on and off See merge request veloren/veloren!4267
This commit is contained in:
commit
92a8bc2806
@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Potion of Agility
|
||||
- A way for servers to specify must-accept rules for players
|
||||
- A flag argument type for commands
|
||||
- The ability to turn lamp-like sprites on and off
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -40,6 +40,7 @@ hud-zoom_lock_indicator-remind = Zoom locked
|
||||
hud-zoom_lock_indicator-enable = Camera zoom locked
|
||||
hud-zoom_lock_indicator-disable = Camera zoom unlocked
|
||||
hud-activate = Activate
|
||||
hud-deactivate = Deactivate
|
||||
hud-collect = Collect
|
||||
hud-pick_up = Pick up
|
||||
hud-open = Open
|
||||
|
@ -1419,6 +1419,12 @@ impl Client {
|
||||
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::DisableLantern));
|
||||
}
|
||||
|
||||
pub fn toggle_sprite_light(&mut self, pos: VolumePos, enable: bool) {
|
||||
self.control_action(ControlAction::InventoryAction(
|
||||
InventoryAction::ToggleSpriteLight(pos, enable),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn remove_buff(&mut self, buff_id: BuffKind) {
|
||||
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::RemoveBuff(
|
||||
buff_id,
|
||||
|
@ -40,6 +40,9 @@ pub enum InventoryAction {
|
||||
Use(Slot),
|
||||
Sort,
|
||||
Collect(Vec3<i32>),
|
||||
// TODO: Not actually inventory-related: refactor to allow sprite interaction without
|
||||
// inventory manipulation!
|
||||
ToggleSpriteLight(VolumePos, bool),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
@ -4,6 +4,7 @@ pub const MAX_MOUNT_RANGE: f32 = 5.0;
|
||||
pub const MAX_SPRITE_MOUNT_RANGE: f32 = 2.0;
|
||||
pub const MAX_TRADE_RANGE: f32 = 20.0;
|
||||
pub const MAX_NPCINTERACT_RANGE: f32 = 30.0;
|
||||
pub const MAX_INTERACT_RANGE: f32 = 5.0;
|
||||
|
||||
pub const GRAVITY: f32 = 25.0;
|
||||
pub const FRIC_GROUND: f32 = 0.15;
|
||||
|
@ -338,6 +338,11 @@ pub enum ServerEvent {
|
||||
entity: EcsEntity,
|
||||
portal: EcsEntity,
|
||||
},
|
||||
ToggleSpriteLight {
|
||||
entity: EcsEntity,
|
||||
pos: Vec3<i32>,
|
||||
enable: bool,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct EventBus<E> {
|
||||
|
@ -117,8 +117,19 @@ impl CharacterBehavior for Data {
|
||||
sprite_pos: self.static_data.sprite_pos,
|
||||
required_item: inv_slot,
|
||||
};
|
||||
output_events
|
||||
.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
|
||||
|
||||
match self.static_data.sprite_kind {
|
||||
SpriteInteractKind::ToggleLight(enable) => {
|
||||
output_events.emit_server(ServerEvent::ToggleSpriteLight {
|
||||
entity: data.entity,
|
||||
pos: self.static_data.sprite_pos,
|
||||
enable,
|
||||
})
|
||||
},
|
||||
_ => output_events
|
||||
.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip)),
|
||||
}
|
||||
|
||||
if matches!(self.static_data.sprite_kind, SpriteInteractKind::Unlock) {
|
||||
output_events.emit_local(LocalEvent::CreateOutcome(
|
||||
Outcome::SpriteUnlocked {
|
||||
@ -157,6 +168,7 @@ pub enum SpriteInteractKind {
|
||||
Collectible,
|
||||
Unlock,
|
||||
Fallback,
|
||||
ToggleLight(bool),
|
||||
}
|
||||
|
||||
impl From<SpriteKind> for Option<SpriteInteractKind> {
|
||||
@ -233,6 +245,11 @@ impl SpriteInteractKind {
|
||||
Duration::from_secs_f32(1.0),
|
||||
Duration::from_secs_f32(0.3),
|
||||
),
|
||||
Self::ToggleLight(_) => (
|
||||
Duration::from_secs_f32(0.1),
|
||||
Duration::from_secs_f32(0.2),
|
||||
Duration::from_secs_f32(0.1),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ use crate::{
|
||||
},
|
||||
consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
mounting::Volume,
|
||||
outcome::Outcome,
|
||||
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
||||
terrain::{Block, TerrainGrid, UnlockKind},
|
||||
@ -1140,6 +1141,29 @@ pub fn handle_manipulate_loadout(
|
||||
let inv_manip = InventoryManip::Use(slot);
|
||||
output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
|
||||
},
|
||||
InventoryAction::ToggleSpriteLight(pos, enable) => {
|
||||
if matches!(pos.kind, Volume::Terrain) {
|
||||
let sprite_interact = sprite_interact::SpriteInteractKind::ToggleLight(enable);
|
||||
|
||||
let (buildup_duration, use_duration, recover_duration) =
|
||||
sprite_interact.durations();
|
||||
|
||||
update.character = CharacterState::SpriteInteract(sprite_interact::Data {
|
||||
static_data: sprite_interact::StaticData {
|
||||
buildup_duration,
|
||||
use_duration,
|
||||
recover_duration,
|
||||
sprite_pos: pos.pos,
|
||||
sprite_kind: sprite_interact,
|
||||
was_wielded: data.character.is_wield(),
|
||||
was_sneak: data.character.is_stealthy(),
|
||||
required_item: None,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,50 +156,44 @@ impl Block {
|
||||
|
||||
/* Constructors */
|
||||
|
||||
// TODO: Rename to `filled`
|
||||
#[inline]
|
||||
pub const fn new(kind: BlockKind, color: Rgb<u8>) -> Self {
|
||||
// TODO: we should probably assert this, overwriting the data fields with a
|
||||
// colour is bad news
|
||||
/*
|
||||
#[cfg(debug_assertions)]
|
||||
assert!(kind.is_filled());
|
||||
*/
|
||||
pub(super) const fn from_raw(kind: BlockKind, data: [u8; 3]) -> Self { Self { kind, data } }
|
||||
|
||||
Self {
|
||||
kind,
|
||||
// Colours are only valid for non-fluids
|
||||
data: if kind.is_filled() {
|
||||
[color.r, color.g, color.b]
|
||||
} else {
|
||||
[0; 3]
|
||||
},
|
||||
// TODO: Rename to `filled`, make caller guarantees stronger
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub const fn new(kind: BlockKind, color: Rgb<u8>) -> Self {
|
||||
if kind.is_filled() {
|
||||
Self::from_raw(kind, [color.r, color.g, color.b])
|
||||
} else {
|
||||
// Works because `SpriteKind::Empty` has no attributes
|
||||
let data = (SpriteKind::Empty as u32).to_be_bytes();
|
||||
Self::from_raw(kind, [data[1], data[2], data[3]])
|
||||
}
|
||||
}
|
||||
|
||||
// Only valid if `block_kind` is unfilled, so this is just a private utility
|
||||
// method
|
||||
#[inline]
|
||||
const fn unfilled(kind: BlockKind, sprite: SpriteKind) -> Self {
|
||||
pub fn unfilled(kind: BlockKind, sprite: SpriteKind) -> Self {
|
||||
#[cfg(debug_assertions)]
|
||||
assert!(!kind.is_filled());
|
||||
|
||||
let sprite_bytes = (sprite as u32).to_be_bytes();
|
||||
|
||||
Self {
|
||||
kind,
|
||||
data: [sprite_bytes[1], sprite_bytes[2], sprite_bytes[3]],
|
||||
}
|
||||
Self::from_raw(kind, sprite.to_initial_bytes())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn air(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Air, sprite) }
|
||||
pub fn air(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Air, sprite) }
|
||||
|
||||
#[inline]
|
||||
pub const fn empty() -> Self { Self::air(SpriteKind::Empty) }
|
||||
pub const fn empty() -> Self {
|
||||
// Works because `SpriteKind::Empty` has no attributes
|
||||
let data = (SpriteKind::Empty as u32).to_be_bytes();
|
||||
Self::from_raw(BlockKind::Air, [data[1], data[2], data[3]])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn water(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Water, sprite) }
|
||||
pub fn water(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Water, sprite) }
|
||||
|
||||
/* Sprite decoding */
|
||||
|
||||
@ -258,6 +252,9 @@ impl Block {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) const fn data(&self) -> [u8; 3] { self.data }
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) const fn with_data(mut self, data: [u8; 3]) -> Self {
|
||||
self.data = data;
|
||||
@ -377,13 +374,13 @@ impl Block {
|
||||
|
||||
#[inline]
|
||||
pub fn get_glow(&self) -> Option<u8> {
|
||||
match self.kind() {
|
||||
BlockKind::Lava => Some(24),
|
||||
BlockKind::GlowingRock | BlockKind::GlowingWeakRock => Some(10),
|
||||
BlockKind::GlowingMushroom => Some(20),
|
||||
let glow_level = match self.kind() {
|
||||
BlockKind::Lava => 24,
|
||||
BlockKind::GlowingRock | BlockKind::GlowingWeakRock => 10,
|
||||
BlockKind::GlowingMushroom => 20,
|
||||
_ => match self.get_sprite()? {
|
||||
SpriteKind::StreetLamp | SpriteKind::StreetLampTall => Some(24),
|
||||
SpriteKind::Ember | SpriteKind::FireBlock => Some(20),
|
||||
SpriteKind::StreetLamp | SpriteKind::StreetLampTall => 24,
|
||||
SpriteKind::Ember | SpriteKind::FireBlock => 20,
|
||||
SpriteKind::WallLamp
|
||||
| SpriteKind::WallLampSmall
|
||||
| SpriteKind::WallSconce
|
||||
@ -391,8 +388,8 @@ impl Block {
|
||||
| SpriteKind::ChristmasOrnament
|
||||
| SpriteKind::CliffDecorBlock
|
||||
| SpriteKind::Orb
|
||||
| SpriteKind::Candle => Some(16),
|
||||
SpriteKind::DiamondLight => Some(30),
|
||||
| SpriteKind::Candle => 16,
|
||||
SpriteKind::DiamondLight => 30,
|
||||
SpriteKind::Velorite
|
||||
| SpriteKind::VeloriteFrag
|
||||
| SpriteKind::CavernGrassBlueShort
|
||||
@ -400,12 +397,12 @@ impl Block {
|
||||
| SpriteKind::CavernGrassBlueLong
|
||||
| SpriteKind::CavernLillypadBlue
|
||||
| SpriteKind::CavernMycelBlue
|
||||
| SpriteKind::CeilingMushroom => Some(6),
|
||||
| SpriteKind::CeilingMushroom => 6,
|
||||
SpriteKind::CaveMushroom
|
||||
| SpriteKind::CookingPot
|
||||
| SpriteKind::CrystalHigh
|
||||
| SpriteKind::CrystalLow => Some(10),
|
||||
SpriteKind::SewerMushroom => Some(16),
|
||||
| SpriteKind::CrystalLow => 10,
|
||||
SpriteKind::SewerMushroom => 16,
|
||||
SpriteKind::Amethyst
|
||||
| SpriteKind::Ruby
|
||||
| SpriteKind::Sapphire
|
||||
@ -417,14 +414,23 @@ impl Block {
|
||||
| SpriteKind::DiamondSmall
|
||||
| SpriteKind::RubySmall
|
||||
| SpriteKind::EmeraldSmall
|
||||
| SpriteKind::SapphireSmall => Some(3),
|
||||
SpriteKind::Lantern => Some(24),
|
||||
SpriteKind::SeashellLantern | SpriteKind::GlowIceCrystal => Some(16),
|
||||
SpriteKind::SeaDecorEmblem => Some(12),
|
||||
SpriteKind::SeaDecorBlock | SpriteKind::HaniwaKeyDoor => Some(10),
|
||||
SpriteKind::Mine => Some(2),
|
||||
_ => None,
|
||||
| SpriteKind::SapphireSmall => 3,
|
||||
SpriteKind::Lantern => 24,
|
||||
SpriteKind::SeashellLantern | SpriteKind::GlowIceCrystal => 16,
|
||||
SpriteKind::SeaDecorEmblem => 12,
|
||||
SpriteKind::SeaDecorBlock | SpriteKind::HaniwaKeyDoor => 10,
|
||||
SpriteKind::Mine => 2,
|
||||
_ => return None,
|
||||
},
|
||||
};
|
||||
|
||||
if self
|
||||
.get_attr::<sprite::LightEnabled>()
|
||||
.map_or(true, |l| l.0)
|
||||
{
|
||||
Some(glow_level)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -625,6 +631,11 @@ impl Block {
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply a light toggle to this block, if possible
|
||||
pub fn with_toggle_light(self, enable: bool) -> Option<Self> {
|
||||
self.with_attr(sprite::LightEnabled(enable)).ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(&self) -> BlockKind { self.kind }
|
||||
|
||||
@ -648,7 +659,7 @@ impl Block {
|
||||
#[must_use]
|
||||
pub fn into_vacant(self) -> Self {
|
||||
if self.is_fluid() {
|
||||
Block::new(self.kind(), Rgb::zero())
|
||||
Block::unfilled(self.kind(), SpriteKind::Empty)
|
||||
} else {
|
||||
// FIXME: Figure out if there's some sensible way to determine what medium to
|
||||
// replace a filled block with if it's removed.
|
||||
@ -675,24 +686,3 @@ impl Block {
|
||||
|
||||
const _: () = assert!(core::mem::size_of::<BlockKind>() == 1);
|
||||
const _: () = assert!(core::mem::size_of::<Block>() == 4);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
#[test]
|
||||
fn convert_u32() {
|
||||
for bk in BlockKind::iter() {
|
||||
let block = Block::new(bk, Rgb::new(165, 90, 204)); // Pretty unique bit patterns
|
||||
if bk.is_filled() {
|
||||
assert_eq!(Block::from_u32(block.to_u32()), Some(block));
|
||||
} else {
|
||||
assert_eq!(
|
||||
Block::from_u32(block.to_u32()),
|
||||
Some(Block::new(bk, Rgb::zero())),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ impl TerrainChunk {
|
||||
pub fn water(sea_level: i32) -> TerrainChunk {
|
||||
TerrainChunk::new(
|
||||
sea_level,
|
||||
Block::new(BlockKind::Water, Rgb::zero()),
|
||||
Block::water(SpriteKind::Empty),
|
||||
Block::air(SpriteKind::Empty),
|
||||
TerrainChunkMeta::void(),
|
||||
)
|
||||
|
@ -120,18 +120,12 @@ sprites! {
|
||||
DungeonChest4 = 0x35,
|
||||
DungeonChest5 = 0x36,
|
||||
CoralChest = 0x37,
|
||||
HaniwaUrn = 0x38,
|
||||
HaniwaUrn = 0x38,
|
||||
CommonLockedChest = 0x39,
|
||||
ChestBuried = 0x3A,
|
||||
Crate = 0x3B,
|
||||
Barrel = 0x3C,
|
||||
CrateBlock = 0x3D,
|
||||
// Standalone lights
|
||||
Lantern = 0x40,
|
||||
StreetLamp = 0x41,
|
||||
StreetLampTall = 0x42,
|
||||
SeashellLantern = 0x43,
|
||||
FireBowlGround = 0x44,
|
||||
// Wall
|
||||
HangingBasket = 0x50,
|
||||
HangingSign = 0x51,
|
||||
@ -355,21 +349,42 @@ sprites! {
|
||||
SeaDecorPillar = 0x1E,
|
||||
MagicalSeal = 0x1F,
|
||||
},
|
||||
Lamp = 7 has Ori, LightEnabled {
|
||||
// Standalone lights
|
||||
Lantern = 0,
|
||||
StreetLamp = 1,
|
||||
StreetLampTall = 2,
|
||||
SeashellLantern = 3,
|
||||
FireBowlGround = 4,
|
||||
},
|
||||
}
|
||||
|
||||
attributes! {
|
||||
Ori { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Ori(x)| x as u16 },
|
||||
Growth { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Growth(x)| x as u16 },
|
||||
LightEnabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightEnabled(x)| x as u16 },
|
||||
}
|
||||
|
||||
// The orientation of the sprite, 0..8
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
// The orientation of the sprite, 0..16
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
|
||||
pub struct Ori(pub u8);
|
||||
|
||||
// The growth of the plant, 0..16
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Growth(pub u8);
|
||||
|
||||
impl Default for Growth {
|
||||
fn default() -> Self { Self(15) }
|
||||
}
|
||||
|
||||
// Whether a light has been toggled on or off.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct LightEnabled(pub bool);
|
||||
|
||||
impl Default for LightEnabled {
|
||||
fn default() -> Self { Self(true) }
|
||||
}
|
||||
|
||||
impl SpriteKind {
|
||||
#[inline]
|
||||
pub fn solid_height(&self) -> Option<f32> {
|
||||
|
@ -158,6 +158,15 @@ macro_rules! sprites {
|
||||
},)*
|
||||
}
|
||||
}
|
||||
|
||||
#[inline] pub(super) fn to_initial_bytes(self) -> [u8; 3] {
|
||||
let sprite_bytes = (self as u32).to_be_bytes();
|
||||
let block = Block::from_raw(super::BlockKind::Air, [sprite_bytes[1], sprite_bytes[2], sprite_bytes[3]]);
|
||||
match self.category() {
|
||||
$(Category::$category_name => block$($(.with_attr($attr::default()).unwrap())*)?,)*
|
||||
}
|
||||
.data()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -170,12 +179,14 @@ pub enum AttributeError<E> {
|
||||
Attribute(E),
|
||||
}
|
||||
|
||||
pub trait Attribute: Sized {
|
||||
/// The unique index assigned to this attribute, used to index offset arrays
|
||||
pub trait Attribute: Default + Sized {
|
||||
/// The unique index assigned to this attribute, used to index offset
|
||||
/// arrays.
|
||||
const INDEX: usize;
|
||||
/// The number of bits required to represent this attribute
|
||||
/// The number of bits required to represent this attribute.
|
||||
const BITS: u8;
|
||||
type Error;
|
||||
/// The error that might occur when decoding the attribute from bits.
|
||||
type Error: core::fmt::Debug;
|
||||
fn from_bits(bits: u16) -> Result<Self, Self::Error>;
|
||||
fn into_bits(self) -> u16;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use common::{
|
||||
Inventory, LootOwner, Pos, SkillGroupKind,
|
||||
},
|
||||
consts::{
|
||||
MAX_MOUNT_RANGE, MAX_NPCINTERACT_RANGE, MAX_SPRITE_MOUNT_RANGE,
|
||||
MAX_INTERACT_RANGE, MAX_MOUNT_RANGE, MAX_NPCINTERACT_RANGE, MAX_SPRITE_MOUNT_RANGE,
|
||||
SOUND_TRAVEL_DIST_PER_VOLUME,
|
||||
},
|
||||
event::EventBus,
|
||||
@ -194,7 +194,7 @@ pub fn handle_mount_volume(server: &mut Server, rider: EcsEntity, volume_pos: Vo
|
||||
block,
|
||||
rider,
|
||||
}).is_ok();
|
||||
#[cfg(feature = "worldgen")]
|
||||
#[cfg(feature = "worldgen")]
|
||||
if _link_successful {
|
||||
let uid_allocator = state.ecs().read_resource::<IdMaps>();
|
||||
if let Some(rider_entity) = uid_allocator.uid_entity(rider)
|
||||
@ -469,3 +469,27 @@ pub fn handle_tame_pet(server: &mut Server, pet_entity: EcsEntity, owner_entity:
|
||||
// showing taming success?
|
||||
tame_pet(server.state.ecs(), pet_entity, owner_entity);
|
||||
}
|
||||
|
||||
pub fn handle_toggle_sprite_light(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
pos: Vec3<i32>,
|
||||
enable: bool,
|
||||
) {
|
||||
let state = server.state_mut();
|
||||
// TODO: Implement toggling lights on volume entities
|
||||
if let Some(entity_pos) = state.ecs().read_storage::<Pos>().get(entity)
|
||||
&& entity_pos.0.distance_squared(pos.as_()) < MAX_INTERACT_RANGE.powi(2)
|
||||
&& state.can_set_block(pos)
|
||||
{
|
||||
if let Some(new_block) = state
|
||||
.terrain()
|
||||
.get(pos)
|
||||
.ok()
|
||||
.and_then(|block| block.with_toggle_light(enable))
|
||||
{
|
||||
state.set_block(pos, new_block);
|
||||
// TODO: Emit outcome
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ use group_manip::handle_group;
|
||||
use information::handle_site_info;
|
||||
use interaction::{
|
||||
handle_create_sprite, handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction,
|
||||
handle_set_pet_stay, handle_sound, handle_unmount,
|
||||
handle_set_pet_stay, handle_sound, handle_toggle_sprite_light, handle_unmount,
|
||||
};
|
||||
use inventory_manip::handle_inventory;
|
||||
use invite::{handle_invite, handle_invite_response};
|
||||
@ -305,6 +305,11 @@ impl Server {
|
||||
ServerEvent::StartTeleporting { entity, portal } => {
|
||||
handle_start_teleporting(self, entity, portal)
|
||||
},
|
||||
ServerEvent::ToggleSpriteLight {
|
||||
entity,
|
||||
pos,
|
||||
enable,
|
||||
} => handle_toggle_sprite_light(self, entity, pos, enable),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2161,6 +2161,11 @@ impl Hud {
|
||||
Some(GameInput::Interact),
|
||||
i18n.get_msg("hud-read").to_string(),
|
||||
)],
|
||||
// TODO: change to turn on/turn off?
|
||||
BlockInteraction::LightToggle(enable) => vec![(
|
||||
Some(GameInput::Interact),
|
||||
i18n.get_msg(if *enable { "hud-activate" } else { "hud-deactivate" }).to_string(),
|
||||
)],
|
||||
};
|
||||
|
||||
// This is only done once per frame, so it's not a performance issue
|
||||
|
@ -294,7 +294,7 @@ pub fn generate_mesh<'a>(
|
||||
let d = d + 2;
|
||||
let flat = {
|
||||
let mut volume = vol.cached();
|
||||
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
|
||||
const AIR: Block = Block::empty();
|
||||
// TODO: Once we can manage it sensibly, consider using something like
|
||||
// Option<Block> instead of just assuming air.
|
||||
let mut flat = vec![AIR; (w * h * d) as usize];
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::hud::CraftingTab;
|
||||
use common::{
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
terrain::{sprite, Block, BlockKind, SpriteKind},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use common_base::span;
|
||||
@ -16,6 +16,7 @@ pub enum Interaction {
|
||||
Craft(CraftingTab),
|
||||
Mount,
|
||||
Read,
|
||||
LightToggle(bool),
|
||||
}
|
||||
|
||||
pub enum FireplaceType {
|
||||
@ -138,80 +139,96 @@ impl BlocksOfInterest {
|
||||
}
|
||||
},
|
||||
BlockKind::Snow | BlockKind::Ice if rng.gen_range(0..16) == 0 => snow.push(pos),
|
||||
_ => match block.get_sprite() {
|
||||
Some(SpriteKind::Ember) => {
|
||||
fires.push(pos);
|
||||
smokers.push(SmokerProperties::new(pos, FireplaceType::House));
|
||||
},
|
||||
// Offset positions to account for block height.
|
||||
// TODO: Is this a good idea?
|
||||
Some(SpriteKind::StreetLamp) => fire_bowls.push(pos + Vec3::unit_z() * 2),
|
||||
Some(SpriteKind::FireBowlGround) => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
Some(SpriteKind::StreetLampTall) => fire_bowls.push(pos + Vec3::unit_z() * 4),
|
||||
Some(SpriteKind::WallSconce) => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
Some(SpriteKind::Beehive) => beehives.push(pos),
|
||||
Some(SpriteKind::CrystalHigh) => fireflies.push(pos),
|
||||
Some(SpriteKind::Reed) => {
|
||||
reeds.push(pos);
|
||||
fireflies.push(pos);
|
||||
if rng.gen_range(0..12) == 0 {
|
||||
frogs.push(pos);
|
||||
_ => {
|
||||
if let Some(sprite) = block.get_sprite() {
|
||||
if sprite.category() == sprite::Category::Lamp {
|
||||
if let Ok(sprite::LightEnabled(enabled)) = block.get_attr() {
|
||||
interactables.push((pos, Interaction::LightToggle(!enabled)));
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(SpriteKind::CaveMushroom) => fireflies.push(pos),
|
||||
Some(SpriteKind::PinkFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::PurpleFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::RedFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::WhiteFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::YellowFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::Sunflower) => flowers.push(pos),
|
||||
Some(SpriteKind::CraftingBench) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::SmokeDummy) => {
|
||||
smokers.push(SmokerProperties::new(pos, FireplaceType::Workshop));
|
||||
},
|
||||
Some(SpriteKind::Forge) => interactables
|
||||
.push((pos, Interaction::Craft(CraftingTab::ProcessedMaterial))),
|
||||
Some(SpriteKind::TanningRack) => interactables
|
||||
.push((pos, Interaction::Craft(CraftingTab::ProcessedMaterial))),
|
||||
Some(SpriteKind::SpinningWheel) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::Loom) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::Cauldron) => {
|
||||
fires.push(pos);
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Potion)))
|
||||
},
|
||||
Some(SpriteKind::Anvil) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Weapon)))
|
||||
},
|
||||
Some(SpriteKind::CookingPot) => {
|
||||
fires.push(pos);
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Food)))
|
||||
},
|
||||
Some(SpriteKind::DismantlingBench) => {
|
||||
fires.push(pos);
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Dismantle)))
|
||||
},
|
||||
Some(SpriteKind::RepairBench) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
Some(SpriteKind::OneWayWall) => one_way_walls.push((
|
||||
pos,
|
||||
Vec2::unit_y()
|
||||
.rotated_z(
|
||||
std::f32::consts::PI * 0.25 * block.get_ori().unwrap_or(0) as f32,
|
||||
)
|
||||
.with_z(0.0),
|
||||
)),
|
||||
Some(SpriteKind::Sign | SpriteKind::HangingSign) => {
|
||||
interactables.push((pos, Interaction::Read))
|
||||
},
|
||||
_ if block.is_mountable() => interactables.push((pos, Interaction::Mount)),
|
||||
_ => {},
|
||||
|
||||
if block.is_mountable() {
|
||||
interactables.push((pos, Interaction::Mount));
|
||||
}
|
||||
|
||||
match sprite {
|
||||
SpriteKind::Ember => {
|
||||
fires.push(pos);
|
||||
smokers.push(SmokerProperties::new(pos, FireplaceType::House));
|
||||
},
|
||||
// Offset positions to account for block height.
|
||||
// TODO: Is this a good idea?
|
||||
SpriteKind::StreetLamp => fire_bowls.push(pos + Vec3::unit_z() * 2),
|
||||
SpriteKind::FireBowlGround => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
SpriteKind::StreetLampTall => fire_bowls.push(pos + Vec3::unit_z() * 4),
|
||||
SpriteKind::WallSconce => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
SpriteKind::Beehive => beehives.push(pos),
|
||||
SpriteKind::CrystalHigh => fireflies.push(pos),
|
||||
SpriteKind::Reed => {
|
||||
reeds.push(pos);
|
||||
fireflies.push(pos);
|
||||
if rng.gen_range(0..12) == 0 {
|
||||
frogs.push(pos);
|
||||
}
|
||||
},
|
||||
SpriteKind::CaveMushroom => fireflies.push(pos),
|
||||
SpriteKind::PinkFlower => flowers.push(pos),
|
||||
SpriteKind::PurpleFlower => flowers.push(pos),
|
||||
SpriteKind::RedFlower => flowers.push(pos),
|
||||
SpriteKind::WhiteFlower => flowers.push(pos),
|
||||
SpriteKind::YellowFlower => flowers.push(pos),
|
||||
SpriteKind::Sunflower => flowers.push(pos),
|
||||
SpriteKind::CraftingBench => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
SpriteKind::SmokeDummy => {
|
||||
smokers.push(SmokerProperties::new(pos, FireplaceType::Workshop));
|
||||
},
|
||||
SpriteKind::Forge => interactables
|
||||
.push((pos, Interaction::Craft(CraftingTab::ProcessedMaterial))),
|
||||
SpriteKind::TanningRack => interactables
|
||||
.push((pos, Interaction::Craft(CraftingTab::ProcessedMaterial))),
|
||||
SpriteKind::SpinningWheel => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
SpriteKind::Loom => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
SpriteKind::Cauldron => {
|
||||
fires.push(pos);
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Potion)))
|
||||
},
|
||||
SpriteKind::Anvil => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Weapon)))
|
||||
},
|
||||
SpriteKind::CookingPot => {
|
||||
fires.push(pos);
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::Food)))
|
||||
},
|
||||
SpriteKind::DismantlingBench => {
|
||||
fires.push(pos);
|
||||
interactables
|
||||
.push((pos, Interaction::Craft(CraftingTab::Dismantle)))
|
||||
},
|
||||
SpriteKind::RepairBench => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
SpriteKind::OneWayWall => one_way_walls.push((
|
||||
pos,
|
||||
Vec2::unit_y()
|
||||
.rotated_z(
|
||||
std::f32::consts::PI
|
||||
* 0.25
|
||||
* block.get_ori().unwrap_or(0) as f32,
|
||||
)
|
||||
.with_z(0.0),
|
||||
)),
|
||||
SpriteKind::Sign | SpriteKind::HangingSign => {
|
||||
interactables.push((pos, Interaction::Read))
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
if block.collectible_id().is_some() {
|
||||
|
@ -10,7 +10,7 @@ use client::Client;
|
||||
use common::{
|
||||
comp,
|
||||
comp::{ship::figuredata::VOXEL_COLLIDER_MANIFEST, tool::ToolKind, Collider, Content},
|
||||
consts::{MAX_PICKUP_RANGE, MAX_SPRITE_MOUNT_RANGE, TELEPORTER_RADIUS},
|
||||
consts::{MAX_INTERACT_RANGE, MAX_PICKUP_RANGE, MAX_SPRITE_MOUNT_RANGE, TELEPORTER_RADIUS},
|
||||
link::Is,
|
||||
mounting::{Mount, Rider, VolumePos, VolumeRider},
|
||||
terrain::{Block, TerrainGrid, UnlockKind},
|
||||
@ -36,6 +36,7 @@ pub enum BlockInteraction {
|
||||
Mine(ToolKind),
|
||||
Mount,
|
||||
Read(Content),
|
||||
LightToggle(bool),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -105,6 +106,7 @@ impl Interactable {
|
||||
},
|
||||
Interaction::Craft(tab) => BlockInteraction::Craft(tab),
|
||||
Interaction::Mount => BlockInteraction::Mount,
|
||||
Interaction::LightToggle(enable) => BlockInteraction::LightToggle(enable),
|
||||
};
|
||||
Some(Self::Block(block, volume_pos, block_interaction))
|
||||
}
|
||||
@ -341,8 +343,9 @@ pub(super) fn select_interactable(
|
||||
.filter(|(wpos, volume_pos, interaction)| {
|
||||
match interaction {
|
||||
Interaction::Mount => !is_volume_rider.contains(player_entity)
|
||||
&& wpos.distance_squared(player_pos) < MAX_SPRITE_MOUNT_RANGE * MAX_SPRITE_MOUNT_RANGE
|
||||
&& !is_volume_rider.join().any(|is_volume_rider| is_volume_rider.pos == *volume_pos),
|
||||
&& wpos.distance_squared(player_pos) < MAX_SPRITE_MOUNT_RANGE.powi(2)
|
||||
&& !is_volume_rider.join().any(|is_volume_rider| is_volume_rider.pos == *volume_pos),
|
||||
Interaction::LightToggle(_) => wpos.distance_squared(player_pos) < MAX_INTERACT_RANGE.powi(2),
|
||||
_ => true,
|
||||
}
|
||||
})
|
||||
|
@ -1042,6 +1042,9 @@ impl PlayState for SessionState {
|
||||
// currently supported
|
||||
common::mounting::Volume::Entity(_) => {},
|
||||
},
|
||||
BlockInteraction::LightToggle(enable) => {
|
||||
client.toggle_sprite_light(*pos, *enable);
|
||||
},
|
||||
}
|
||||
},
|
||||
Interactable::Entity(entity) => {
|
||||
|
@ -47,7 +47,7 @@ pub struct Colors {
|
||||
pub vein: (u8, u8, u8),
|
||||
}
|
||||
|
||||
const EMPTY_AIR: Block = Block::air(SpriteKind::Empty);
|
||||
const EMPTY_AIR: Block = Block::empty();
|
||||
|
||||
pub struct PathLocals {
|
||||
pub riverless_alt: f32,
|
||||
|
@ -167,7 +167,7 @@ impl Archetype for Keep {
|
||||
make_block(colors.pole.0, colors.pole.1, colors.pole.2).with_priority(important_layer);
|
||||
let flag =
|
||||
make_block(flag_color.0, flag_color.1, flag_color.2).with_priority(important_layer);
|
||||
const AIR: Block = Block::air(SpriteKind::Empty);
|
||||
const AIR: Block = Block::empty();
|
||||
const EMPTY: BlockMask = BlockMask::nothing();
|
||||
let internal = BlockMask::new(AIR, internal_layer);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user