mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
Merge pull request #748 from b-guild/tilemapdata
Improved Tile Set Editing
This commit is contained in:
@@ -77,17 +77,28 @@ impl dyn CommandContext + '_ {
|
||||
}
|
||||
}
|
||||
|
||||
/// An object that can be added to the editors [`CommandStack`] so the user
|
||||
/// can execute it and revert it.
|
||||
pub trait CommandTrait: Debug + 'static {
|
||||
/// The name that the user should see in the command stack.
|
||||
fn name(&mut self, context: &dyn CommandContext) -> String;
|
||||
/// Perform the operation that this object represents.
|
||||
/// This happens when the object is first added to the command stack,
|
||||
/// and when the object is redone after being undone.
|
||||
fn execute(&mut self, context: &mut dyn CommandContext);
|
||||
/// Undo the consequences of calling [`CommandTrait::execute`].
|
||||
fn revert(&mut self, context: &mut dyn CommandContext);
|
||||
/// This object is leaving the command stack, so it will never
|
||||
/// be executed or reverted again.
|
||||
fn finalize(&mut self, _: &mut dyn CommandContext) {}
|
||||
}
|
||||
|
||||
/// An untyped command for the editor to execute or revert.
|
||||
#[derive(Debug)]
|
||||
pub struct Command(pub Box<dyn CommandTrait>);
|
||||
|
||||
impl Command {
|
||||
/// Create a command from the given `CommandTrait` object.
|
||||
pub fn new<C: CommandTrait>(cmd: C) -> Self {
|
||||
Self(Box::new(cmd))
|
||||
}
|
||||
@@ -107,6 +118,11 @@ impl DerefMut for Command {
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of commands to execute in order as a single command.
|
||||
/// The commands are reverted in reverse order.
|
||||
/// Use [`CommandGroup::with_custom_name`] to give the command a
|
||||
/// name. Otherwise, a name is automatically constructed by listing
|
||||
/// the names of the commands in the group.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CommandGroup {
|
||||
commands: Vec<Command>,
|
||||
@@ -123,19 +139,28 @@ impl From<Vec<Command>> for CommandGroup {
|
||||
}
|
||||
|
||||
impl CommandGroup {
|
||||
/// Add an object of the `CommandTriat` to the group.
|
||||
pub fn push<C: CommandTrait>(&mut self, command: C) {
|
||||
self.commands.push(Command::new(command))
|
||||
}
|
||||
|
||||
/// Add a `Command` to the group.
|
||||
pub fn push_command(&mut self, command: Command) {
|
||||
self.commands.push(command)
|
||||
}
|
||||
|
||||
/// Replace the automatically constructed name.
|
||||
pub fn with_custom_name<S: AsRef<str>>(mut self, name: S) -> Self {
|
||||
self.custom_name = name.as_ref().to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// True if this group contains no commands.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.commands.is_empty()
|
||||
}
|
||||
|
||||
/// The number of commands in the group.
|
||||
pub fn len(&self) -> usize {
|
||||
self.commands.len()
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ use fyrox::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command::{Command, CommandContext, CommandTrait},
|
||||
command::{Command, CommandContext, CommandGroup, CommandTrait},
|
||||
send_sync_message,
|
||||
};
|
||||
|
||||
@@ -117,7 +117,7 @@ impl TerrainSource for TileSetTerrainSource<'_, '_> {
|
||||
|
||||
fn iter(&self) -> impl Iterator<Item = NeededTerrain<Vector2<i32>, TileTerrainId>> + '_ {
|
||||
self.update.iter().filter_map(|(p, v)| {
|
||||
let cell_data = self.instance.cells.get(v.as_ref()?.brush_cell.as_ref()?)?;
|
||||
let cell_data = self.instance.cells.get(&v.as_ref()?.source?.handle()?)?;
|
||||
Some(NeededTerrain {
|
||||
position: *p,
|
||||
terrain: cell_data.terrain_id,
|
||||
@@ -130,7 +130,7 @@ impl TerrainSource for TileSetTerrainSource<'_, '_> {
|
||||
let Some(el) = self.update.get(position).cloned().flatten() else {
|
||||
return false;
|
||||
};
|
||||
let Some(brush_cell) = el.brush_cell else {
|
||||
let Some(brush_cell) = el.source.and_then(|s| s.handle()) else {
|
||||
return false;
|
||||
};
|
||||
self.instance.cells.contains_key(&brush_cell)
|
||||
@@ -280,7 +280,7 @@ impl BrushMacro for AutoTileMacro {
|
||||
cell_set.extend(data.cells.keys());
|
||||
}
|
||||
|
||||
fn create_cell(&self, context: &BrushMacroCell) -> Option<Command> {
|
||||
fn create_cell(&self, context: &BrushMacroCellContext) -> Option<Command> {
|
||||
let instance = context.settings()?;
|
||||
let cell = context.cell?;
|
||||
Some(Command::new(SetCellCommand {
|
||||
@@ -291,17 +291,99 @@ impl BrushMacro for AutoTileMacro {
|
||||
}))
|
||||
}
|
||||
|
||||
fn remove_cell(&self, context: &BrushMacroCell) -> Option<Command> {
|
||||
let instance = context.settings()?;
|
||||
let cell = context.cell?;
|
||||
fn move_cells(
|
||||
&self,
|
||||
from: Box<[TileDefinitionHandle]>,
|
||||
to: Box<[TileDefinitionHandle]>,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command> {
|
||||
let instance = context.settings::<AutoTileInstance>()?;
|
||||
Some(Command::new(MoveCellsCommand::new(
|
||||
context.brush.clone(),
|
||||
from,
|
||||
to,
|
||||
instance,
|
||||
)))
|
||||
}
|
||||
|
||||
fn move_pages(
|
||||
&self,
|
||||
from: Box<[Vector2<i32>]>,
|
||||
to: Box<[Vector2<i32>]>,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command> {
|
||||
let instance = context.settings::<AutoTileInstance>()?;
|
||||
Some(Command::new(MovePagesCommand::new(
|
||||
context.brush.clone(),
|
||||
from,
|
||||
to,
|
||||
instance,
|
||||
)))
|
||||
}
|
||||
|
||||
fn copy_cell(
|
||||
&self,
|
||||
source: Option<TileDefinitionHandle>,
|
||||
destination: TileDefinitionHandle,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command> {
|
||||
let instance = context.settings::<AutoTileInstance>()?;
|
||||
let guard = instance.data_ref();
|
||||
let data = if let Some(source) = source {
|
||||
guard.cells.get(&source).cloned()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Command::new(SetCellCommand {
|
||||
brush: context.brush.clone(),
|
||||
cell,
|
||||
instance,
|
||||
data: None,
|
||||
cell: destination,
|
||||
instance: instance.clone(),
|
||||
data,
|
||||
}))
|
||||
}
|
||||
|
||||
fn copy_page(
|
||||
&self,
|
||||
source: Option<Vector2<i32>>,
|
||||
destination: Vector2<i32>,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command> {
|
||||
let instance = context.settings::<AutoTileInstance>()?;
|
||||
let guard = instance.data_ref();
|
||||
let used = guard
|
||||
.cells
|
||||
.keys()
|
||||
.filter(|h| Some(h.page()) == source)
|
||||
.map(|h| h.tile())
|
||||
.collect::<Vec<_>>();
|
||||
let commands = guard
|
||||
.cells
|
||||
.iter()
|
||||
.filter_map(|(handle, data)| {
|
||||
if Some(handle.page()) == source {
|
||||
let cell = TileDefinitionHandle::try_new(destination, handle.tile())?;
|
||||
Some(Command::new(SetCellCommand {
|
||||
brush: context.brush.clone(),
|
||||
cell,
|
||||
instance: instance.clone(),
|
||||
data: Some(data.clone()),
|
||||
}))
|
||||
} else if handle.page() == destination && !used.contains(&handle.tile()) {
|
||||
Some(Command::new(SetCellCommand {
|
||||
brush: context.brush.clone(),
|
||||
cell: *handle,
|
||||
instance: instance.clone(),
|
||||
data: None,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
(!commands.is_empty())
|
||||
.then(|| Command::new(CommandGroup::from(commands).with_custom_name("Set Page Macros")))
|
||||
}
|
||||
|
||||
fn build_instance_editor(
|
||||
&mut self,
|
||||
context: &BrushMacroInstance,
|
||||
@@ -360,7 +442,7 @@ impl BrushMacro for AutoTileMacro {
|
||||
|
||||
fn build_cell_editor(
|
||||
&mut self,
|
||||
context: &BrushMacroCell,
|
||||
context: &BrushMacroCellContext,
|
||||
ctx: &mut BuildContext,
|
||||
) -> Option<Handle<UiNode>> {
|
||||
let instance = context.settings::<AutoTileInstance>().unwrap();
|
||||
@@ -521,8 +603,10 @@ impl BrushMacro for AutoTileMacro {
|
||||
};
|
||||
let frequency_property = instance.frequency_property;
|
||||
// Check that at least one cell in the stamp is part of this instance.
|
||||
if !stamp.values().any(|StampElement { brush_cell, .. }| {
|
||||
brush_cell.is_some_and(|handle| instance.cells.contains_key(&handle))
|
||||
if !stamp.values().any(|StampElement { source, .. }| {
|
||||
source
|
||||
.and_then(|s| s.handle())
|
||||
.is_some_and(|handle| instance.cells.contains_key(&handle))
|
||||
}) {
|
||||
self.context.clear();
|
||||
return;
|
||||
@@ -613,6 +697,129 @@ impl CommandTrait for SetCellCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MoveCellsCommand {
|
||||
pub brush: TileMapBrushResource,
|
||||
pub from: Box<[TileDefinitionHandle]>,
|
||||
pub to: Box<[TileDefinitionHandle]>,
|
||||
pub instance: Resource<AutoTileInstance>,
|
||||
data: Box<[Option<CellData>]>,
|
||||
}
|
||||
|
||||
impl MoveCellsCommand {
|
||||
fn new(
|
||||
brush: TileMapBrushResource,
|
||||
from: Box<[TileDefinitionHandle]>,
|
||||
to: Box<[TileDefinitionHandle]>,
|
||||
instance: Resource<AutoTileInstance>,
|
||||
) -> Self {
|
||||
Self {
|
||||
data: vec![None; from.len()].into_boxed_slice(),
|
||||
brush,
|
||||
from,
|
||||
to,
|
||||
instance,
|
||||
}
|
||||
}
|
||||
fn swap(&mut self) {
|
||||
let mut instance = self.instance.data_ref();
|
||||
for (&from, data) in self.from.iter().zip(self.data.iter_mut()) {
|
||||
swap_hash_map_entry(instance.cells.entry(from), data);
|
||||
}
|
||||
for (&to, data) in self.to.iter().zip(self.data.iter_mut()) {
|
||||
swap_hash_map_entry(instance.cells.entry(to), data);
|
||||
}
|
||||
std::mem::swap(&mut self.from, &mut self.to);
|
||||
self.brush.data_ref().change_flag.set();
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandTrait for MoveCellsCommand {
|
||||
fn name(&mut self, _context: &dyn CommandContext) -> String {
|
||||
"Update Autotile Cell".into()
|
||||
}
|
||||
|
||||
fn execute(&mut self, _context: &mut dyn CommandContext) {
|
||||
self.swap();
|
||||
}
|
||||
|
||||
fn revert(&mut self, _context: &mut dyn CommandContext) {
|
||||
self.swap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MovePagesCommand {
|
||||
pub brush: TileMapBrushResource,
|
||||
pub from: Box<[Vector2<i32>]>,
|
||||
pub to: Box<[Vector2<i32>]>,
|
||||
pub instance: Resource<AutoTileInstance>,
|
||||
data: Box<[FxHashMap<Vector2<i32>, CellData>]>,
|
||||
}
|
||||
|
||||
impl MovePagesCommand {
|
||||
fn new(
|
||||
brush: TileMapBrushResource,
|
||||
from: Box<[Vector2<i32>]>,
|
||||
to: Box<[Vector2<i32>]>,
|
||||
instance: Resource<AutoTileInstance>,
|
||||
) -> Self {
|
||||
Self {
|
||||
data: vec![FxHashMap::default(); from.len()].into_boxed_slice(),
|
||||
brush,
|
||||
from,
|
||||
to,
|
||||
instance,
|
||||
}
|
||||
}
|
||||
fn swap(&mut self) {
|
||||
let mut instance = self.instance.data_ref();
|
||||
for (&from, data) in self.from.iter().zip(self.data.iter_mut()) {
|
||||
swap_page(&mut instance.cells, from, data);
|
||||
}
|
||||
for (&to, data) in self.to.iter().zip(self.data.iter_mut()) {
|
||||
swap_page(&mut instance.cells, to, data);
|
||||
}
|
||||
std::mem::swap(&mut self.from, &mut self.to);
|
||||
self.brush.data_ref().change_flag.set();
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_page<T: Default>(
|
||||
map: &mut FxHashMap<TileDefinitionHandle, T>,
|
||||
position: Vector2<i32>,
|
||||
data: &mut FxHashMap<Vector2<i32>, T>,
|
||||
) {
|
||||
let mut new_data = FxHashMap::default();
|
||||
map.retain(|h, d| {
|
||||
if h.page() != position {
|
||||
true
|
||||
} else {
|
||||
new_data.insert(h.tile(), std::mem::take(d));
|
||||
false
|
||||
}
|
||||
});
|
||||
map.extend(
|
||||
data.drain()
|
||||
.filter_map(|(p, d)| Some((TileDefinitionHandle::try_new(position, p)?, d))),
|
||||
);
|
||||
*data = new_data;
|
||||
}
|
||||
|
||||
impl CommandTrait for MovePagesCommand {
|
||||
fn name(&mut self, _context: &dyn CommandContext) -> String {
|
||||
"Update Autotile Cell".into()
|
||||
}
|
||||
|
||||
fn execute(&mut self, _context: &mut dyn CommandContext) {
|
||||
self.swap();
|
||||
}
|
||||
|
||||
fn revert(&mut self, _context: &mut dyn CommandContext) {
|
||||
self.swap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SetPatternPropCommand {
|
||||
pub brush: TileMapBrushResource,
|
||||
|
||||
@@ -156,8 +156,8 @@ pub struct MacroMessageContext {
|
||||
pub cell: Option<TileDefinitionHandle>,
|
||||
}
|
||||
|
||||
impl From<BrushMacroCell> for MacroMessageContext {
|
||||
fn from(value: BrushMacroCell) -> Self {
|
||||
impl From<BrushMacroCellContext> for MacroMessageContext {
|
||||
fn from(value: BrushMacroCellContext) -> Self {
|
||||
Self {
|
||||
brush: value.brush,
|
||||
cell: value.cell,
|
||||
@@ -192,7 +192,7 @@ impl MacroMessageContext {
|
||||
/// such as adding a new cell to the macro, removing a cell, or building the widgets
|
||||
/// for editing the settings of a cell.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BrushMacroCell {
|
||||
pub struct BrushMacroCellContext {
|
||||
/// The brush that has an instance of the macro.
|
||||
pub brush: TileMapBrushResource,
|
||||
/// The configuration for an instance of the macro.
|
||||
@@ -201,8 +201,8 @@ pub struct BrushMacroCell {
|
||||
pub cell: Option<TileDefinitionHandle>,
|
||||
}
|
||||
|
||||
impl From<BrushMacroCell> for BrushMacroInstance {
|
||||
fn from(value: BrushMacroCell) -> Self {
|
||||
impl From<BrushMacroCellContext> for BrushMacroInstance {
|
||||
fn from(value: BrushMacroCellContext) -> Self {
|
||||
Self {
|
||||
brush: value.brush,
|
||||
settings: value.settings,
|
||||
@@ -210,7 +210,7 @@ impl From<BrushMacroCell> for BrushMacroInstance {
|
||||
}
|
||||
}
|
||||
|
||||
impl BrushMacroCell {
|
||||
impl BrushMacroCellContext {
|
||||
/// A typed reference to the configuration resource.
|
||||
pub fn settings<T>(&self) -> Option<Resource<T>>
|
||||
where
|
||||
@@ -296,11 +296,47 @@ pub trait BrushMacro: 'static + Send + Sync {
|
||||
/// or no cell is selected. Adding the currently selected cell will naturally require
|
||||
/// the widgets that edit the data to change, but that will wait until
|
||||
/// [`BrushMacro::sync_cell_editors`] is called.
|
||||
fn create_cell(&self, context: &BrushMacroCell) -> Option<Command>;
|
||||
/// Create a command to modify the given instances's data to remove the given cell.
|
||||
/// None is returned if no command is necessary, such as if the cell is already excluded
|
||||
/// or no cell is selected.
|
||||
fn remove_cell(&self, context: &BrushMacroCell) -> Option<Command>;
|
||||
fn create_cell(&self, context: &BrushMacroCellContext) -> Option<Command>;
|
||||
/// Create a command to move cell data from the given cells to the given cells.
|
||||
/// The two lists of cells should always be the same length.
|
||||
/// This method works on multiple cells at once because `from` and `to` may
|
||||
/// share some cells in common, in which case correctly moving the cells
|
||||
/// requires first removing all the cells from `from`, then inserting all
|
||||
/// the cells into `to`.
|
||||
fn move_cells(
|
||||
&self,
|
||||
from: Box<[TileDefinitionHandle]>,
|
||||
to: Box<[TileDefinitionHandle]>,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command>;
|
||||
/// Create a command to move cell data from the given pages to the given pages.
|
||||
/// The two lists of pages should always be the same length.
|
||||
/// This method works on multiple pages at once because `from` and `to` may
|
||||
/// share some pages in common, in which case correctly moving the pages
|
||||
/// requires first removing all the pages from `from`, then inserting all
|
||||
/// the pages into `to`.
|
||||
fn move_pages(
|
||||
&self,
|
||||
from: Box<[Vector2<i32>]>,
|
||||
to: Box<[Vector2<i32>]>,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command>;
|
||||
/// Create a command to modify the given instances's data to copy the given cell.
|
||||
/// None is returned if no command is necessary, such as if the macro has no cell data.
|
||||
fn copy_cell(
|
||||
&self,
|
||||
source: Option<TileDefinitionHandle>,
|
||||
destination: TileDefinitionHandle,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command>;
|
||||
/// Create a command to modify the given instances's data to copy the given page.
|
||||
/// None is returned if no command is necessary, such as if the macro has no cell data.
|
||||
fn copy_page(
|
||||
&self,
|
||||
source: Option<Vector2<i32>>,
|
||||
destination: Vector2<i32>,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command>;
|
||||
/// Modify the given `cell_set` to include the handles of all the cells that are part of
|
||||
/// the given instance of this macro. This is necessary for the brush editor to accurately
|
||||
/// update itself.
|
||||
@@ -329,7 +365,7 @@ pub trait BrushMacro: 'static + Send + Sync {
|
||||
/// when [`BrushMacro::sync_cell_editors`] is called.
|
||||
fn build_cell_editor(
|
||||
&mut self,
|
||||
context: &BrushMacroCell,
|
||||
context: &BrushMacroCellContext,
|
||||
ctx: &mut BuildContext,
|
||||
) -> Option<Handle<UiNode>>;
|
||||
/// Send the necessary messages to update the cell editor widgets to edit the data for
|
||||
|
||||
@@ -1127,13 +1127,26 @@ pub struct ModifyPageIconCommand {
|
||||
pub tile_set: TileSetResource,
|
||||
pub page: Vector2<i32>,
|
||||
pub icon: TileDefinitionHandle,
|
||||
error: bool,
|
||||
}
|
||||
|
||||
impl ModifyPageIconCommand {
|
||||
pub fn new(tile_set: TileSetResource, page: Vector2<i32>, icon: TileDefinitionHandle) -> Self {
|
||||
Self {
|
||||
tile_set,
|
||||
page,
|
||||
icon,
|
||||
error: false,
|
||||
}
|
||||
}
|
||||
fn swap(&mut self) {
|
||||
if self.error {
|
||||
return;
|
||||
}
|
||||
let mut tile_set = self.tile_set.data_ref();
|
||||
let Some(page) = &mut tile_set.pages.get_mut(&self.page) else {
|
||||
Log::err("Modify icon of non-existent tile page.");
|
||||
self.error = true;
|
||||
return;
|
||||
};
|
||||
std::mem::swap(&mut self.icon, &mut page.icon);
|
||||
@@ -1160,13 +1173,30 @@ pub struct ModifyBrushPageIconCommand {
|
||||
pub brush: TileMapBrushResource,
|
||||
pub page: Vector2<i32>,
|
||||
pub icon: TileDefinitionHandle,
|
||||
error: bool,
|
||||
}
|
||||
|
||||
impl ModifyBrushPageIconCommand {
|
||||
pub fn new(
|
||||
brush: TileMapBrushResource,
|
||||
page: Vector2<i32>,
|
||||
icon: TileDefinitionHandle,
|
||||
) -> Self {
|
||||
Self {
|
||||
brush,
|
||||
page,
|
||||
icon,
|
||||
error: false,
|
||||
}
|
||||
}
|
||||
fn swap(&mut self) {
|
||||
if self.error {
|
||||
return;
|
||||
}
|
||||
let mut brush = self.brush.data_ref();
|
||||
let Some(page) = &mut brush.pages.get_mut(&self.page) else {
|
||||
Log::err("Modify icon of non-existent tile page.");
|
||||
self.error = true;
|
||||
return;
|
||||
};
|
||||
std::mem::swap(&mut self.icon, &mut page.icon);
|
||||
|
||||
@@ -231,7 +231,7 @@ fn make_items(
|
||||
});
|
||||
continue;
|
||||
}
|
||||
let brush_macro_cell = BrushMacroCell {
|
||||
let brush_macro_cell = BrushMacroCellContext {
|
||||
brush: brush.clone(),
|
||||
settings: instance.settings.clone(),
|
||||
cell,
|
||||
@@ -410,15 +410,10 @@ impl MacroInspector {
|
||||
let settings = instance.settings.clone();
|
||||
drop(brush_guard);
|
||||
let command = if cell_sets.cell_has_macro(cell, i) {
|
||||
let cell = Some(cell);
|
||||
brush_macro.remove_cell(&BrushMacroCell {
|
||||
brush,
|
||||
settings,
|
||||
cell,
|
||||
})
|
||||
brush_macro.copy_cell(None, cell, &BrushMacroInstance { brush, settings })
|
||||
} else {
|
||||
let cell = Some(cell);
|
||||
brush_macro.create_cell(&BrushMacroCell {
|
||||
brush_macro.create_cell(&BrushMacroCellContext {
|
||||
brush,
|
||||
settings,
|
||||
cell,
|
||||
|
||||
@@ -59,7 +59,6 @@ use fyrox::gui::style::Style;
|
||||
use fyrox::gui::text::TextBuilder;
|
||||
use fyrox::gui::{message::KeyCode, texture::TextureResource};
|
||||
use fyrox::gui::{HorizontalAlignment, VerticalAlignment};
|
||||
use fyrox::scene::tilemap::brush::TileMapBrushResource;
|
||||
use fyrox::scene::tilemap::tileset::{TileSetPropertyLayer, ELEMENT_MATCH_HIGHLIGHT_COLOR};
|
||||
use fyrox::scene::tilemap::{StampElement, TileMapEffectRef};
|
||||
pub use handle_editor::*;
|
||||
@@ -547,7 +546,7 @@ impl TileDrawState {
|
||||
#[inline]
|
||||
pub fn update_stamp<F>(
|
||||
&mut self,
|
||||
brush: Option<TileMapBrushResource>,
|
||||
book: Option<TileBook>,
|
||||
tile_set: Option<TileSetResource>,
|
||||
tile_handle: F,
|
||||
) where
|
||||
@@ -555,7 +554,7 @@ impl TileDrawState {
|
||||
{
|
||||
self.tile_set = tile_set;
|
||||
self.stamp.build(
|
||||
brush,
|
||||
book,
|
||||
self.selection
|
||||
.positions
|
||||
.iter()
|
||||
|
||||
@@ -22,8 +22,9 @@
|
||||
//! is responsible for displaying a grid of tiles where the user may select tiles,
|
||||
//! drag tiles, and use drawing tools upon the tiles.
|
||||
|
||||
use fyrox::scene::tilemap::brush::TileMapBrushResource;
|
||||
use fyrox::scene::tilemap::tileset::OptionTileSet;
|
||||
use fyrox::scene::tilemap::ResourceTilePosition;
|
||||
use fyrox::scene::tilemap::{ResourceTilePosition, RotTileHandle};
|
||||
|
||||
use super::{commands::*, *};
|
||||
use crate::asset::item::AssetItem;
|
||||
@@ -255,6 +256,10 @@ pub struct PaletteWidget {
|
||||
#[visit(skip)]
|
||||
#[reflect(hidden)]
|
||||
macro_cells: Option<MacroCellSetListRef>,
|
||||
/// The cells which should be marked as being included in some macro.
|
||||
#[visit(skip)]
|
||||
#[reflect(hidden)]
|
||||
macro_list: Option<BrushMacroListRef>,
|
||||
#[visit(skip)]
|
||||
#[reflect(hidden)]
|
||||
colliders: Vec<ColliderHighlight>,
|
||||
@@ -371,6 +376,317 @@ fn invert_transform(trans: &Matrix3<f32>) -> Matrix3<f32> {
|
||||
trans.try_inverse().unwrap_or(Matrix3::identity())
|
||||
}
|
||||
|
||||
fn send_update_tile_set_pages(
|
||||
tile_set: &TileSetResource,
|
||||
update: &TransTilesUpdate,
|
||||
state: &TileDrawStateRef,
|
||||
sender: &mut MessageSender,
|
||||
) {
|
||||
match state.lock().stamp.source() {
|
||||
Some(TileBook::TileSet(source)) => {
|
||||
send_copy_tile_set_pages(source, update, tile_set, sender)
|
||||
}
|
||||
_ => send_update_tile_set_icons(update, tile_set, sender),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_copy_tile_set_pages(
|
||||
source: &TileSetResource,
|
||||
update: &TransTilesUpdate,
|
||||
destination: &TileSetResource,
|
||||
sender: &mut MessageSender,
|
||||
) {
|
||||
let mut commands = Vec::default();
|
||||
for (p, data) in update.iter() {
|
||||
let Some(RotTileHandle {
|
||||
element:
|
||||
StampElement {
|
||||
handle,
|
||||
source: element_source,
|
||||
},
|
||||
transform,
|
||||
}) = data
|
||||
else {
|
||||
commands.push(Command::new(SetTileSetPageCommand {
|
||||
tile_set: destination.clone(),
|
||||
position: *p,
|
||||
page: None,
|
||||
}));
|
||||
continue;
|
||||
};
|
||||
if let Some(ResourceTilePosition::Page(source_page)) = element_source {
|
||||
let source = source.data_ref();
|
||||
let mut page_data = source.get_page(*source_page).cloned();
|
||||
if let Some(page_data) = page_data.as_mut() {
|
||||
let icon = page_data.icon;
|
||||
let icon = source
|
||||
.get_transformed_version(*transform, icon)
|
||||
.unwrap_or(icon);
|
||||
page_data.icon = icon;
|
||||
}
|
||||
commands.push(Command::new(SetTileSetPageCommand {
|
||||
tile_set: destination.clone(),
|
||||
position: *p,
|
||||
page: page_data,
|
||||
}));
|
||||
} else {
|
||||
let source = source.data_ref();
|
||||
let icon = source
|
||||
.get_transformed_version(*transform, *handle)
|
||||
.unwrap_or(*handle);
|
||||
commands.push(Command::new(ModifyPageIconCommand::new(
|
||||
destination.clone(),
|
||||
*p,
|
||||
icon,
|
||||
)));
|
||||
}
|
||||
}
|
||||
if !commands.is_empty() {
|
||||
sender.do_command(CommandGroup::from(commands).with_custom_name("Edit Tile Set Pages"));
|
||||
}
|
||||
}
|
||||
|
||||
fn send_update_tile_set_icons(
|
||||
update: &TransTilesUpdate,
|
||||
destination: &TileSetResource,
|
||||
sender: &mut MessageSender,
|
||||
) {
|
||||
let mut commands = Vec::default();
|
||||
for (p, data) in update.iter() {
|
||||
let Some(RotTileHandle {
|
||||
element: StampElement { handle, .. },
|
||||
transform,
|
||||
}) = data
|
||||
else {
|
||||
commands.push(Command::new(SetTileSetPageCommand {
|
||||
tile_set: destination.clone(),
|
||||
position: *p,
|
||||
page: None,
|
||||
}));
|
||||
continue;
|
||||
};
|
||||
let tile_set = destination.data_ref();
|
||||
let icon = tile_set
|
||||
.get_transformed_version(*transform, *handle)
|
||||
.unwrap_or(*handle);
|
||||
commands.push(Command::new(ModifyPageIconCommand::new(
|
||||
destination.clone(),
|
||||
*p,
|
||||
icon,
|
||||
)));
|
||||
}
|
||||
if !commands.is_empty() {
|
||||
sender.do_command(CommandGroup::from(commands).with_custom_name("Edit Tile Set Pages"));
|
||||
}
|
||||
}
|
||||
|
||||
fn send_update_brush_pages(
|
||||
brush: &TileMapBrushResource,
|
||||
update: &TransTilesUpdate,
|
||||
state: &TileDrawStateRef,
|
||||
macro_list: Option<&BrushMacroListRef>,
|
||||
sender: &mut MessageSender,
|
||||
) {
|
||||
let guard = state.lock();
|
||||
match guard.stamp.source() {
|
||||
Some(TileBook::Brush(source)) => {
|
||||
send_copy_brush_pages(source, update, brush, macro_list, sender)
|
||||
}
|
||||
_ => {
|
||||
send_update_brush_icons(guard.tile_set.as_ref(), update, brush, macro_list, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_copy_brush_pages(
|
||||
source: &TileMapBrushResource,
|
||||
update: &TransTilesUpdate,
|
||||
destination: &TileMapBrushResource,
|
||||
macro_list: Option<&BrushMacroListRef>,
|
||||
sender: &mut MessageSender,
|
||||
) {
|
||||
let mut commands = CommandGroup::default();
|
||||
let same_brush = source == destination;
|
||||
for (p, data) in update.iter() {
|
||||
let Some(RotTileHandle {
|
||||
element:
|
||||
StampElement {
|
||||
handle,
|
||||
source: element_source,
|
||||
},
|
||||
transform,
|
||||
}) = data
|
||||
else {
|
||||
make_commands_to_erase_brush_page(destination, *p, macro_list, &mut commands);
|
||||
continue;
|
||||
};
|
||||
if let Some(ResourceTilePosition::Page(source_page)) = element_source {
|
||||
let source = source.data_ref();
|
||||
let tile_set = source.tile_set();
|
||||
let mut page_data = source.pages.get(source_page).cloned();
|
||||
if let (Some(tile_set), Some(page_data)) = (tile_set, page_data.as_mut()) {
|
||||
let icon = page_data.icon;
|
||||
let icon = tile_set
|
||||
.data_ref()
|
||||
.get_transformed_version(*transform, icon)
|
||||
.unwrap_or(icon);
|
||||
page_data.icon = icon;
|
||||
}
|
||||
if same_brush {
|
||||
drop(source);
|
||||
make_commands_to_copy_brush_page(
|
||||
destination,
|
||||
*source_page,
|
||||
*p,
|
||||
macro_list,
|
||||
&mut commands,
|
||||
);
|
||||
}
|
||||
commands.push(SetBrushPageCommand {
|
||||
brush: destination.clone(),
|
||||
position: *p,
|
||||
page: page_data,
|
||||
});
|
||||
} else {
|
||||
let source = source.data_ref();
|
||||
let icon = if let Some(tile_set) = source.tile_set() {
|
||||
tile_set
|
||||
.data_ref()
|
||||
.get_transformed_version(*transform, *handle)
|
||||
.unwrap_or(*handle)
|
||||
} else {
|
||||
*handle
|
||||
};
|
||||
commands.push(ModifyBrushPageIconCommand::new(
|
||||
destination.clone(),
|
||||
*p,
|
||||
icon,
|
||||
));
|
||||
}
|
||||
}
|
||||
if !commands.is_empty() {
|
||||
sender.do_command(commands.with_custom_name("Edit Tile Set Pages"));
|
||||
}
|
||||
}
|
||||
|
||||
fn send_update_brush_icons(
|
||||
tile_set: Option<&TileSetResource>,
|
||||
update: &TransTilesUpdate,
|
||||
destination: &TileMapBrushResource,
|
||||
macro_list: Option<&BrushMacroListRef>,
|
||||
sender: &mut MessageSender,
|
||||
) {
|
||||
let mut commands = CommandGroup::default();
|
||||
for (p, data) in update.iter() {
|
||||
let Some(RotTileHandle {
|
||||
element: StampElement { handle, .. },
|
||||
transform,
|
||||
}) = data
|
||||
else {
|
||||
make_commands_to_erase_brush_page(destination, *p, macro_list, &mut commands);
|
||||
continue;
|
||||
};
|
||||
let icon = if let Some(tile_set) = tile_set {
|
||||
tile_set
|
||||
.data_ref()
|
||||
.get_transformed_version(*transform, *handle)
|
||||
.unwrap_or(*handle)
|
||||
} else {
|
||||
*handle
|
||||
};
|
||||
commands.push(ModifyBrushPageIconCommand::new(
|
||||
destination.clone(),
|
||||
*p,
|
||||
icon,
|
||||
));
|
||||
}
|
||||
if !commands.is_empty() {
|
||||
sender.do_command(commands.with_custom_name("Edit Tile Set Pages"));
|
||||
}
|
||||
}
|
||||
|
||||
fn make_commands_to_erase_brush_page(
|
||||
brush: &TileMapBrushResource,
|
||||
page: Vector2<i32>,
|
||||
macro_list: Option<&BrushMacroListRef>,
|
||||
commands: &mut CommandGroup,
|
||||
) {
|
||||
if let Some(macro_list) = macro_list {
|
||||
let macro_list = macro_list.lock();
|
||||
for instance in brush.data_ref().macros.iter() {
|
||||
if let Some(m) = macro_list.get_by_uuid(&instance.macro_id) {
|
||||
if let Some(command) = m.copy_page(
|
||||
None,
|
||||
page,
|
||||
&BrushMacroInstance {
|
||||
brush: brush.clone(),
|
||||
settings: instance.settings.clone(),
|
||||
},
|
||||
) {
|
||||
commands.push_command(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
commands.push(SetBrushPageCommand {
|
||||
brush: brush.clone(),
|
||||
position: page,
|
||||
page: None,
|
||||
});
|
||||
}
|
||||
|
||||
fn make_commands_to_copy_brush_page(
|
||||
brush: &TileMapBrushResource,
|
||||
from: Vector2<i32>,
|
||||
to: Vector2<i32>,
|
||||
macro_list: Option<&BrushMacroListRef>,
|
||||
commands: &mut CommandGroup,
|
||||
) {
|
||||
if let Some(macro_list) = macro_list {
|
||||
let macro_list = macro_list.lock();
|
||||
for instance in brush.data_ref().macros.iter() {
|
||||
if let Some(m) = macro_list.get_by_uuid(&instance.macro_id) {
|
||||
if let Some(command) = m.copy_page(
|
||||
Some(from),
|
||||
to,
|
||||
&BrushMacroInstance {
|
||||
brush: brush.clone(),
|
||||
settings: instance.settings.clone(),
|
||||
},
|
||||
) {
|
||||
commands.push_command(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_commands_to_copy_brush_tile(
|
||||
brush: &TileMapBrushResource,
|
||||
from: Option<TileDefinitionHandle>,
|
||||
to: TileDefinitionHandle,
|
||||
macro_list: Option<&BrushMacroListRef>,
|
||||
commands: &mut CommandGroup,
|
||||
) {
|
||||
if let Some(macro_list) = macro_list {
|
||||
let macro_list = macro_list.lock();
|
||||
for instance in brush.data_ref().macros.iter() {
|
||||
if let Some(m) = macro_list.get_by_uuid(&instance.macro_id) {
|
||||
if let Some(command) = m.copy_cell(
|
||||
from,
|
||||
to,
|
||||
&BrushMacroInstance {
|
||||
brush: brush.clone(),
|
||||
settings: instance.settings.clone(),
|
||||
},
|
||||
) {
|
||||
commands.push_command(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PaletteWidget {
|
||||
/// Each brush and tile set has two palette areas: the pages and the tiles within each page.
|
||||
/// These two areas are called stages, and each of the two stages needs to be handled separately.
|
||||
@@ -393,10 +709,7 @@ impl PaletteWidget {
|
||||
fn sync_to_state(&mut self, ui: &mut UserInterface) {
|
||||
let state = self.state.lock();
|
||||
let drawing_mode = if self.editable {
|
||||
match self.kind {
|
||||
TilePaletteStage::Pages => DrawingMode::Pick,
|
||||
TilePaletteStage::Tiles => state.drawing_mode,
|
||||
}
|
||||
state.drawing_mode
|
||||
} else {
|
||||
DrawingMode::Pick
|
||||
};
|
||||
@@ -437,7 +750,7 @@ impl PaletteWidget {
|
||||
.push(ColliderHighlight::new(pos, color, tile_collider.clone()));
|
||||
});
|
||||
}
|
||||
if self.editable && self.kind == TilePaletteStage::Tiles {
|
||||
if self.editable {
|
||||
self.overlay.clear();
|
||||
self.highlight.clear();
|
||||
match state.drawing_mode {
|
||||
@@ -525,11 +838,37 @@ impl PaletteWidget {
|
||||
Vector2::new((pos.x / s.x).floor() as i32, (pos.y / s.y).floor() as i32)
|
||||
}
|
||||
fn send_update(&mut self) {
|
||||
match self.kind {
|
||||
TilePaletteStage::Pages => self.send_update_pages(),
|
||||
TilePaletteStage::Tiles => self.send_update_tiles(),
|
||||
}
|
||||
}
|
||||
fn send_update_pages(&mut self) {
|
||||
assert_eq!(self.kind, TilePaletteStage::Pages);
|
||||
match &self.content {
|
||||
TileBook::Empty => (),
|
||||
TileBook::TileSet(tile_set) => {
|
||||
send_update_tile_set_pages(tile_set, &self.update, &self.state, &mut self.sender);
|
||||
}
|
||||
TileBook::Brush(brush) => {
|
||||
send_update_brush_pages(
|
||||
brush,
|
||||
&self.update,
|
||||
&self.state,
|
||||
self.macro_list.as_ref(),
|
||||
&mut self.sender,
|
||||
);
|
||||
}
|
||||
}
|
||||
self.update.clear();
|
||||
}
|
||||
fn send_update_tiles(&mut self) {
|
||||
assert_eq!(self.kind, TilePaletteStage::Tiles);
|
||||
let Some(page) = self.page else {
|
||||
return;
|
||||
};
|
||||
let state = self.state.lock();
|
||||
let source = state.stamp.source();
|
||||
let source_set = state.tile_set.as_ref();
|
||||
match &self.content {
|
||||
TileBook::Empty => (),
|
||||
@@ -553,12 +892,37 @@ impl PaletteWidget {
|
||||
.cloned()
|
||||
.or_else(|| resource.state().data()?.tile_set())
|
||||
{
|
||||
let mut commands = CommandGroup::default();
|
||||
let same_brush = if let Some(TileBook::Brush(brush)) = source.as_ref() {
|
||||
brush == resource
|
||||
} else {
|
||||
false
|
||||
};
|
||||
for (p, d) in self.update.iter() {
|
||||
let from = if same_brush {
|
||||
d.as_ref().and_then(|d| d.element.source?.handle())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let Some(to) = TileDefinitionHandle::try_new(page, *p) else {
|
||||
continue;
|
||||
};
|
||||
make_commands_to_copy_brush_tile(
|
||||
resource,
|
||||
from,
|
||||
to,
|
||||
self.macro_list.as_ref(),
|
||||
&mut commands,
|
||||
);
|
||||
}
|
||||
let mut source_set = TileSetRef::new(&source_set);
|
||||
self.sender.do_command(SetBrushTilesCommand {
|
||||
commands.push(SetBrushTilesCommand {
|
||||
brush: resource.clone(),
|
||||
page,
|
||||
tiles: self.update.build_tiles_update(&source_set.as_loaded()),
|
||||
});
|
||||
self.sender
|
||||
.do_command(commands.with_custom_name("Edit Brush Tiles"));
|
||||
}
|
||||
self.update.clear();
|
||||
}
|
||||
@@ -579,46 +943,13 @@ impl PaletteWidget {
|
||||
if state.selection_palette() != self.handle || !state.has_selection() {
|
||||
return false;
|
||||
}
|
||||
match self.kind {
|
||||
TilePaletteStage::Pages => {
|
||||
let sel = state
|
||||
.selection_positions()
|
||||
.iter()
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
drop(state);
|
||||
let commands = sel
|
||||
.into_iter()
|
||||
.filter_map(|p| self.delete_page(p))
|
||||
.collect::<Vec<_>>();
|
||||
self.sender
|
||||
.do_command(CommandGroup::from(commands).with_custom_name("Delete Pages"));
|
||||
}
|
||||
TilePaletteStage::Tiles => {
|
||||
for position in state.selection_positions() {
|
||||
self.update.insert(*position, None);
|
||||
}
|
||||
drop(state);
|
||||
self.send_update();
|
||||
}
|
||||
for position in state.selection_positions() {
|
||||
self.update.insert(*position, None);
|
||||
}
|
||||
drop(state);
|
||||
self.send_update();
|
||||
true
|
||||
}
|
||||
fn delete_page(&mut self, position: Vector2<i32>) -> Option<Command> {
|
||||
match &self.content {
|
||||
TileBook::Empty => None,
|
||||
TileBook::TileSet(tile_set) => Some(Command::new(SetTileSetPageCommand {
|
||||
tile_set: tile_set.clone(),
|
||||
position,
|
||||
page: None,
|
||||
})),
|
||||
TileBook::Brush(brush) => Some(Command::new(SetBrushPageCommand {
|
||||
brush: brush.clone(),
|
||||
position,
|
||||
page: None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
fn set_page(&mut self, resource: TileBook, page: Option<Vector2<i32>>, ui: &mut UserInterface) {
|
||||
let mut state = self.state.lock_mut("set_page");
|
||||
if state.selection_palette() == self.handle {
|
||||
@@ -641,21 +972,21 @@ impl PaletteWidget {
|
||||
}
|
||||
fn drawing_mode(&self) -> DrawingMode {
|
||||
if self.editable {
|
||||
match self.kind {
|
||||
TilePaletteStage::Pages => DrawingMode::Pick,
|
||||
TilePaletteStage::Tiles => self.current_tool,
|
||||
}
|
||||
self.current_tool
|
||||
} else {
|
||||
DrawingMode::Pick
|
||||
}
|
||||
}
|
||||
fn update_stamp(&self, state: &mut TileDrawStateGuardMut) {
|
||||
let page = self.page.unwrap_or_default();
|
||||
let brush = self.content.brush_ref().cloned();
|
||||
state.update_stamp(brush, self.content.get_tile_set(), |p| {
|
||||
self.content
|
||||
.get_stamp_element(ResourceTilePosition::new(self.stage(), page, p))
|
||||
});
|
||||
state.update_stamp(
|
||||
Some(self.content.clone()),
|
||||
self.content.get_tile_set(),
|
||||
|p| {
|
||||
self.content
|
||||
.get_stamp_element(ResourceTilePosition::new(self.stage(), page, p))
|
||||
},
|
||||
);
|
||||
}
|
||||
/// After the data changes in the tile set or the brush that the widget is displaying,
|
||||
/// call this method to rebuild the stamp from the currently selected tiles. This is necessary
|
||||
@@ -897,8 +1228,29 @@ impl PaletteWidget {
|
||||
.do_command(MoveTileSetPageCommand::new(tile_set, pages, offset));
|
||||
}
|
||||
TileBook::Brush(brush) => {
|
||||
let mut commands = CommandGroup::default();
|
||||
let from = pages.clone().into_boxed_slice();
|
||||
let to = pages.iter().map(|p| *p + offset).collect::<Box<_>>();
|
||||
if let Some(macro_list) = self.macro_list.as_ref() {
|
||||
let macro_list = macro_list.lock();
|
||||
for instance in brush.data_ref().macros.iter() {
|
||||
if let Some(m) = macro_list.get_by_uuid(&instance.macro_id) {
|
||||
if let Some(command) = m.move_pages(
|
||||
from.clone(),
|
||||
to.clone(),
|
||||
&BrushMacroInstance {
|
||||
brush: brush.clone(),
|
||||
settings: instance.settings.clone(),
|
||||
},
|
||||
) {
|
||||
commands.push_command(command)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
commands.push(MoveBrushPageCommand::new(brush, pages, offset));
|
||||
self.sender
|
||||
.do_command(MoveBrushPageCommand::new(brush, pages, offset));
|
||||
.do_command(commands.with_custom_name("Move Brush Pages"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -923,13 +1275,40 @@ impl PaletteWidget {
|
||||
.do_command(MoveTileSetTileCommand::new(tile_set, page, tiles, offset));
|
||||
}
|
||||
TileBook::Brush(brush) => {
|
||||
let mut commands = CommandGroup::default();
|
||||
let data = brush.data_ref();
|
||||
let tiles = tiles
|
||||
.filter(|p| data.has_tile_at(page, *p))
|
||||
.collect::<Vec<_>>();
|
||||
let from = tiles
|
||||
.iter()
|
||||
.filter_map(|p| TileDefinitionHandle::try_new(page, *p))
|
||||
.collect::<Box<_>>();
|
||||
let to = tiles
|
||||
.iter()
|
||||
.filter_map(|p| TileDefinitionHandle::try_new(page, *p + offset))
|
||||
.collect::<Box<_>>();
|
||||
if let Some(macro_list) = self.macro_list.as_ref() {
|
||||
let macro_list = macro_list.lock();
|
||||
for instance in data.macros.iter() {
|
||||
if let Some(m) = macro_list.get_by_uuid(&instance.macro_id) {
|
||||
if let Some(command) = m.move_cells(
|
||||
from.clone(),
|
||||
to.clone(),
|
||||
&BrushMacroInstance {
|
||||
brush: brush.clone(),
|
||||
settings: instance.settings.clone(),
|
||||
},
|
||||
) {
|
||||
commands.push_command(command)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(data);
|
||||
commands.push(MoveBrushTileCommand::new(brush, page, tiles, offset));
|
||||
self.sender
|
||||
.do_command(MoveBrushTileCommand::new(brush, page, tiles, offset));
|
||||
.do_command(commands.with_custom_name("Move Brush Tiles"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1297,10 +1676,7 @@ impl PaletteWidget {
|
||||
}
|
||||
fn is_overlay_visible(&self) -> bool {
|
||||
let drawing_mode = if self.editable {
|
||||
match self.kind {
|
||||
TilePaletteStage::Pages => DrawingMode::Pick,
|
||||
TilePaletteStage::Tiles => self.state.lock().drawing_mode,
|
||||
}
|
||||
self.state.lock().drawing_mode
|
||||
} else {
|
||||
DrawingMode::Pick
|
||||
};
|
||||
@@ -1497,10 +1873,7 @@ impl Control for PaletteWidget {
|
||||
self.commit_color(CURSOR_HIGHLIGHT_COLOR, ctx);
|
||||
}
|
||||
|
||||
if self.editable
|
||||
&& self.kind == TilePaletteStage::Tiles
|
||||
&& self.state.lock().drawing_mode == DrawingMode::Erase
|
||||
{
|
||||
if self.editable && self.state.lock().drawing_mode == DrawingMode::Erase {
|
||||
self.push_erase_area(line_thickness, ctx);
|
||||
self.commit_color(Color::RED, ctx);
|
||||
}
|
||||
@@ -1632,6 +2005,7 @@ pub struct PaletteWidgetBuilder {
|
||||
sender: MessageSender,
|
||||
state: TileDrawStateRef,
|
||||
macro_cells: Option<MacroCellSetListRef>,
|
||||
macro_list: Option<BrushMacroListRef>,
|
||||
kind: TilePaletteStage,
|
||||
editable: bool,
|
||||
}
|
||||
@@ -1651,6 +2025,7 @@ impl PaletteWidgetBuilder {
|
||||
sender,
|
||||
state,
|
||||
macro_cells: None,
|
||||
macro_list: None,
|
||||
kind: TilePaletteStage::default(),
|
||||
editable: false,
|
||||
page: None,
|
||||
@@ -1693,6 +2068,13 @@ impl PaletteWidgetBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Giving the palette access to a list of macro cells allows it to draw outlines
|
||||
/// around cells that are involved in some macros.
|
||||
pub fn with_macro_list(mut self, macro_list: BrushMacroListRef) -> Self {
|
||||
self.macro_list = Some(macro_list);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`PaletteWidget`].
|
||||
pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
|
||||
ctx.add_node(UiNode::new(PaletteWidget {
|
||||
@@ -1704,6 +2086,7 @@ impl PaletteWidgetBuilder {
|
||||
sender: self.sender,
|
||||
state: self.state,
|
||||
macro_cells: self.macro_cells,
|
||||
macro_list: self.macro_list,
|
||||
overlay: PaletteOverlay::default(),
|
||||
content: self.tile_book,
|
||||
kind: self.kind,
|
||||
|
||||
@@ -1181,20 +1181,12 @@ impl TileInspector {
|
||||
TileBook::Empty => return,
|
||||
TileBook::TileSet(tile_set) => state
|
||||
.page_positions()
|
||||
.map(|position| ModifyPageIconCommand {
|
||||
tile_set: tile_set.clone(),
|
||||
page: position,
|
||||
icon,
|
||||
})
|
||||
.map(|position| ModifyPageIconCommand::new(tile_set.clone(), position, icon))
|
||||
.map(Command::new)
|
||||
.collect::<Vec<_>>(),
|
||||
TileBook::Brush(brush) => state
|
||||
.page_positions()
|
||||
.map(|position| ModifyBrushPageIconCommand {
|
||||
brush: brush.clone(),
|
||||
page: position,
|
||||
icon,
|
||||
})
|
||||
.map(|position| ModifyBrushPageIconCommand::new(brush.clone(), position, icon))
|
||||
.map(Command::new)
|
||||
.collect::<Vec<_>>(),
|
||||
};
|
||||
|
||||
@@ -292,6 +292,7 @@ impl TileSetEditor {
|
||||
.with_resource(tile_book.clone())
|
||||
.with_kind(TilePaletteStage::Pages)
|
||||
.with_editable(true)
|
||||
.with_macro_list(macro_list.clone())
|
||||
.build(ctx);
|
||||
|
||||
let tiles_palette = PaletteWidgetBuilder::new(
|
||||
@@ -305,6 +306,7 @@ impl TileSetEditor {
|
||||
.with_resource(tile_book.clone())
|
||||
.with_kind(TilePaletteStage::Tiles)
|
||||
.with_editable(true)
|
||||
.with_macro_list(macro_list.clone())
|
||||
.with_macro_cells(brush_macro_cell_sets.clone())
|
||||
.build(ctx);
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use fyrox::{
|
||||
asset::{
|
||||
untyped::{ResourceKind, UntypedResource},
|
||||
@@ -45,7 +47,7 @@ use fyrox::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command::{Command, CommandContext, CommandTrait},
|
||||
command::{Command, CommandContext, CommandGroup, CommandTrait},
|
||||
send_sync_message,
|
||||
};
|
||||
|
||||
@@ -399,7 +401,7 @@ impl BrushMacro for WfcMacro {
|
||||
cell_set.extend(data.cells.iter());
|
||||
}
|
||||
|
||||
fn create_cell(&self, context: &BrushMacroCell) -> Option<Command> {
|
||||
fn create_cell(&self, context: &BrushMacroCellContext) -> Option<Command> {
|
||||
let instance = context.settings()?;
|
||||
let cell = context.cell?;
|
||||
Some(Command::new(SetCellCommand {
|
||||
@@ -410,17 +412,99 @@ impl BrushMacro for WfcMacro {
|
||||
}))
|
||||
}
|
||||
|
||||
fn remove_cell(&self, context: &BrushMacroCell) -> Option<Command> {
|
||||
fn move_cells(
|
||||
&self,
|
||||
from: Box<[TileDefinitionHandle]>,
|
||||
to: Box<[TileDefinitionHandle]>,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command> {
|
||||
let instance = context.settings()?;
|
||||
let cell = context.cell?;
|
||||
Some(Command::new(MoveCellsCommand::new(
|
||||
context.brush.clone(),
|
||||
from,
|
||||
to,
|
||||
instance,
|
||||
)))
|
||||
}
|
||||
|
||||
fn move_pages(
|
||||
&self,
|
||||
from: Box<[Vector2<i32>]>,
|
||||
to: Box<[Vector2<i32>]>,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command> {
|
||||
let instance = context.settings()?;
|
||||
Some(Command::new(MovePagesCommand::new(
|
||||
context.brush.clone(),
|
||||
from,
|
||||
to,
|
||||
instance,
|
||||
)))
|
||||
}
|
||||
|
||||
fn copy_cell(
|
||||
&self,
|
||||
source: Option<TileDefinitionHandle>,
|
||||
destination: TileDefinitionHandle,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command> {
|
||||
let instance = context.settings::<WfcInstance>()?;
|
||||
let guard = instance.data_ref();
|
||||
let included = if let Some(source) = source {
|
||||
guard.cells.contains(&source)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
Some(Command::new(SetCellCommand {
|
||||
brush: context.brush.clone(),
|
||||
cell,
|
||||
instance,
|
||||
included: false,
|
||||
cell: destination,
|
||||
instance: instance.clone(),
|
||||
included,
|
||||
}))
|
||||
}
|
||||
|
||||
fn copy_page(
|
||||
&self,
|
||||
source: Option<Vector2<i32>>,
|
||||
destination: Vector2<i32>,
|
||||
context: &BrushMacroInstance,
|
||||
) -> Option<Command> {
|
||||
let instance = context.settings::<WfcInstance>()?;
|
||||
let guard = instance.data_ref();
|
||||
let used = guard
|
||||
.cells
|
||||
.iter()
|
||||
.filter(|h| Some(h.page()) == source)
|
||||
.map(|h| h.tile())
|
||||
.collect::<Vec<_>>();
|
||||
let commands = guard
|
||||
.cells
|
||||
.iter()
|
||||
.filter_map(|handle| {
|
||||
if Some(handle.page()) == source {
|
||||
let cell = TileDefinitionHandle::try_new(destination, handle.tile())?;
|
||||
Some(Command::new(SetCellCommand {
|
||||
brush: context.brush.clone(),
|
||||
cell,
|
||||
instance: instance.clone(),
|
||||
included: true,
|
||||
}))
|
||||
} else if handle.page() == destination && !used.contains(&handle.tile()) {
|
||||
Some(Command::new(SetCellCommand {
|
||||
brush: context.brush.clone(),
|
||||
cell: *handle,
|
||||
instance: instance.clone(),
|
||||
included: false,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
(!commands.is_empty())
|
||||
.then(|| Command::new(CommandGroup::from(commands).with_custom_name("Set Page Macros")))
|
||||
}
|
||||
|
||||
fn build_instance_editor(
|
||||
&mut self,
|
||||
context: &BrushMacroInstance,
|
||||
@@ -550,7 +634,7 @@ impl BrushMacro for WfcMacro {
|
||||
|
||||
fn build_cell_editor(
|
||||
&mut self,
|
||||
_context: &BrushMacroCell,
|
||||
_context: &BrushMacroCellContext,
|
||||
_ctx: &mut BuildContext,
|
||||
) -> Option<Handle<UiNode>> {
|
||||
None
|
||||
@@ -663,13 +747,11 @@ impl BrushMacro for WfcMacro {
|
||||
let mut propagator = TileSetWfcPropagator::default();
|
||||
propagator.fill_from(constraint.deref());
|
||||
for (&p, v) in update.iter() {
|
||||
if let Some(StampElement {
|
||||
brush_cell: Some(cell),
|
||||
..
|
||||
}) = v
|
||||
{
|
||||
if instance.cells.contains(cell) {
|
||||
propagator.add_cell(p);
|
||||
if let Some(StampElement { source, .. }) = v {
|
||||
if let Some(cell) = source.and_then(|s| s.handle()) {
|
||||
if instance.cells.contains(&cell) {
|
||||
propagator.add_cell(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -737,6 +819,142 @@ impl CommandTrait for SetCellCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MoveCellsCommand {
|
||||
pub brush: TileMapBrushResource,
|
||||
pub from: Box<[TileDefinitionHandle]>,
|
||||
pub to: Box<[TileDefinitionHandle]>,
|
||||
pub instance: Resource<WfcInstance>,
|
||||
data: Box<[bool]>,
|
||||
}
|
||||
|
||||
impl MoveCellsCommand {
|
||||
fn new(
|
||||
brush: TileMapBrushResource,
|
||||
from: Box<[TileDefinitionHandle]>,
|
||||
to: Box<[TileDefinitionHandle]>,
|
||||
instance: Resource<WfcInstance>,
|
||||
) -> Self {
|
||||
Self {
|
||||
data: vec![false; from.len()].into_boxed_slice(),
|
||||
brush,
|
||||
from,
|
||||
to,
|
||||
instance,
|
||||
}
|
||||
}
|
||||
fn swap(&mut self) {
|
||||
let mut instance = self.instance.data_ref();
|
||||
for (&from, data) in self.from.iter().zip(self.data.iter_mut()) {
|
||||
swap_set_entry(&mut instance.cells, from, data);
|
||||
}
|
||||
for (&to, data) in self.to.iter().zip(self.data.iter_mut()) {
|
||||
swap_set_entry(&mut instance.cells, to, data);
|
||||
}
|
||||
std::mem::swap(&mut self.from, &mut self.to);
|
||||
self.brush.data_ref().change_flag.set();
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_set_entry<T: Hash + Eq>(set: &mut FxHashSet<T>, value: T, contains: &mut bool) {
|
||||
let old_contains = set.contains(&value);
|
||||
if old_contains == *contains {
|
||||
return;
|
||||
};
|
||||
if *contains {
|
||||
set.insert(value);
|
||||
} else {
|
||||
set.remove(&value);
|
||||
}
|
||||
*contains = old_contains;
|
||||
}
|
||||
|
||||
impl CommandTrait for MoveCellsCommand {
|
||||
fn name(&mut self, _context: &dyn CommandContext) -> String {
|
||||
"Update Autotile Cell".into()
|
||||
}
|
||||
|
||||
fn execute(&mut self, _context: &mut dyn CommandContext) {
|
||||
self.swap();
|
||||
}
|
||||
|
||||
fn revert(&mut self, _context: &mut dyn CommandContext) {
|
||||
self.swap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MovePagesCommand {
|
||||
pub brush: TileMapBrushResource,
|
||||
pub from: Box<[Vector2<i32>]>,
|
||||
pub to: Box<[Vector2<i32>]>,
|
||||
pub instance: Resource<WfcInstance>,
|
||||
data: Box<[FxHashSet<Vector2<i32>>]>,
|
||||
}
|
||||
|
||||
impl MovePagesCommand {
|
||||
fn new(
|
||||
brush: TileMapBrushResource,
|
||||
from: Box<[Vector2<i32>]>,
|
||||
to: Box<[Vector2<i32>]>,
|
||||
instance: Resource<WfcInstance>,
|
||||
) -> Self {
|
||||
Self {
|
||||
data: vec![FxHashSet::default(); from.len()].into_boxed_slice(),
|
||||
brush,
|
||||
from,
|
||||
to,
|
||||
instance,
|
||||
}
|
||||
}
|
||||
fn swap(&mut self) {
|
||||
let mut instance = self.instance.data_ref();
|
||||
for (&from, data) in self.from.iter().zip(self.data.iter_mut()) {
|
||||
swap_page(&mut instance.cells, from, data);
|
||||
}
|
||||
for (&to, data) in self.to.iter().zip(self.data.iter_mut()) {
|
||||
swap_page(&mut instance.cells, to, data);
|
||||
}
|
||||
std::mem::swap(&mut self.from, &mut self.to);
|
||||
self.brush.data_ref().change_flag.set();
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_page(
|
||||
set: &mut FxHashSet<TileDefinitionHandle>,
|
||||
position: Vector2<i32>,
|
||||
data: &mut FxHashSet<Vector2<i32>>,
|
||||
) {
|
||||
let mut new_data = FxHashSet::default();
|
||||
set.retain(|h| {
|
||||
if h.page() != position {
|
||||
true
|
||||
} else {
|
||||
new_data.insert(h.tile());
|
||||
false
|
||||
}
|
||||
});
|
||||
set.extend(
|
||||
data.drain()
|
||||
.filter_map(|p| TileDefinitionHandle::try_new(position, p)),
|
||||
);
|
||||
*data = new_data;
|
||||
}
|
||||
|
||||
impl CommandTrait for MovePagesCommand {
|
||||
fn name(&mut self, _context: &dyn CommandContext) -> String {
|
||||
"Update Autotile Cell".into()
|
||||
}
|
||||
|
||||
fn execute(&mut self, _context: &mut dyn CommandContext) {
|
||||
self.swap();
|
||||
}
|
||||
|
||||
fn revert(&mut self, _context: &mut dyn CommandContext) {
|
||||
self.swap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SetPatternPropCommand {
|
||||
pub brush: TileMapBrushResource,
|
||||
|
||||
@@ -325,15 +325,11 @@ impl TileSetAutoTiler {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let brush_cell = update
|
||||
.get(pos)
|
||||
.cloned()
|
||||
.flatten()
|
||||
.and_then(|el| el.brush_cell);
|
||||
let source = update.get(pos).cloned().flatten().and_then(|el| el.source);
|
||||
let handle = if handle.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(StampElement { handle, brush_cell })
|
||||
Some(StampElement { handle, source })
|
||||
};
|
||||
_ = update.insert(*pos, handle);
|
||||
}
|
||||
@@ -492,15 +488,11 @@ impl TileSetWfcPropagator {
|
||||
let Some(&handle) = value_map.get_random(rng, pat) else {
|
||||
continue;
|
||||
};
|
||||
let brush_cell = update
|
||||
.get(pos)
|
||||
.cloned()
|
||||
.flatten()
|
||||
.and_then(|el| el.brush_cell);
|
||||
let source = update.get(pos).cloned().flatten().and_then(|el| el.source);
|
||||
let handle = if handle.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(StampElement { handle, brush_cell })
|
||||
Some(StampElement { handle, source })
|
||||
};
|
||||
_ = update.insert(*pos, handle);
|
||||
}
|
||||
|
||||
@@ -494,16 +494,19 @@ impl TileMapBrush {
|
||||
|
||||
/// The stamp element for the given position, if the tile in that cell is used
|
||||
/// to create a stamp. The [`StampElement::handle`] refers to the location of the tile within the
|
||||
/// tile set, while the [`StampElement::brush_cell`] refers to the location of the tile within
|
||||
/// tile set, while the [`StampElement::source`] refers to the location of the tile within
|
||||
/// the brush.
|
||||
pub fn stamp_element(&self, position: ResourceTilePosition) -> Option<StampElement> {
|
||||
match position.stage() {
|
||||
TilePaletteStage::Pages => self.redirect_handle(position).map(|h| h.into()),
|
||||
TilePaletteStage::Pages => self.redirect_handle(position).map(|handle| StampElement {
|
||||
handle,
|
||||
source: Some(position),
|
||||
}),
|
||||
TilePaletteStage::Tiles => {
|
||||
let page = self.pages.get(&position.page())?;
|
||||
Some(StampElement {
|
||||
handle: *page.tiles.get(&position.stage_position())?,
|
||||
brush_cell: position.handle(),
|
||||
source: Some(position),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,6 +409,17 @@ pub enum ResourceTilePosition {
|
||||
Tile(Vector2<i32>, Vector2<i32>),
|
||||
}
|
||||
|
||||
impl Display for ResourceTilePosition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ResourceTilePosition::Page(p) => write!(f, "Page({},{})", p.x, p.y),
|
||||
ResourceTilePosition::Tile(page, pos) => {
|
||||
write!(f, "({},{}):({},{})", page.x, page.y, pos.x, pos.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TileDefinitionHandle> for ResourceTilePosition {
|
||||
fn from(value: TileDefinitionHandle) -> Self {
|
||||
Self::Tile(value.page(), value.tile())
|
||||
@@ -725,19 +736,14 @@ impl TileBook {
|
||||
TileBook::Brush(r) => r.state().data()?.redirect_handle(position),
|
||||
}
|
||||
}
|
||||
/// The StampElement for the given position in this resource. For tile sets the element's
|
||||
/// [`StampElement::brush_cell`] is None.
|
||||
/// For brushes, the [`StampElement::handle`] refers to the location of the tile within the
|
||||
/// tile set, while the [`StampElement::brush_cell`] refers to the location of the tile within
|
||||
/// the brush.
|
||||
/// The StampElement for the given position in this resource.
|
||||
/// For brushes, the [`StampElement::handle`] refers to the ultimate location of the tile within the
|
||||
/// tile set, while the [`StampElement::source`] refers to the location of the tile within
|
||||
/// the brush or tile set that was used to create the stamp.
|
||||
pub fn get_stamp_element(&self, position: ResourceTilePosition) -> Option<StampElement> {
|
||||
match self {
|
||||
TileBook::Empty => None,
|
||||
TileBook::TileSet(r) => r
|
||||
.state()
|
||||
.data()?
|
||||
.redirect_handle(position)
|
||||
.map(|h| h.into()),
|
||||
TileBook::TileSet(r) => r.state().data()?.stamp_element(position),
|
||||
TileBook::Brush(r) => r.state().data()?.stamp_element(position),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ impl TileRegion {
|
||||
/// A trait for types that can produce a TileDefinitionHandle upon demand,
|
||||
/// for use with drawing on tilemaps.
|
||||
pub trait TileSource {
|
||||
/// The brush where these tiles were originally taken from.
|
||||
/// The source where these tiles were originally taken from.
|
||||
fn brush(&self) -> Option<&TileMapBrushResource>;
|
||||
/// The transformation that should be applied to the tiles before they are written.
|
||||
fn transformation(&self) -> OrthoTransformation;
|
||||
@@ -376,7 +376,7 @@ pub struct Stamp {
|
||||
#[visit(skip)]
|
||||
elements: OrthoTransformMap<StampElement>,
|
||||
#[visit(skip)]
|
||||
brush: Option<TileMapBrushResource>,
|
||||
source: Option<TileBook>,
|
||||
}
|
||||
|
||||
/// Each cell of a stamp must have a tile handle and it may optionally have
|
||||
@@ -386,14 +386,14 @@ pub struct StampElement {
|
||||
/// The stamp cell's tile handle
|
||||
pub handle: TileDefinitionHandle,
|
||||
/// The brush cell that this stamp element came from.
|
||||
pub brush_cell: Option<TileDefinitionHandle>,
|
||||
pub source: Option<ResourceTilePosition>,
|
||||
}
|
||||
|
||||
impl From<TileDefinitionHandle> for StampElement {
|
||||
fn from(handle: TileDefinitionHandle) -> Self {
|
||||
Self {
|
||||
handle,
|
||||
brush_cell: None,
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -432,7 +432,7 @@ impl DerefMut for Tiles {
|
||||
|
||||
impl TileSource for Stamp {
|
||||
fn brush(&self) -> Option<&TileMapBrushResource> {
|
||||
self.brush.as_ref()
|
||||
self.source.as_ref().and_then(|s| s.brush_ref())
|
||||
}
|
||||
fn transformation(&self) -> OrthoTransformation {
|
||||
self.transform
|
||||
@@ -443,6 +443,10 @@ impl TileSource for Stamp {
|
||||
}
|
||||
|
||||
impl Stamp {
|
||||
/// The resource where this stamp was taken from.
|
||||
pub fn source(&self) -> Option<&TileBook> {
|
||||
self.source.as_ref()
|
||||
}
|
||||
/// Iterate over the tile handles of the stamp.
|
||||
pub fn tile_iter(&self) -> impl Iterator<Item = TileDefinitionHandle> + '_ {
|
||||
self.elements.values().map(|s| s.handle)
|
||||
@@ -479,10 +483,10 @@ impl Stamp {
|
||||
/// The transform is set to identity.
|
||||
pub fn build<I: Iterator<Item = (Vector2<i32>, StampElement)> + Clone>(
|
||||
&mut self,
|
||||
brush: Option<TileMapBrushResource>,
|
||||
book: Option<TileBook>,
|
||||
source: I,
|
||||
) {
|
||||
self.brush = brush;
|
||||
self.source = book;
|
||||
self.clear();
|
||||
let mut rect = OptionTileRect::default();
|
||||
for (p, _) in source.clone() {
|
||||
|
||||
@@ -1345,6 +1345,17 @@ pub struct TileSet {
|
||||
}
|
||||
|
||||
impl TileSet {
|
||||
/// The stamp element for the given position, if the tile in that cell is used
|
||||
/// to create a stamp. The [`StampElement::handle`] refers to the location of the tile within the
|
||||
/// tile set, while the [`StampElement::source`] refers to the location of the tile within
|
||||
/// the brush.
|
||||
pub fn stamp_element(&self, position: ResourceTilePosition) -> Option<StampElement> {
|
||||
self.redirect_handle(position).map(|handle| StampElement {
|
||||
handle,
|
||||
source: Some(position),
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterate all valid tile handles.
|
||||
pub fn all_tiles(&self) -> TileSetHandleIterator {
|
||||
TileSetHandleIterator::new(Some(self.pages.iter()))
|
||||
|
||||
@@ -618,9 +618,9 @@ impl Debug for MacroTilesUpdate {
|
||||
f.write_str("MacroTilesUpdate")?;
|
||||
for (p, v) in self.0.iter() {
|
||||
write!(f, " ({:2},{:2})->", p.x, p.y)?;
|
||||
if let Some(StampElement { handle, brush_cell }) = v {
|
||||
if let Some(StampElement { handle, source }) = v {
|
||||
write!(f, "{handle}")?;
|
||||
if let Some(cell) = brush_cell {
|
||||
if let Some(cell) = source {
|
||||
write!(f, "[{cell}]")?;
|
||||
}
|
||||
} else {
|
||||
@@ -737,7 +737,7 @@ impl TransTilesUpdate {
|
||||
*pos,
|
||||
Some(StampElement {
|
||||
handle,
|
||||
brush_cell: element.brush_cell,
|
||||
source: element.source,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user