replaceininv
This commit is contained in:
parent
dc2f3d7c05
commit
edd54dfa7f
80
Manual.md
80
Manual.md
@ -164,6 +164,28 @@ generated in the destination map, or the entire selection if an offset is used.
|
||||
**Tip:** Overlay will be significantly faster if no offset is used, as
|
||||
mapblocks can be copied verbatim.
|
||||
|
||||
### replaceininv
|
||||
|
||||
Usage: `replaceininv [--deletemeta] [--node <node>] [--p1 x y z] [--p2 x y z] [--invert] <item> <new_item>`
|
||||
|
||||
Replace a certain item with another in node inventories. To delete items
|
||||
instead of replacing them, use "Empty" (with a capital E) for `replacename`.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `item`: Name of item to replace
|
||||
- `new_item`: Name of new item to replace with
|
||||
- `--deletemeta`: Delete metadata of replaced items. If not specified, any item
|
||||
metadata will remain unchanged.
|
||||
- `--node`: Name of node to to replace in. If not specified, the item will be
|
||||
replaced in all node inventories.
|
||||
- `--p1, --p2`: Area in which to search for nodes. If not specified, items will
|
||||
be replaced across the entire map.
|
||||
- `--invert`: Only search for nodes *outside* the given area.
|
||||
|
||||
**Tip:** To only delete metadata without replacing the nodes, use the
|
||||
`--deletemeta` flag, and make `new_item` the same as `item`.
|
||||
|
||||
### replacenodes
|
||||
|
||||
Usage: `replacenodes [--p1 x y z] [--p2 x y z] [--invert] <node> <new_node>`
|
||||
@ -181,6 +203,22 @@ Arguments:
|
||||
will be replaced across the entire map.
|
||||
- `--invert`: Only replace nodes *outside* the given area.
|
||||
|
||||
### setmetavar
|
||||
|
||||
Usage: `setmetavar [--node <node>] [--p1 x y z] [--p2 x y z] [--invert] <key> <value>`
|
||||
|
||||
Set a variable in node metadata. This only works on metadata where the variable
|
||||
is already set.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `key`: Name of variable to set, e.g. `infotext`, `formspec`, etc.
|
||||
- `value`: Value to set variable to. This should be a string.
|
||||
- `--node`: Name of node to modify. If not specified, the variable will be
|
||||
set for all nodes that have it.
|
||||
- `--p1, --p2`: Area in which to modify nodes.
|
||||
- `--invert`: Only modify nodes *outside* the given area.
|
||||
|
||||
### setparam2
|
||||
|
||||
Usage: `setparam2 [--node <node>] [--p1 x y z] [--p2 x y z] [--invert] <param2_val>`
|
||||
@ -192,8 +230,7 @@ Arguments:
|
||||
- `param2_val`: Param2 value to set, between 0 and 255.
|
||||
- `--node`: Name of node to modify. If not specified, the param2 values of
|
||||
all nodes will be set.
|
||||
- `--p1, --p2`: Area in which to set param2. Required if `--node` is
|
||||
not specified.
|
||||
- `--p1, --p2`: Area in which to set param2.
|
||||
- `--invert`: Only set param2 *outside* the given area.
|
||||
|
||||
### vacuum
|
||||
@ -210,42 +247,3 @@ or deleted.
|
||||
**Note:** Because data is copied into another file, vacuum could require
|
||||
as much free disk space as is already occupied by the map. For example, if
|
||||
map.sqlite is 10 GB, make sure you have **at least 10 GB** of free space!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Danger Zone!
|
||||
|
||||
### `setmetavar`
|
||||
|
||||
**Usage:** `setmetavar [--searchnode <searchnode>] [--p1 x y z] [--p2 x y z] [--invert] <metakey> <metavalue>`
|
||||
|
||||
Set a variable in node metadata. This only works on metadata where the variable is already set.
|
||||
|
||||
Arguments:
|
||||
|
||||
- **`metakey`**: Name of variable to set, e.g. `infotext`, `formspec`, etc.
|
||||
- **`metavalue`**: Value to set variable to. This should be a string.
|
||||
- **`--searchnode`**: Name of node to search for. If not specified, the variable will be set for all nodes that have it.
|
||||
- **`--p1, --p2`**: Area in which to search. Required if `searchnode` is not specified.
|
||||
- **`--invert`**: Only search for nodes *outside* the given area.
|
||||
|
||||
### `replaceininv`
|
||||
|
||||
**Usage:** ` replaceininv [--deletemeta] [--searchnode <searchnode>] [--p1 x y z] [--p2 x y z] [--invert] <searchitem> <replaceitem>`
|
||||
|
||||
Replace a certain item with another in node inventories.
|
||||
To delete items instead of replacing them, use "Empty" (with a capital E) for `replacename`.
|
||||
|
||||
Arguments:
|
||||
|
||||
- **`searchitem`**: Item to search for in node inventories.
|
||||
- **`replaceitem`**: Item to replace with in node inventories.
|
||||
- **`--deletemeta`**: Delete metadata of replaced items. If not specified, any item metadata will remain unchanged.
|
||||
- **`--searchnode`**: Name of node to to replace in. If not specified, the item will be replaced in all node inventories.
|
||||
- **`--p1, --p2`**: Area in which to search for nodes. If not specified, items will be replaced across the entire map.
|
||||
- **`--invert`**: Only search for nodes *outside* the given area.
|
||||
|
||||
**Tip:** To only delete metadata without replacing the nodes, use the `--deletemeta` flag, and make `replaceitem` the same as `searchitem`.
|
||||
|
@ -87,6 +87,16 @@ fn to_cmd_line_args<'a>(tup: &(ArgType, &'a str))
|
||||
a
|
||||
}
|
||||
},
|
||||
ArgType::Item =>
|
||||
Arg::with_name("item")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help(help),
|
||||
ArgType::NewItem =>
|
||||
Arg::with_name("new_item")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help(help),
|
||||
ArgType::Param2Val(_) =>
|
||||
Arg::with_name("param2_val")
|
||||
.required(true)
|
||||
@ -102,7 +112,17 @@ fn to_cmd_line_args<'a>(tup: &(ArgType, &'a str))
|
||||
.long("items")
|
||||
.min_values(0)
|
||||
.max_values(1)
|
||||
.help(help)
|
||||
.help(help),
|
||||
ArgType::Key =>
|
||||
Arg::with_name("key")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help(help),
|
||||
ArgType::Value =>
|
||||
Arg::with_name("value")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help(help),
|
||||
}]
|
||||
}
|
||||
|
||||
@ -157,11 +177,15 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
|
||||
.context("Invalid offset value")?,
|
||||
node: sub_matches.value_of("node").map(str::to_string),
|
||||
new_node: sub_matches.value_of("new_node").map(str::to_string),
|
||||
item: sub_matches.value_of("item").map(str::to_string),
|
||||
new_item: sub_matches.value_of("new_item").map(str::to_string),
|
||||
param2_val: sub_matches.value_of("param2_val")
|
||||
.map(|v| v.parse().unwrap()),
|
||||
object: sub_matches.value_of("object").map(str::to_string),
|
||||
items: sub_matches.values_of("items")
|
||||
.map(|v| v.map(str::to_string).collect()),
|
||||
key: sub_matches.value_of("key").map(str::to_string),
|
||||
value: sub_matches.value_of("value").map(str::to_string),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,14 @@ use crate::instance::{ArgType, InstArgs, InstBundle};
|
||||
|
||||
mod clone;
|
||||
mod delete_blocks;
|
||||
mod delete_metadata;
|
||||
mod delete_meta;
|
||||
mod delete_objects;
|
||||
mod delete_timers;
|
||||
mod fill;
|
||||
mod overlay;
|
||||
mod replace_in_inv;
|
||||
mod replace_nodes;
|
||||
mod set_meta_var;
|
||||
mod set_param2;
|
||||
mod vacuum;
|
||||
|
||||
@ -32,12 +34,14 @@ pub fn get_commands() -> BTreeMap<&'static str, Command> {
|
||||
|
||||
new_cmd!("clone", clone);
|
||||
new_cmd!("deleteblocks", delete_blocks);
|
||||
new_cmd!("deletemeta", delete_metadata);
|
||||
new_cmd!("deletemeta", delete_meta);
|
||||
new_cmd!("deleteobjects", delete_objects);
|
||||
new_cmd!("deletetimers", delete_timers);
|
||||
new_cmd!("fill", fill);
|
||||
new_cmd!("replacenodes", replace_nodes);
|
||||
new_cmd!("replaceininv", replace_in_inv);
|
||||
new_cmd!("overlay", overlay);
|
||||
new_cmd!("setmetavar", set_meta_var);
|
||||
new_cmd!("setparam2", set_param2);
|
||||
new_cmd!("vacuum", vacuum);
|
||||
|
||||
|
123
src/commands/replace_in_inv.rs
Normal file
123
src/commands/replace_in_inv.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use super::Command;
|
||||
|
||||
use crate::unwrap_or;
|
||||
use crate::spatial::Vec3;
|
||||
use crate::instance::{ArgType, InstBundle};
|
||||
use crate::map_block::{MapBlock, NodeMetadataList};
|
||||
use crate::utils::{query_keys, fmt_big_num};
|
||||
|
||||
|
||||
fn do_replace(inv: &mut Vec<u8>, item: &[u8], new_item: &[u8], del_meta: bool)
|
||||
-> u64
|
||||
{
|
||||
const NEWLINE: u8 = b'\n';
|
||||
const SPACE: u8 = b' ';
|
||||
|
||||
let mut new_inv = Vec::new();
|
||||
let mut mods = 0;
|
||||
for line in inv.split(|&x| x == NEWLINE) {
|
||||
let parts: Vec<&[u8]> = line.splitn(4, |&x| x == SPACE).collect();
|
||||
|
||||
if parts[0] == b"Item" && parts[1] == item {
|
||||
new_inv.extend_from_slice(b"Item ");
|
||||
new_inv.extend_from_slice(new_item);
|
||||
if let Some(count) = parts.get(2) {
|
||||
new_inv.push(SPACE);
|
||||
new_inv.extend_from_slice(count);
|
||||
}
|
||||
if !del_meta {
|
||||
if let Some(meta) = parts.get(3) {
|
||||
new_inv.push(SPACE);
|
||||
new_inv.extend_from_slice(meta);
|
||||
}
|
||||
}
|
||||
mods += 1;
|
||||
} else {
|
||||
new_inv.extend_from_slice(line);
|
||||
}
|
||||
new_inv.push(NEWLINE);
|
||||
}
|
||||
|
||||
*inv = new_inv;
|
||||
mods
|
||||
}
|
||||
|
||||
|
||||
fn replace_in_inv(inst: &mut InstBundle) {
|
||||
let item = inst.args.item.as_ref().unwrap().as_bytes().to_owned();
|
||||
let new_item = inst.args.new_item.as_ref().unwrap().as_bytes().to_owned();
|
||||
let del_meta = false;
|
||||
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
||||
|
||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||
node.as_deref(), inst.args.area, inst.args.invert, true);
|
||||
|
||||
inst.status.begin_editing();
|
||||
let mut item_mods: u64 = 0;
|
||||
let mut node_mods: u64 = 0;
|
||||
|
||||
for key in keys {
|
||||
inst.status.inc_done();
|
||||
let data = inst.db.get_block(key).unwrap();
|
||||
let mut block = unwrap_or!(MapBlock::deserialize(&data), continue);
|
||||
|
||||
let node_data = block.node_data.get_ref();
|
||||
let node_id = node.as_deref().and_then(|n| block.nimap.get_id(n));
|
||||
if node.is_some() && node_id.is_none() {
|
||||
continue; // Block doesn't contain the required node.
|
||||
}
|
||||
|
||||
let mut meta = unwrap_or!(
|
||||
NodeMetadataList::deserialize(block.metadata.get_ref()), continue);
|
||||
|
||||
let block_corner = Vec3::from_block_key(key) * 16;
|
||||
let mut modified = false;
|
||||
|
||||
for (&idx, data) in &mut meta.list {
|
||||
let pos = Vec3::from_u16_key(idx);
|
||||
let abs_pos = pos + block_corner;
|
||||
if let Some(a) = inst.args.area {
|
||||
if a.contains(abs_pos) == inst.args.invert {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(id) = node_id {
|
||||
if node_data.nodes[idx as usize] != id {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let i_mods = do_replace(&mut data.inv, &item, &new_item, del_meta);
|
||||
item_mods += i_mods;
|
||||
if i_mods > 0 {
|
||||
node_mods += 1;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if modified {
|
||||
*block.metadata.get_mut() = meta.serialize(block.version);
|
||||
inst.db.set_block(key, &block.serialize()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
inst.status.end_editing();
|
||||
inst.status.log_info(format!("Replaced {} item stacks in {} nodes.",
|
||||
fmt_big_num(item_mods), fmt_big_num(node_mods)));
|
||||
}
|
||||
|
||||
|
||||
pub fn get_command() -> Command {
|
||||
Command {
|
||||
func: replace_in_inv,
|
||||
verify_args: None,
|
||||
args: vec![
|
||||
(ArgType::Item, "Name of item to replace"),
|
||||
(ArgType::NewItem, "Name of new item to replace with"),
|
||||
(ArgType::Area(false), "Area in which to modify inventories"),
|
||||
(ArgType::Invert, "Modify inventories outside the given area."),
|
||||
(ArgType::Node(false), "Node to modify inventories of")
|
||||
],
|
||||
help: "Replace items in node inventories."
|
||||
}
|
||||
}
|
87
src/commands/set_meta_var.rs
Normal file
87
src/commands/set_meta_var.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use super::Command;
|
||||
|
||||
use crate::unwrap_or;
|
||||
use crate::spatial::Vec3;
|
||||
use crate::instance::{ArgType, InstBundle};
|
||||
use crate::map_block::{MapBlock, NodeMetadataList};
|
||||
use crate::utils::{query_keys, fmt_big_num};
|
||||
|
||||
|
||||
fn set_meta_var(inst: &mut InstBundle) {
|
||||
let key = inst.args.key.as_ref().unwrap().as_bytes().to_owned();
|
||||
let value = inst.args.value.as_ref().unwrap().as_bytes().to_owned();
|
||||
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
||||
|
||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||
node.as_deref(), inst.args.area, inst.args.invert, true);
|
||||
|
||||
inst.status.begin_editing();
|
||||
let mut count: u64 = 0;
|
||||
|
||||
for block_key in keys {
|
||||
inst.status.inc_done();
|
||||
let data = inst.db.get_block(block_key).unwrap();
|
||||
let mut block = unwrap_or!(MapBlock::deserialize(&data), continue);
|
||||
|
||||
let node_data = block.node_data.get_ref();
|
||||
let node_id = node.as_deref().and_then(|n| block.nimap.get_id(n));
|
||||
if node.is_some() && node_id.is_none() {
|
||||
continue; // Block doesn't contain the required node.
|
||||
}
|
||||
|
||||
let mut meta = unwrap_or!(
|
||||
NodeMetadataList::deserialize(block.metadata.get_ref()), continue);
|
||||
|
||||
let block_corner = Vec3::from_block_key(block_key) * 16;
|
||||
let mut modified = false;
|
||||
|
||||
for (&idx, data) in &mut meta.list {
|
||||
let pos = Vec3::from_u16_key(idx);
|
||||
let abs_pos = pos + block_corner;
|
||||
|
||||
if let Some(a) = inst.args.area {
|
||||
if a.contains(abs_pos) == inst.args.invert {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(id) = node_id {
|
||||
if node_data.nodes[idx as usize] != id {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(val) = data.vars.get_mut(&key) {
|
||||
val.0 = value.clone();
|
||||
modified = true;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if modified {
|
||||
*block.metadata.get_mut() = meta.serialize(block.version);
|
||||
inst.db.set_block(block_key, &block.serialize()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
inst.status.end_editing();
|
||||
inst.status.log_info(
|
||||
format!("Set metadata variable of {} nodes.", fmt_big_num(count)));
|
||||
}
|
||||
|
||||
|
||||
pub fn get_command() -> Command {
|
||||
Command {
|
||||
func: set_meta_var,
|
||||
verify_args: None,
|
||||
args: vec![
|
||||
(ArgType::Key, "Name of key to set in metadata"),
|
||||
(ArgType::Value, "Value to set in metadata"),
|
||||
(ArgType::Area(false), "Area in which to modify node metadata"),
|
||||
(ArgType::Invert, "Modify node metadata outside the given area."),
|
||||
(ArgType::Node(false),
|
||||
"Node to modify metadata in. If not specified, all relevant \
|
||||
metadata will be modified.")
|
||||
],
|
||||
help: "Set a variable in node metadata."
|
||||
}
|
||||
}
|
@ -17,9 +17,13 @@ pub enum ArgType {
|
||||
Offset(bool),
|
||||
Node(bool),
|
||||
NewNode(bool),
|
||||
Item,
|
||||
NewItem,
|
||||
Param2Val(bool),
|
||||
Object(bool),
|
||||
Items,
|
||||
Key,
|
||||
Value,
|
||||
}
|
||||
|
||||
|
||||
@ -33,9 +37,13 @@ pub struct InstArgs {
|
||||
pub offset: Option<Vec3>,
|
||||
pub node: Option<String>,
|
||||
pub new_node: Option<String>,
|
||||
pub item: Option<String>,
|
||||
pub new_item: Option<String>,
|
||||
pub param2_val: Option<u8>,
|
||||
pub object: Option<String>,
|
||||
pub items: Option<Vec<String>>,
|
||||
pub key: Option<String>,
|
||||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@ -248,12 +256,7 @@ fn compute_thread(args: InstArgs, status: StatusServer)
|
||||
};
|
||||
|
||||
let func = commands[args.command.as_str()].func;
|
||||
let mut inst = InstBundle {
|
||||
args,
|
||||
status,
|
||||
db,
|
||||
idb
|
||||
};
|
||||
let mut inst = InstBundle {args, status, db, idb};
|
||||
func(&mut inst);
|
||||
|
||||
if inst.db.is_in_transaction() {
|
||||
|
@ -9,7 +9,8 @@ mod commands;
|
||||
mod cmd_line;
|
||||
|
||||
|
||||
// Todo: Check for unnecessary #derives!
|
||||
// TODO: Check for unnecessary #derives!
|
||||
// TODO: Check mapedit TODOs and implement what's needed.
|
||||
fn main() {
|
||||
// TODO: Add GUI. hmm...
|
||||
cmd_line::run_cmd_line();
|
||||
|
@ -2,7 +2,6 @@ use super::*;
|
||||
|
||||
const MIN_BLOCK_VER: u8 = 25;
|
||||
const MAX_BLOCK_VER: u8 = 28;
|
||||
|
||||
const BLOCK_BUF_SIZE: usize = 2048;
|
||||
|
||||
|
||||
|
@ -149,21 +149,30 @@ mod tests {
|
||||
Area::from_unsorted(Vec3::new(10, 80, 42), Vec3::new(10, -50, 99)),
|
||||
Area::new(Vec3::new(10, -50, 42), Vec3::new(10, 80, 99))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Area::new(Vec3::new(0, 0, 0), Vec3::new(0, 0, 0)).volume(), 1);
|
||||
assert_eq!(
|
||||
Area::new(Vec3::new(0, -9, 14), Vec3::new(19, 0, 17)).volume(),
|
||||
800);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_area_iteration() {
|
||||
let a = Area::new(Vec3::new(0, -1, -2), Vec3::new(5, 7, 11));
|
||||
let mut iter = a.iterate();
|
||||
|
||||
for z in -2..=11 {
|
||||
for y in -1..=7 {
|
||||
for x in 0..=5 {
|
||||
assert_eq!(iter.next(), Some(Vec3::new(x, y, z)));
|
||||
fn iter_area(a: Area) {
|
||||
let mut iter = a.iterate();
|
||||
for z in a.min.z..=a.max.z {
|
||||
for y in a.min.y..=a.max.y {
|
||||
for x in a.min.x..=a.max.x {
|
||||
assert_eq!(iter.next(), Some(Vec3::new(x, y, z)))
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
assert_eq!(iter.next(), None);
|
||||
iter_area(Area::new(Vec3::new(-1, -1, -1), Vec3::new(-1, -1, -1)));
|
||||
iter_area(Area::new(Vec3::new(10, -99, 11), Vec3::new(10, -99, 12)));
|
||||
iter_area(Area::new(Vec3::new(0, -1, -2), Vec3::new(5, 7, 11)));
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
use std::cmp::{min, max};
|
||||
|
||||
mod vec3;
|
||||
// TODO
|
||||
// mod v3f;
|
||||
mod area;
|
||||
|
||||
pub use vec3::Vec3;
|
||||
// pub use v3f::V3f;
|
||||
pub use area::Area;
|
||||
|
||||
|
||||
// TODO: Should these go in the area impl?
|
||||
pub fn area_contains_block(area: &Area, block_pos: Vec3) -> bool {
|
||||
let corner = block_pos * 16;
|
||||
area.min.x <= corner.x && corner.x + 15 <= area.max.x
|
||||
|
@ -1,72 +0,0 @@
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct V3f {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32
|
||||
}
|
||||
|
||||
impl V3f {
|
||||
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self {x, y, z}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add<Self> for V3f {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self {
|
||||
x: self.x + rhs.x,
|
||||
y: self.y + rhs.y,
|
||||
z: self.z + rhs.z
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add<f32> for V3f {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: f32) -> Self {
|
||||
Self {
|
||||
x: self.x + rhs,
|
||||
y: self.y + rhs,
|
||||
z: self.z + rhs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub<Self> for V3f {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self {
|
||||
Self {
|
||||
x: self.x - rhs.x,
|
||||
y: self.y - rhs.y,
|
||||
z: self.z - rhs.z
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<Self> for V3f {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
Self {
|
||||
x: self.x * rhs.x,
|
||||
y: self.y * rhs.y,
|
||||
z: self.z * rhs.z
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<f32> for V3f {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: f32) -> Self {
|
||||
Self {
|
||||
x: self.x * rhs,
|
||||
y: self.y * rhs,
|
||||
z: self.z * rhs
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user