mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-31 20:32:34 +00:00
feat(command): implement Vec3 argument type (#1943)
* added `Vector3` dot and cross methods and added both coordinate types * added parsing for coordinates and created `vec3` file * finished `vec3` argument type and implemented parsing for local coordinates
This commit is contained in:
@@ -90,7 +90,7 @@ impl<T: Copy> Vector3<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Math + PartialOrd + Copy> Vector3<T> {
|
||||
impl<T> Vector3<T> {
|
||||
/// Creates a new `Vector3` with the given components.
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -104,7 +104,9 @@ impl<T: Math + PartialOrd + Copy> Vector3<T> {
|
||||
pub const fn new(x: T, y: T, z: T) -> Self {
|
||||
Self { x, y, z }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Math + PartialOrd + Copy> Vector3<T> {
|
||||
/// Calculates the squared length (magnitude) of the vector.
|
||||
///
|
||||
/// # Returns
|
||||
@@ -343,6 +345,40 @@ impl<T: Math + PartialOrd + Copy> Vector3<T> {
|
||||
&& self.z >= min_z
|
||||
&& self.z <= max_z
|
||||
}
|
||||
|
||||
/// Computes the dot product of this vector with the given coordinates.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `x` – The X coordinate to dot with.
|
||||
/// - `y` – The Y coordinate to dot with.
|
||||
/// - `z` – The Z coordinate to dot with.
|
||||
///
|
||||
/// # Returns
|
||||
/// The dot product `self.x * x + self.y * y + self.z * z`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn dot(&self, other: &Self) -> T {
|
||||
self.x * other.x + self.y * other.y + self.z * other.z
|
||||
}
|
||||
|
||||
/// Computes the cross product of this vector with the given coordinates.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `x` – The X coordinate to cross with.
|
||||
/// - `y` – The Y coordinate to cross with.
|
||||
/// - `z` – The Z coordinate to cross with.
|
||||
///
|
||||
/// # Returns
|
||||
/// The cross product of both vectors.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cross(&self, other: &Self) -> Self {
|
||||
Self::new(
|
||||
self.y * other.z - self.z * other.y,
|
||||
self.z * other.x - self.x * other.z,
|
||||
self.x * other.y - self.y * other.x,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Math + Copy + Float> Vector3<T> {
|
||||
|
||||
337
pumpkin/src/command/argument_types/coordinates/mod.rs
Normal file
337
pumpkin/src/command/argument_types/coordinates/mod.rs
Normal file
@@ -0,0 +1,337 @@
|
||||
use crate::command::context::command_source::CommandSource;
|
||||
use crate::command::errors::command_syntax_error::CommandSyntaxError;
|
||||
use crate::command::errors::error_types::{
|
||||
CommandErrorType, READER_EXPECTED_DOUBLE, READER_EXPECTED_INT,
|
||||
};
|
||||
use crate::command::string_reader::StringReader;
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use pumpkin_util::math::vector3::{Axis, Vector3};
|
||||
|
||||
pub mod vec3;
|
||||
|
||||
pub const MIXED_TYPE_ERROR_TYPE: CommandErrorType<0> =
|
||||
CommandErrorType::new(translation::ARGUMENT_POS_MIXED);
|
||||
|
||||
/// Represents a single world coordinate.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum WorldCoordinate {
|
||||
Absolute(f64),
|
||||
Relative(f64),
|
||||
}
|
||||
|
||||
impl WorldCoordinate {
|
||||
/// Creates a new `WorldCoordinate`.
|
||||
#[must_use]
|
||||
pub const fn new(is_relative: bool, value: f64) -> Self {
|
||||
if is_relative {
|
||||
Self::Relative(value)
|
||||
} else {
|
||||
Self::Absolute(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether this coordinate is relative.
|
||||
#[must_use]
|
||||
pub const fn is_relative(&self) -> bool {
|
||||
matches!(self, Self::Relative(_))
|
||||
}
|
||||
|
||||
/// Returns the physical coordinate value this [`WorldCoordinate`] represents, given
|
||||
/// an absolute coordinate origin.
|
||||
#[must_use]
|
||||
pub const fn resolve(&self, origin: f64) -> f64 {
|
||||
match self {
|
||||
Self::Absolute(absolute) => *absolute,
|
||||
Self::Relative(relative) => origin + *relative,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a `StringReader` is about to describle a relative coordinate.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `reader` - The `StringReader` to check.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `true` if a `~` (tilde) can be found. It is also skipped by this method.
|
||||
/// - `false` if no `~` can be found.
|
||||
pub fn consume_relative_start(reader: &mut StringReader) -> bool {
|
||||
if reader.peek() == Some('~') {
|
||||
reader.skip();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to parse a [`WorldCoordinate`] from a single number.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `reader` - The `StringReader` to parse the coordinate from.
|
||||
/// * `center_integers` - Whether to correct integral coordinates by adding `+0.5` to them
|
||||
/// (as mentioned by [`Vec3ArgumentType::Default`]).
|
||||
///
|
||||
/// # Returns
|
||||
/// - The `WorldCoordinate` if it was correctly parsed, wrapped in an `Ok`.
|
||||
/// - A [`CommandSyntaxError`] describing an error if it could not be correctly parsed,
|
||||
/// wrapped in an `Err`.
|
||||
///
|
||||
/// [`Vec3ArgumentType::Default`]: Vec3ArgumentType::Default
|
||||
pub fn parse(
|
||||
reader: &mut StringReader,
|
||||
center_integers: bool,
|
||||
) -> Result<Self, CommandSyntaxError> {
|
||||
if reader.peek() == Some('^') {
|
||||
Err(MIXED_TYPE_ERROR_TYPE.create(reader))
|
||||
} else if !reader.can_read_char() {
|
||||
Err(READER_EXPECTED_DOUBLE.create(reader))
|
||||
} else {
|
||||
let is_relative = Self::consume_relative_start(reader);
|
||||
let i = reader.cursor();
|
||||
let mut value = if reader.can_read_char() && reader.peek() != Some(' ') {
|
||||
reader.read_double()?
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let slice = &reader.string()[i..reader.cursor()];
|
||||
if is_relative && slice.is_empty() {
|
||||
Ok(Self::Relative(0.0))
|
||||
} else {
|
||||
if !slice.contains('.') && !is_relative && center_integers {
|
||||
value += 0.5;
|
||||
}
|
||||
Ok(Self::new(is_relative, value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to parse a [`WorldCoordinate`] from a single number, expecting an integral non-relative coordinate
|
||||
/// or any relative coordinate.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `reader` - The `StringReader` to parse the coordinate from.
|
||||
///
|
||||
/// # Returns
|
||||
/// - The `WorldCoordinate` if it was correctly parsed, wrapped in an `Ok`.
|
||||
/// - A [`CommandSyntaxError`] describing an error if it could not be correctly parsed,
|
||||
/// wrapped in an `Err`.
|
||||
pub fn parse_integer(reader: &mut StringReader) -> Result<Self, CommandSyntaxError> {
|
||||
if reader.peek() == Some('^') {
|
||||
Err(MIXED_TYPE_ERROR_TYPE.create(reader))
|
||||
} else if !reader.can_read_char() {
|
||||
Err(READER_EXPECTED_INT.create(reader))
|
||||
} else {
|
||||
let is_relative = Self::consume_relative_start(reader);
|
||||
let value = if reader.can_read_char() && reader.peek() != Some(' ') {
|
||||
if is_relative {
|
||||
reader.read_double()?
|
||||
} else {
|
||||
reader.read_int()? as f64
|
||||
}
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
Ok(Self::new(is_relative, value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An object representing some command coordinates.
|
||||
///
|
||||
/// A set of [`Coordinates`] can be *resolved* via the [`Coordinates::resolve`] method.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Coordinates {
|
||||
/// Normal coordinates (each coordinate can be *absolute* or *relative*.)
|
||||
World(Vector3<WorldCoordinate>),
|
||||
/// Local coordinates (can be different depending on the command source.)
|
||||
Local { left: f64, up: f64, forward: f64 },
|
||||
}
|
||||
|
||||
macro_rules! check_for_space_char {
|
||||
($reader:ident, $i:ident, $value:ident) => {
|
||||
if $reader.peek() == Some(' ') {
|
||||
$reader.skip();
|
||||
Ok($value)
|
||||
} else {
|
||||
$reader.set_cursor($i);
|
||||
Err(vec3::INCOMPLETE_ERROR_TYPE.create($reader))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Coordinates {
|
||||
/// Returns whether a coordinate (of the given [`Axis`]) of these [`Coordinates`] is relative.
|
||||
///
|
||||
/// This also returns `true` for a *local coordinate*.
|
||||
#[must_use]
|
||||
pub const fn is_relative(&self, axis: Axis) -> bool {
|
||||
match self {
|
||||
Self::World(vector) => vector.get_axis(axis).is_relative(),
|
||||
Self::Local { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the physical position that these [`Coordinates`] represent.
|
||||
#[must_use]
|
||||
pub fn resolve(&self, source: &CommandSource) -> Vector3<f64> {
|
||||
match self {
|
||||
Self::World(vector) => {
|
||||
let pos = source.position;
|
||||
Vector3::new(
|
||||
vector.x.resolve(pos.x),
|
||||
vector.y.resolve(pos.y),
|
||||
vector.z.resolve(pos.z),
|
||||
)
|
||||
}
|
||||
Self::Local { left, up, forward } => {
|
||||
convert_local_coordinates(*left, *up, *forward, source.rotation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to parse a set of world [`Coordinates`], expecting coordinates, each either being
|
||||
/// an integral non-relative coordinate or any relative coordinate.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `reader` - The `StringReader` to parse the coordinates from.
|
||||
/// * `center_integers` - Whether to correct integral coordinates by adding `+0.5` to them
|
||||
/// (as mentioned by [`Vec3ArgumentType::Default`]).
|
||||
///
|
||||
/// # Returns
|
||||
/// - The world `Coordinates` if they were correctly parsed, wrapped in an `Ok`.
|
||||
/// - A [`CommandSyntaxError`] describing an error if they could not be correctly parsed,
|
||||
/// wrapped in an `Err`.
|
||||
///
|
||||
/// [`Vec3ArgumentType::Default`]: Vec3ArgumentType::Default
|
||||
pub fn parse_world(
|
||||
reader: &mut StringReader,
|
||||
center_integers: bool,
|
||||
) -> Result<Self, CommandSyntaxError> {
|
||||
let i = reader.cursor();
|
||||
let coordinate_1 = Self::parse_world_single(i, reader, center_integers)?;
|
||||
// The Y coordinate is never centered.
|
||||
let coordinate_2 = Self::parse_world_single(i, reader, false)?;
|
||||
let coordinate_3 = WorldCoordinate::parse(reader, center_integers)?;
|
||||
Ok(Self::World(Vector3::new(
|
||||
coordinate_1,
|
||||
coordinate_2,
|
||||
coordinate_3,
|
||||
)))
|
||||
}
|
||||
|
||||
fn parse_world_single(
|
||||
i: usize,
|
||||
reader: &mut StringReader,
|
||||
center_integers: bool,
|
||||
) -> Result<WorldCoordinate, CommandSyntaxError> {
|
||||
let coordinate = WorldCoordinate::parse(reader, center_integers)?;
|
||||
check_for_space_char!(reader, i, coordinate)
|
||||
}
|
||||
|
||||
/// Tries to parse a set of world [`Coordinates`].
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `reader` - The `StringReader` to parse the coordinate from.
|
||||
///
|
||||
/// # Returns
|
||||
/// - The world `Coordinates` if they were correctly parsed, wrapped in an `Ok`.
|
||||
/// - A [`CommandSyntaxError`] describing an error if they could not be correctly parsed,
|
||||
/// wrapped in an `Err`.
|
||||
pub fn parse_world_integers(reader: &mut StringReader) -> Result<Self, CommandSyntaxError> {
|
||||
let i = reader.cursor();
|
||||
let coordinate_1 = Self::parse_world_single_integer(i, reader)?;
|
||||
let coordinate_2 = Self::parse_world_single_integer(i, reader)?;
|
||||
let coordinate_3 = WorldCoordinate::parse_integer(reader)?;
|
||||
Ok(Self::World(Vector3::new(
|
||||
coordinate_1,
|
||||
coordinate_2,
|
||||
coordinate_3,
|
||||
)))
|
||||
}
|
||||
|
||||
fn parse_world_single_integer(
|
||||
i: usize,
|
||||
reader: &mut StringReader,
|
||||
) -> Result<WorldCoordinate, CommandSyntaxError> {
|
||||
let coordinate = WorldCoordinate::parse_integer(reader)?;
|
||||
check_for_space_char!(reader, i, coordinate)
|
||||
}
|
||||
|
||||
/// Tries to parse a set of local [`Coordinates`].
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `reader` - The `StringReader` to parse the coordinate from.
|
||||
///
|
||||
/// # Returns
|
||||
/// - The local `Coordinates` if they were correctly parsed, wrapped in an `Ok`.
|
||||
/// - A [`CommandSyntaxError`] describing an error if they could not be correctly parsed,
|
||||
/// wrapped in an `Err`.
|
||||
pub fn parse_local(reader: &mut StringReader) -> Result<Self, CommandSyntaxError> {
|
||||
let i = reader.cursor();
|
||||
let left = Self::parse_local_single(i, reader)?;
|
||||
let up = Self::parse_local_single(i, reader)?;
|
||||
let forward = Self::parse_local_number(i, reader)?;
|
||||
Ok(Self::Local { left, up, forward })
|
||||
}
|
||||
|
||||
fn parse_local_single(i: usize, reader: &mut StringReader) -> Result<f64, CommandSyntaxError> {
|
||||
let number = Self::parse_local_number(i, reader)?;
|
||||
check_for_space_char!(reader, i, number)
|
||||
}
|
||||
|
||||
fn parse_local_number(i: usize, reader: &mut StringReader) -> Result<f64, CommandSyntaxError> {
|
||||
if !reader.can_read_char() {
|
||||
Err(READER_EXPECTED_DOUBLE.create(reader))
|
||||
} else if reader.peek() != Some('^') {
|
||||
reader.set_cursor(i);
|
||||
Err(MIXED_TYPE_ERROR_TYPE.create(reader))
|
||||
} else {
|
||||
reader.skip();
|
||||
let number = if reader.can_read_char() && reader.peek() != Some(' ') {
|
||||
reader.read_double()?
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
Ok(number)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a set of local coordinates to their physical [`Vector3`] form.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `left` - The left component of the coordinates.
|
||||
/// * `up` - The up component of the coordinates.
|
||||
/// * `forward` - The forward component of the coordinates.
|
||||
/// * `rotation` - The rotation to use to calculate the physical coordinates.
|
||||
/// Both coordinates must be in *degrees*.
|
||||
///
|
||||
/// # Returns
|
||||
/// The physical position represented by the local coordinates.
|
||||
#[must_use]
|
||||
fn convert_local_coordinates(
|
||||
left: f64,
|
||||
up: f64,
|
||||
forward: f64,
|
||||
rotation: Vector2<f32>,
|
||||
) -> Vector3<f64> {
|
||||
let y = (rotation.y + 90.0).to_radians() as f64;
|
||||
let y_cos = y.cos();
|
||||
let y_sin = y.sin();
|
||||
let x = (-rotation.x).to_radians() as f64;
|
||||
let x_cos = x.cos();
|
||||
let x_sin = x.sin();
|
||||
let x_up = (-rotation.x + 90.0).to_radians() as f64;
|
||||
let x_up_cos = x_up.cos();
|
||||
let x_up_sin = x_up.sin();
|
||||
|
||||
let forward_vector = Vector3::new(y_cos * x_cos, x_sin, y_sin * x_cos);
|
||||
let up_vector = Vector3::new(y_cos * x_up_cos, x_up_sin, y_sin * x_up_cos);
|
||||
let left_vector = forward_vector.cross(&up_vector) * -1.0;
|
||||
|
||||
Vector3::new(
|
||||
forward_vector.x * forward + up_vector.x * up + left_vector.x * left,
|
||||
forward_vector.y * forward + up_vector.y * up + left_vector.y * left,
|
||||
forward_vector.z * forward + up_vector.z * up + left_vector.z * left,
|
||||
)
|
||||
}
|
||||
162
pumpkin/src/command/argument_types/coordinates/vec3.rs
Normal file
162
pumpkin/src/command/argument_types/coordinates/vec3.rs
Normal file
@@ -0,0 +1,162 @@
|
||||
use crate::command::argument_types::argument_type::{ArgumentType, JavaClientArgumentType};
|
||||
use crate::command::argument_types::coordinates::Coordinates;
|
||||
use crate::command::errors::command_syntax_error::CommandSyntaxError;
|
||||
use crate::command::errors::error_types::CommandErrorType;
|
||||
use crate::command::string_reader::StringReader;
|
||||
use pumpkin_data::translation;
|
||||
|
||||
pub const INCOMPLETE_ERROR_TYPE: CommandErrorType<0> =
|
||||
CommandErrorType::new(translation::ARGUMENT_POS3D_INCOMPLETE);
|
||||
|
||||
/// An argument type for a 3-dimensional vector.
|
||||
pub enum Vec3ArgumentType {
|
||||
/// The default `Vec3ArgumentType` variant.
|
||||
///
|
||||
/// To represent some position in the world,
|
||||
/// you'll almost always want to use this.
|
||||
///
|
||||
/// For each coordinate, if it does not use the decimal (`.`) sign
|
||||
/// (the coordinate is integral) and it is not relative,
|
||||
/// a `+0.5` offset is added to it.
|
||||
///
|
||||
Default,
|
||||
/// No center correction occurs for this `Vec3ArgumentType` variant.
|
||||
Uncorrected,
|
||||
}
|
||||
|
||||
impl Vec3ArgumentType {
|
||||
/// Returns whether this argument type centers integers.\
|
||||
#[must_use]
|
||||
pub const fn centers_integers(&self) -> bool {
|
||||
matches!(self, Self::Default)
|
||||
}
|
||||
}
|
||||
|
||||
impl ArgumentType for Vec3ArgumentType {
|
||||
type Item = Coordinates;
|
||||
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Self::Item, CommandSyntaxError> {
|
||||
if reader.peek() == Some('^') {
|
||||
Coordinates::parse_local(reader)
|
||||
} else {
|
||||
Coordinates::parse_world(reader, self.centers_integers())
|
||||
}
|
||||
}
|
||||
|
||||
fn client_side_parser(&'_ self) -> JavaClientArgumentType<'_> {
|
||||
JavaClientArgumentType::Vec3
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<String> {
|
||||
examples!("1 1 1", "3 ~34 ~-2", "40 50 60", "^ ^4 ^3")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::command::argument_types::argument_type::ArgumentType;
|
||||
use crate::command::argument_types::coordinates::vec3::{
|
||||
INCOMPLETE_ERROR_TYPE, Vec3ArgumentType,
|
||||
};
|
||||
use crate::command::argument_types::coordinates::{
|
||||
Coordinates, MIXED_TYPE_ERROR_TYPE, WorldCoordinate,
|
||||
};
|
||||
use crate::command::string_reader::StringReader;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
macro_rules! world_coordinate {
|
||||
($( ($variant:ident, $value:expr) ),+) => {
|
||||
Coordinates::World(Vector3::new(
|
||||
$( WorldCoordinate::$variant($value), )+
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_test() {
|
||||
let mut reader = StringReader::new("0 0 0");
|
||||
|
||||
// The default type centers both the X and Z coordinates.
|
||||
assert_parse_ok_reset!(
|
||||
reader,
|
||||
Vec3ArgumentType::Default,
|
||||
world_coordinate!((Absolute, 0.5), (Absolute, 0.0), (Absolute, 0.5))
|
||||
);
|
||||
assert_parse_ok_reset!(
|
||||
reader,
|
||||
Vec3ArgumentType::Uncorrected,
|
||||
world_coordinate!((Absolute, 0.0), (Absolute, 0.0), (Absolute, 0.0))
|
||||
);
|
||||
|
||||
let mut reader = StringReader::new("~ ~4 8");
|
||||
|
||||
assert_parse_ok_reset!(
|
||||
reader,
|
||||
Vec3ArgumentType::Default,
|
||||
world_coordinate!(
|
||||
(Relative, 0.0),
|
||||
(Relative, 4.0),
|
||||
// Only the Z coordinate is centered.
|
||||
(Absolute, 8.5)
|
||||
)
|
||||
);
|
||||
assert_parse_ok_reset!(
|
||||
reader,
|
||||
Vec3ArgumentType::Uncorrected,
|
||||
world_coordinate!((Relative, 0.0), (Relative, 4.0), (Absolute, 8.0))
|
||||
);
|
||||
|
||||
let mut reader = StringReader::new("~-1 ~-2.5 ~-3");
|
||||
|
||||
assert_parse_ok_reset!(
|
||||
reader,
|
||||
Vec3ArgumentType::Default,
|
||||
world_coordinate!((Relative, -1.0), (Relative, -2.5), (Relative, -3.0))
|
||||
);
|
||||
assert_parse_ok_reset!(
|
||||
reader,
|
||||
Vec3ArgumentType::Uncorrected,
|
||||
world_coordinate!((Relative, -1.0), (Relative, -2.5), (Relative, -3.0))
|
||||
);
|
||||
|
||||
let mut reader = StringReader::new("-3 4 5");
|
||||
|
||||
assert_parse_ok_reset!(
|
||||
reader,
|
||||
Vec3ArgumentType::Default,
|
||||
world_coordinate!((Absolute, -2.5), (Absolute, 4.0), (Absolute, 5.5))
|
||||
);
|
||||
|
||||
let mut reader = StringReader::new("1000 2000");
|
||||
|
||||
assert_parse_err_reset!(reader, Vec3ArgumentType::Default, &INCOMPLETE_ERROR_TYPE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_local_coordinates() {
|
||||
let mut reader = StringReader::new("^1 ^10 ^30");
|
||||
|
||||
assert_parse_ok_reset!(
|
||||
reader,
|
||||
Vec3ArgumentType::Default,
|
||||
Coordinates::Local {
|
||||
left: 1.0,
|
||||
up: 10.0,
|
||||
forward: 30.0
|
||||
}
|
||||
);
|
||||
|
||||
// We can't mix world & local coordinates.
|
||||
let mut reader = StringReader::new("^1 ^10 ~20");
|
||||
|
||||
assert_parse_err_reset!(reader, Vec3ArgumentType::Default, &MIXED_TYPE_ERROR_TYPE);
|
||||
|
||||
let mut reader = StringReader::new("^-3 ^-7 10");
|
||||
|
||||
assert_parse_err_reset!(reader, Vec3ArgumentType::Default, &MIXED_TYPE_ERROR_TYPE);
|
||||
|
||||
let mut reader = StringReader::new("^1 ^2");
|
||||
|
||||
assert_parse_err_reset!(reader, Vec3ArgumentType::Default, &INCOMPLETE_ERROR_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,7 @@ impl FromStringReader for FloatDegreeBounds {
|
||||
}
|
||||
|
||||
pub mod argument_type;
|
||||
pub mod coordinates;
|
||||
pub mod core;
|
||||
pub mod entity;
|
||||
pub mod entity_selector;
|
||||
|
||||
Reference in New Issue
Block a user