mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Vox spawn hack
This commit is contained in:
parent
61edae79ae
commit
6a6d68ddb9
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1421,9 +1421,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dot_vox"
|
name = "dot_vox"
|
||||||
version = "4.1.0"
|
version = "4.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/Imberflur/dot_vox.git#23a877bcaeba0f86035a50bc49a89e5391eb38c7"
|
||||||
checksum = "83c18405ef54de0398b77a3ec8394d3a1639e7bf060e1385201e8db40c44ab41"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@ -5744,6 +5743,7 @@ dependencies = [
|
|||||||
"dot_vox",
|
"dot_vox",
|
||||||
"enum-iterator",
|
"enum-iterator",
|
||||||
"hashbrown 0.11.2",
|
"hashbrown 0.11.2",
|
||||||
|
"image",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
|
BIN
assets/coliseum.vox
(Stored with Git LFS)
Normal file
BIN
assets/coliseum.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
3
assets/place.ron
Normal file
3
assets/place.ron
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
(pieces: [
|
||||||
|
("coliseum", (0, 0, 0)),
|
||||||
|
])
|
BIN
assets/thing.vox
(Stored with Git LFS)
Normal file
BIN
assets/thing.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -47,7 +47,8 @@ rand = "0.8"
|
|||||||
|
|
||||||
# Assets
|
# Assets
|
||||||
common-assets = {package = "veloren-common-assets", path = "assets"}
|
common-assets = {package = "veloren-common-assets", path = "assets"}
|
||||||
dot_vox = "4.0"
|
dot_vox = { git = "https://github.com/Imberflur/dot_vox.git" }
|
||||||
|
image = { version = "0.23.12", default-features = false, features = ["png"] }
|
||||||
|
|
||||||
# Assets
|
# Assets
|
||||||
serde_repr = "0.1.6"
|
serde_repr = "0.1.6"
|
||||||
|
@ -9,7 +9,7 @@ version = "0.10.0"
|
|||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
assets_manager = {version = "0.5.0", features = ["bincode", "ron", "json", "hot-reloading"]}
|
assets_manager = {version = "0.5.0", features = ["bincode", "ron", "json", "hot-reloading"]}
|
||||||
ron = { version = "0.6", default-features = false }
|
ron = { version = "0.6", default-features = false }
|
||||||
dot_vox = "4.0"
|
dot_vox = { git = "https://github.com/Imberflur/dot_vox.git" }
|
||||||
image = { version = "0.23.12", default-features = false, features = ["png"] }
|
image = { version = "0.23.12", default-features = false, features = ["png"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ pub use self::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol},
|
vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, VolSize, Vox, WriteVol},
|
||||||
volumes::dyna::Dyna,
|
volumes::{chunk::Chunk, dyna::Dyna, vol_grid_3d::VolGrid3d},
|
||||||
};
|
};
|
||||||
use dot_vox::DotVoxData;
|
use dot_vox::DotVoxData;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -252,3 +252,93 @@ impl MatSegment {
|
|||||||
impl From<&DotVoxData> for MatSegment {
|
impl From<&DotVoxData> for MatSegment {
|
||||||
fn from(dot_vox_data: &DotVoxData) -> Self { Self::from_vox(dot_vox_data, false) }
|
fn from(dot_vox_data: &DotVoxData) -> Self { Self::from_vox(dot_vox_data, false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SscSize;
|
||||||
|
impl VolSize for SscSize {
|
||||||
|
const SIZE: Vec3<u32> = Vec3 {
|
||||||
|
x: 32,
|
||||||
|
y: 32,
|
||||||
|
z: 32,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SparseScene = VolGrid3d<Chunk<Cell, SscSize, ()>>;
|
||||||
|
|
||||||
|
impl From<&DotVoxData> for SparseScene {
|
||||||
|
fn from(dot_vox_data: &DotVoxData) -> Self {
|
||||||
|
let mut sparse_scene = match VolGrid3d::new() {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(_) => panic!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut models = dot_vox_data.scene.clone();
|
||||||
|
|
||||||
|
// Needed to work with vox files without a scene graph
|
||||||
|
if models.is_empty() {
|
||||||
|
models.push((
|
||||||
|
dot_vox::Transform {
|
||||||
|
t: [0, 0, 0],
|
||||||
|
r: [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (transform, model_id) in models {
|
||||||
|
if let Some(model) = dot_vox_data.models.get(model_id) {
|
||||||
|
let palette = dot_vox_data
|
||||||
|
.palette
|
||||||
|
.iter()
|
||||||
|
.map(|col| Rgba::from(col.to_ne_bytes()).into())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Rotation
|
||||||
|
let rot = Mat3::from_row_arrays(transform.r).map(|e| e as i32);
|
||||||
|
// Get the rotated size of the model
|
||||||
|
let size = rot.map(|e| e.abs() as u32)
|
||||||
|
* Vec3::new(model.size.x, model.size.y, model.size.z);
|
||||||
|
// Position of min corner
|
||||||
|
let pos = Vec3::<i32>::from(transform.t)
|
||||||
|
.map2(size, |m, s| (s, m))
|
||||||
|
.map2(rot * Vec3::<i32>::one(), |(s, m), f| {
|
||||||
|
m - (s as i32 + f.min(0) * -1) / 2
|
||||||
|
});
|
||||||
|
// dbg!(pos);
|
||||||
|
// Insert required chunks
|
||||||
|
let min_key = sparse_scene.pos_key(pos);
|
||||||
|
let max_key = sparse_scene.pos_key(pos + size.map(|e| e as i32 - 1));
|
||||||
|
for x in min_key.x..=max_key.x {
|
||||||
|
for y in min_key.y..=max_key.y {
|
||||||
|
for z in min_key.z..=max_key.z {
|
||||||
|
let key = Vec3::new(x, y, z);
|
||||||
|
if sparse_scene.get_key_arc(key).is_none() {
|
||||||
|
sparse_scene.insert(
|
||||||
|
key,
|
||||||
|
std::sync::Arc::new(Chunk::filled(Cell::empty(), ())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = (rot
|
||||||
|
* Vec3::new(model.size.x, model.size.y, model.size.z).map(|e| e as i32))
|
||||||
|
.map(|e| if e > 0 { 0 } else { -e - 1 });
|
||||||
|
for voxel in &model.voxels {
|
||||||
|
if let Some(&color) = palette.get(voxel.i as usize) {
|
||||||
|
sparse_scene
|
||||||
|
.set(
|
||||||
|
(rot * Vec3::new(voxel.x, voxel.y, voxel.z).map(|e| i32::from(e)))
|
||||||
|
+ offset
|
||||||
|
+ pos,
|
||||||
|
Cell::new(color, false, false),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sparse_scene
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -89,7 +89,7 @@ crossbeam-utils = "0.8.1"
|
|||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
# TODO: remove
|
# TODO: remove
|
||||||
directories-next = "2.0"
|
directories-next = "2.0"
|
||||||
dot_vox = "4.0"
|
dot_vox = { git = "https://github.com/Imberflur/dot_vox.git" }
|
||||||
enum-iterator = "0.6"
|
enum-iterator = "0.6"
|
||||||
futures-executor = "0.3"
|
futures-executor = "0.3"
|
||||||
guillotiere = "0.6"
|
guillotiere = "0.6"
|
||||||
|
@ -174,6 +174,9 @@ impl From<&crate::settings::GamepadSettings> for ControllerSettings {
|
|||||||
map.entry(settings.game_buttons.swap_loadout)
|
map.entry(settings.game_buttons.swap_loadout)
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(GameInput::SwapLoadout);
|
.push(GameInput::SwapLoadout);
|
||||||
|
map.entry(settings.game_buttons.place_vox)
|
||||||
|
.or_default()
|
||||||
|
.push(GameInput::PlaceVox);
|
||||||
map
|
map
|
||||||
},
|
},
|
||||||
menu_button_map: {
|
menu_button_map: {
|
||||||
|
@ -9,9 +9,9 @@ use vek::*;
|
|||||||
|
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::{
|
use common::{
|
||||||
assets::AssetExt,
|
assets::{self, Asset, AssetExt, AssetHandle, DotVoxAsset},
|
||||||
comp,
|
|
||||||
comp::{
|
comp::{
|
||||||
|
self,
|
||||||
inventory::slot::{EquipSlot, Slot},
|
inventory::slot::{EquipSlot, Slot},
|
||||||
invite::InviteKind,
|
invite::InviteKind,
|
||||||
item::{tool::ToolKind, ItemDef, ItemDesc},
|
item::{tool::ToolKind, ItemDef, ItemDesc},
|
||||||
@ -32,6 +32,7 @@ use common_net::{
|
|||||||
msg::{server::InviteAnswer, PresenceKind},
|
msg::{server::InviteAnswer, PresenceKind},
|
||||||
sync::WorldSyncExt,
|
sync::WorldSyncExt,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
audio::sfx::SfxEvent,
|
audio::sfx::SfxEvent,
|
||||||
@ -54,6 +55,57 @@ enum TickAction {
|
|||||||
// Disconnected (i.e. go to main menu)
|
// Disconnected (i.e. go to main menu)
|
||||||
Disconnect,
|
Disconnect,
|
||||||
}
|
}
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct VoxSpec(String, [i32; 3]);
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct PlaceSpec {
|
||||||
|
pieces: Vec<VoxSpec>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Asset for PlaceSpec {
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
|
||||||
|
const EXTENSION: &'static str = "ron";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlaceSpec {
|
||||||
|
// pub fn load_watched() -> std::sync::Arc<Self> {
|
||||||
|
// PlaceSpec::load("place")
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn build_place(&self) -> (common::figure::SparseScene, Vec3<i32>) {
|
||||||
|
// TODO add sparse scene combination
|
||||||
|
//use common::figure::{DynaUnionizer, Segment};
|
||||||
|
fn graceful_load_vox(name: &str) -> AssetHandle<DotVoxAsset> {
|
||||||
|
match DotVoxAsset::load(name) {
|
||||||
|
Ok(dot_vox) => dot_vox,
|
||||||
|
Err(_) => {
|
||||||
|
error!("Could not load vox file for placement: {}", name);
|
||||||
|
DotVoxAsset::load_expect("voxygen.voxel.not_found")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//let mut unionizer = DynaUnionizer::new();
|
||||||
|
//for VoxSpec(specifier, offset) in &self.pieces {
|
||||||
|
// let seg = Segment::from(graceful_load_vox(&specifier,
|
||||||
|
// indicator).as_ref()); unionizer = unionizer.add(seg,
|
||||||
|
// (*offset).into());
|
||||||
|
//}
|
||||||
|
|
||||||
|
//unionizer.unify()
|
||||||
|
let hack = "asset that doesn't exist";
|
||||||
|
(
|
||||||
|
match self.pieces.get(0) {
|
||||||
|
Some(VoxSpec(specifier, _offset)) => {
|
||||||
|
common::figure::SparseScene::from(&graceful_load_vox(&specifier).read().0)
|
||||||
|
},
|
||||||
|
None => common::figure::SparseScene::from(&graceful_load_vox(&hack).read().0),
|
||||||
|
},
|
||||||
|
Vec3::zero(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SessionState {
|
pub struct SessionState {
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
@ -75,6 +127,14 @@ pub struct SessionState {
|
|||||||
interactable: Option<Interactable>,
|
interactable: Option<Interactable>,
|
||||||
saved_zoom_dist: Option<f32>,
|
saved_zoom_dist: Option<f32>,
|
||||||
hitboxes: HashMap<specs::Entity, DebugShapeId>,
|
hitboxes: HashMap<specs::Entity, DebugShapeId>,
|
||||||
|
|
||||||
|
placing_vox: Vec<(
|
||||||
|
Vec3<i32>,
|
||||||
|
Arc<common::volumes::chunk::Chunk<common::figure::Cell, common::figure::SscSize, ()>>,
|
||||||
|
)>,
|
||||||
|
last_sent: Option<(Vec3<i32>, Block)>,
|
||||||
|
vox: common::figure::SparseScene,
|
||||||
|
placepos: Vec3<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an active game session (i.e., the one being played).
|
/// Represents an active game session (i.e., the one being played).
|
||||||
@ -123,6 +183,13 @@ impl SessionState {
|
|||||||
interactable: None,
|
interactable: None,
|
||||||
saved_zoom_dist: None,
|
saved_zoom_dist: None,
|
||||||
hitboxes: HashMap::new(),
|
hitboxes: HashMap::new(),
|
||||||
|
|
||||||
|
placing_vox: Vec::new(),
|
||||||
|
last_sent: None,
|
||||||
|
// let mut place_indicator = PlaceSpec::load_expect("place");
|
||||||
|
// TODO: use offset
|
||||||
|
vox: PlaceSpec::load_expect("place").read().build_place().0,
|
||||||
|
placepos: Vec3::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,6 +335,13 @@ impl SessionState {
|
|||||||
|
|
||||||
impl PlayState for SessionState {
|
impl PlayState for SessionState {
|
||||||
fn enter(&mut self, global_state: &mut GlobalState, _: Direction) {
|
fn enter(&mut self, global_state: &mut GlobalState, _: Direction) {
|
||||||
|
// Clear vox spawining things
|
||||||
|
self.placing_vox = Vec::new();
|
||||||
|
self.last_sent = None;
|
||||||
|
// TODO: use offset
|
||||||
|
self.vox = PlaceSpec::load_expect("place").read().build_place().0;
|
||||||
|
self.placepos = Vec3::zero();
|
||||||
|
|
||||||
// Trap the cursor.
|
// Trap the cursor.
|
||||||
global_state.window.grab_cursor(true);
|
global_state.window.grab_cursor(true);
|
||||||
|
|
||||||
@ -290,6 +364,7 @@ impl PlayState for SessionState {
|
|||||||
let client = self.client.borrow();
|
let client = self.client.borrow();
|
||||||
(client.presence(), client.registered())
|
(client.presence(), client.registered())
|
||||||
};
|
};
|
||||||
|
|
||||||
if client_presence.is_some() {
|
if client_presence.is_some() {
|
||||||
let camera = self.scene.camera_mut();
|
let camera = self.scene.camera_mut();
|
||||||
|
|
||||||
@ -766,6 +841,24 @@ impl PlayState for SessionState {
|
|||||||
client.decline_invite();
|
client.decline_invite();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
GameInput::PlaceVox if state => {
|
||||||
|
// start placing
|
||||||
|
if let Some(build_pos) = build_pos.filter(|_| can_build) {
|
||||||
|
self.placepos = build_pos.map(|e| e.floor() as i32);
|
||||||
|
// reload in case vox was changed
|
||||||
|
self.vox =
|
||||||
|
PlaceSpec::load_expect("place").read().build_place().0;
|
||||||
|
self.placing_vox = self
|
||||||
|
.vox
|
||||||
|
.iter()
|
||||||
|
.map(|(key, chunk)| (key, Arc::clone(chunk)))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.collect();
|
||||||
|
self.last_sent = None;
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -958,6 +1051,53 @@ impl PlayState for SessionState {
|
|||||||
.camera_mut()
|
.camera_mut()
|
||||||
.compute_dependents(&*self.client.borrow().state().terrain());
|
.compute_dependents(&*self.client.borrow().state().terrain());
|
||||||
|
|
||||||
|
// Only send next portion when the previous subchunk finishes sending
|
||||||
|
if let Some((world_pos, block)) = self.last_sent.as_ref().copied() {
|
||||||
|
if self
|
||||||
|
.client
|
||||||
|
.borrow()
|
||||||
|
.state()
|
||||||
|
.terrain()
|
||||||
|
.get(world_pos)
|
||||||
|
.map(|b| *b == block)
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
|
self.last_sent = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.last_sent.is_none() {
|
||||||
|
if let Some((key, chunk)) = self.placing_vox.pop() {
|
||||||
|
use common::vol::IntoFullVolIterator;
|
||||||
|
|
||||||
|
for (pos, cell) in chunk.full_vol_iter() {
|
||||||
|
let world_pos = self.vox.key_pos(key) + pos + self.placepos;
|
||||||
|
match cell.get_color() {
|
||||||
|
Some(color) => {
|
||||||
|
let block = Block::new(BlockKind::Misc, color);
|
||||||
|
self.client.borrow_mut().place_block(world_pos, block);
|
||||||
|
self.last_sent = Some((world_pos, block))
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// Comment out this section to not carve out the empty space
|
||||||
|
let mut client = self.client.borrow_mut();
|
||||||
|
// Only remove the block if there is something there
|
||||||
|
if client
|
||||||
|
.state()
|
||||||
|
.terrain()
|
||||||
|
.get(world_pos)
|
||||||
|
.map(|block| !block.is_fluid())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
client.remove_block(world_pos);
|
||||||
|
self.last_sent = Some((world_pos, Block::empty()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate debug info, if needed (it iterates through enough data that we might
|
// Generate debug info, if needed (it iterates through enough data that we might
|
||||||
// as well avoid it unless we need it).
|
// as well avoid it unless we need it).
|
||||||
let debug_info = global_state
|
let debug_info = global_state
|
||||||
|
@ -170,6 +170,7 @@ impl ControlSettings {
|
|||||||
GameInput::DeclineGroupInvite => KeyMouse::Key(VirtualKeyCode::N),
|
GameInput::DeclineGroupInvite => KeyMouse::Key(VirtualKeyCode::N),
|
||||||
GameInput::MapZoomIn => KeyMouse::Key(VirtualKeyCode::Plus),
|
GameInput::MapZoomIn => KeyMouse::Key(VirtualKeyCode::Plus),
|
||||||
GameInput::MapZoomOut => KeyMouse::Key(VirtualKeyCode::Minus),
|
GameInput::MapZoomOut => KeyMouse::Key(VirtualKeyCode::Minus),
|
||||||
|
GameInput::PlaceVox => KeyMouse::Key(VirtualKeyCode::F7),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,7 @@ pub mod con_settings {
|
|||||||
pub interact: Button,
|
pub interact: Button,
|
||||||
pub toggle_wield: Button,
|
pub toggle_wield: Button,
|
||||||
pub swap_loadout: Button,
|
pub swap_loadout: Button,
|
||||||
|
pub place_vox: Button,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
@ -175,6 +176,7 @@ pub mod con_settings {
|
|||||||
interact: Button::Simple(GilButton::North),
|
interact: Button::Simple(GilButton::North),
|
||||||
toggle_wield: Button::Simple(GilButton::West),
|
toggle_wield: Button::Simple(GilButton::West),
|
||||||
swap_loadout: Button::Simple(GilButton::LeftThumb),
|
swap_loadout: Button::Simple(GilButton::LeftThumb),
|
||||||
|
place_vox: Button::Simple(GilButton::Unknown),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ pub enum GameInput {
|
|||||||
DeclineGroupInvite,
|
DeclineGroupInvite,
|
||||||
MapZoomIn,
|
MapZoomIn,
|
||||||
MapZoomOut,
|
MapZoomOut,
|
||||||
|
PlaceVox,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameInput {
|
impl GameInput {
|
||||||
@ -145,6 +146,7 @@ impl GameInput {
|
|||||||
GameInput::DeclineGroupInvite => "gameinput.declinegroupinvite",
|
GameInput::DeclineGroupInvite => "gameinput.declinegroupinvite",
|
||||||
GameInput::MapZoomIn => "gameinput.mapzoomin",
|
GameInput::MapZoomIn => "gameinput.mapzoomin",
|
||||||
GameInput::MapZoomOut => "gameinput.mapzoomout",
|
GameInput::MapZoomOut => "gameinput.mapzoomout",
|
||||||
|
GameInput::PlaceVox => "gameinput.placevox",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +213,7 @@ impl GameInput {
|
|||||||
GameInput::DeclineGroupInvite,
|
GameInput::DeclineGroupInvite,
|
||||||
GameInput::MapZoomIn,
|
GameInput::MapZoomIn,
|
||||||
GameInput::MapZoomOut,
|
GameInput::MapZoomOut,
|
||||||
|
GameInput::PlaceVox,
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
|
Loading…
Reference in New Issue
Block a user