class Fill(wx.Panel, OperationUI): def __init__(self, parent: wx.Window, canvas: "EditCanvas", world: "World", options_path: str): wx.Panel.__init__(self, parent) OperationUI.__init__(self, parent, canvas, world, options_path) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) options = self._load_options({}) self._block_define = BlockDefine( self, world.translation_manager, wx.VERTICAL, *(options.get("fill_block_options", []) or [world.world_wrapper.platform]), show_pick_block=True) self._block_click_registered = False self._block_define.Bind(EVT_PICK, self._on_pick_block_button) self._sizer.Add(self._block_define, 1, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self._run_button = wx.Button(self, label="Run Operation") self._run_button.Bind(wx.EVT_BUTTON, self._run_operation) self._sizer.Add(self._run_button, 0, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self.Layout() @property def wx_add_options(self) -> Tuple[int, ...]: return (1, ) def _on_pick_block_button(self, evt): """Set up listening for the block click""" if not self._block_click_registered: self.canvas.Bind(EVT_BOX_CLICK, self._on_pick_block) self._block_click_registered = True evt.Skip() def _on_pick_block(self, evt): self.canvas.Unbind(EVT_BOX_CLICK, handler=self._on_pick_block) self._block_click_registered = False x, y, z = self.canvas.cursor_location self._block_define.universal_block = ( self.world.get_block(x, y, z, self.canvas.dimension), None, ) def _get_fill_block(self): return self._block_define.universal_block[0] def unload(self): self._save_options({ "fill_block": self._get_fill_block(), "fill_block_options": ( self._block_define.platform, self._block_define.version_number, self._block_define.force_blockstate, self._block_define.namespace, self._block_define.block_name, self._block_define.properties, ), }) def _run_operation(self, _): self.canvas.run_operation(lambda: fill( self.world, self.canvas.dimension, self.canvas.selection_group, self._get_fill_block(), ))
class Fill(wx.Panel, DefaultOperationUI): def __init__( self, parent: wx.Window, canvas: "EditCanvas", world: "BaseLevel", options_path: str, ): wx.Panel.__init__(self, parent) DefaultOperationUI.__init__(self, parent, canvas, world, options_path) self.Freeze() self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) options = self._load_options({}) self._block_define = BlockDefine( self, world.translation_manager, wx.VERTICAL, *(options.get("fill_block_options", []) or [world.level_wrapper.platform]), show_pick_block=True) self._block_define.Bind(EVT_PICK, self._on_pick_block_button) self._sizer.Add(self._block_define, 1, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self._run_button = wx.Button(self, label="Run Operation") self._run_button.Bind(wx.EVT_BUTTON, self._run_operation) self._sizer.Add(self._run_button, 0, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self.Layout() self.Thaw() @property def wx_add_options(self) -> Tuple[int, ...]: return (1, ) def _on_pick_block_button(self, evt): """Set up listening for the block click""" self._show_pointer = True def _on_box_click(self): if self._show_pointer: self._show_pointer = False x, y, z = self._pointer.pointer_base self._block_define.universal_block = ( self.world.get_block(x, y, z, self.canvas.dimension), None, ) def _get_fill_block(self): return self._block_define.universal_block[0] def disable(self): self._save_options({ "fill_block": self._get_fill_block(), "fill_block_options": ( self._block_define.platform, self._block_define.version_number, self._block_define.force_blockstate, self._block_define.namespace, self._block_define.block_name, self._block_define.properties, ), }) def _run_operation(self, _): self.canvas.run_operation(lambda: fill( self.world, self.canvas.dimension, self.canvas.selection.selection_group, self._get_fill_block(), ))
def __init__( self, parent: wx.Window, canvas: "EditCanvas", world: "World", options_path: str ): wx.Panel.__init__(self, parent) OperationUI.__init__(self, parent, canvas, world, options_path) self.Freeze() self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) options = self._load_options({}) top_sizer = wx.BoxSizer(wx.HORIZONTAL) self._sizer.Add(top_sizer, 0, wx.EXPAND | wx.ALL, 5) help_button = wx.BitmapButton( self, bitmap=image.icon.tablericons.help.bitmap(22, 22) ) top_sizer.Add(help_button) def on_button(evt): dialog = SimpleDialog(self, "Extra block help.") text = wx.TextCtrl( dialog, value="Blocks in the newer versions of Minecraft support having two blocks in the same location.\n" "This is how the game is able to have water and blocks like fences at the same location.\n" "In the example of waterlogged fences the fence is the first block and the water is the second. Unless it is water the second block is usually just visual.\n" "In Java currently the second block is strictly water but in Bedrock there is no limit on what the second block can be.\n" "It is not possible to set non-water second blocks in the game but this operation enables the use of that feature.\n" "There are a number of different modes which can be selected at the top. A description of how it works will appear.", style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_BESTWRAP, ) dialog.sizer.Add(text, 1, wx.EXPAND) dialog.ShowModal() evt.Skip() help_button.Bind(wx.EVT_BUTTON, on_button) self._mode = wx.Choice(self, choices=list(MODES.keys())) self._mode.SetSelection(0) top_sizer.Add(self._mode, 1, wx.EXPAND | wx.LEFT, 5) self._mode.Bind(wx.EVT_CHOICE, self._on_mode_change) self._mode_description = wx.TextCtrl( self, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_BESTWRAP ) self._sizer.Add(self._mode_description, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) self._mode_description.SetLabel( MODES[self._mode.GetString(self._mode.GetSelection())] ) self._mode_description.Fit() self._block_define = BlockDefine( self, world.world_wrapper.translation_manager, wx.VERTICAL, *(options.get("fill_block_options", []) or [world.world_wrapper.platform]), show_pick_block=True ) self._block_click_registered = False self._block_define.Bind(EVT_PICK_BLOCK, self._on_pick_block_button) self._sizer.Add(self._block_define, 1, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self._run_button = wx.Button(self, label="Run Operation") self._run_button.Bind(wx.EVT_BUTTON, self._run_operation) self._sizer.Add(self._run_button, 0, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self.Layout() self.Thaw()
class Waterlog(wx.Panel, OperationUI): def __init__( self, parent: wx.Window, canvas: "EditCanvas", world: "World", options_path: str ): wx.Panel.__init__(self, parent) OperationUI.__init__(self, parent, canvas, world, options_path) self.Freeze() self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) options = self._load_options({}) top_sizer = wx.BoxSizer(wx.HORIZONTAL) self._sizer.Add(top_sizer, 0, wx.EXPAND | wx.ALL, 5) help_button = wx.BitmapButton( self, bitmap=image.icon.tablericons.help.bitmap(22, 22) ) top_sizer.Add(help_button) def on_button(evt): dialog = SimpleDialog(self, "Extra block help.") text = wx.TextCtrl( dialog, value="Blocks in the newer versions of Minecraft support having two blocks in the same location.\n" "This is how the game is able to have water and blocks like fences at the same location.\n" "In the example of waterlogged fences the fence is the first block and the water is the second. Unless it is water the second block is usually just visual.\n" "In Java currently the second block is strictly water but in Bedrock there is no limit on what the second block can be.\n" "It is not possible to set non-water second blocks in the game but this operation enables the use of that feature.\n" "There are a number of different modes which can be selected at the top. A description of how it works will appear.", style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_BESTWRAP, ) dialog.sizer.Add(text, 1, wx.EXPAND) dialog.ShowModal() evt.Skip() help_button.Bind(wx.EVT_BUTTON, on_button) self._mode = wx.Choice(self, choices=list(MODES.keys())) self._mode.SetSelection(0) top_sizer.Add(self._mode, 1, wx.EXPAND | wx.LEFT, 5) self._mode.Bind(wx.EVT_CHOICE, self._on_mode_change) self._mode_description = wx.TextCtrl( self, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_BESTWRAP ) self._sizer.Add(self._mode_description, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) self._mode_description.SetLabel( MODES[self._mode.GetString(self._mode.GetSelection())] ) self._mode_description.Fit() self._block_define = BlockDefine( self, world.world_wrapper.translation_manager, wx.VERTICAL, *(options.get("fill_block_options", []) or [world.world_wrapper.platform]), show_pick_block=True ) self._block_click_registered = False self._block_define.Bind(EVT_PICK_BLOCK, self._on_pick_block_button) self._sizer.Add(self._block_define, 1, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self._run_button = wx.Button(self, label="Run Operation") self._run_button.Bind(wx.EVT_BUTTON, self._run_operation) self._sizer.Add(self._run_button, 0, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self.Layout() self.Thaw() @property def wx_add_options(self) -> Tuple[int, ...]: return (1,) def _on_mode_change(self, evt): self._mode_description.SetLabel( MODES[self._mode.GetString(self._mode.GetSelection())] ) self._mode_description.Fit() self.Layout() evt.Skip() def _on_pick_block_button(self, evt): """Set up listening for the block click""" if not self._block_click_registered: self.canvas.Bind(EVT_BOX_CLICK, self._on_pick_block) self._block_click_registered = True evt.Skip() def _on_pick_block(self, evt): self.canvas.Unbind(EVT_BOX_CLICK, handler=self._on_pick_block) self._block_click_registered = False x, y, z = self.canvas.cursor_location self._block_define.universal_block = ( self.world.get_block(x, y, z, self.canvas.dimension), None, ) def _get_fill_block(self): return self._block_define.universal_block[0] def unload(self): self._save_options( { "fill_block": self._get_fill_block(), "fill_block_options": ( self._block_define.platform, self._block_define.version_number, self._block_define.force_blockstate, self._block_define.namespace, self._block_define.block_name, self._block_define.properties, ), } ) def _run_operation(self, _): self.canvas.run_operation(lambda: self._waterlog()) def _waterlog(self): mode = self._mode.GetString(self._mode.GetSelection()) waterlog_block = self._get_fill_block().base_block world = self.world selection = self.canvas.selection_group dimension = self.canvas.dimension iter_count = len(list(world.get_chunk_slices(selection, dimension, True))) count = 0 for chunk, slices, _ in world.get_chunk_slices(selection, dimension, True): original_blocks = chunk.blocks[slices] palette, blocks = numpy.unique(original_blocks, return_inverse=True) blocks = blocks.reshape(original_blocks.shape) if mode == "Overlay": lut = numpy.array( [ world.palette.get_add_block( waterlog_block if world.palette[block_id].namespaced_name == "universal_minecraft:air" else world.palette[block_id].base_block + waterlog_block # get the Block object for that id and add the user specified block ) # register the new block / get the numerical id if it was already registered for block_id in palette ] # add the new id to the palette ) elif mode == "Underlay": lut = numpy.array( [ world.palette.get_add_block( waterlog_block if world.palette[block_id].namespaced_name == "universal_minecraft:air" else waterlog_block + world.palette[ # get the Block object for that id and add the user specified block block_id ].base_block ) # register the new block / get the numerical id if it was already registered for block_id in palette ] # add the new id to the palette ) elif mode == "Normal Fill": lut = numpy.array( [ world.palette.get_add_block( waterlog_block ) # register the new block / get the numerical id if it was already registered ] * len(palette) # add the new id to the palette ) elif mode == "Game Fill": lut = numpy.array( [ world.palette.get_add_block( waterlog_block + world.palette[block_id].base_block if world.palette[block_id].namespaced_name == "universal_minecraft:water" else waterlog_block ) # register the new block / get the numerical id if it was already registered for block_id in palette ] # add the new id to the palette ) elif mode == "Set First": lut = numpy.array( [ world.palette.get_add_block( waterlog_block + world.palette[block_id].extra_blocks[0] if world.palette[block_id].extra_blocks else waterlog_block ) # register the new block / get the numerical id if it was already registered for block_id in palette ] # add the new id to the palette ) elif mode == "Set Second": lut = numpy.array( [ world.palette.get_add_block( world.palette[block_id].base_block + waterlog_block ) # register the new block / get the numerical id if it was already registered for block_id in palette ] # add the new id to the palette ) else: raise Exception("hello") chunk.blocks[slices] = lut[blocks] chunk.changed = True count += 1 yield count / iter_count
class _CollapsibleBlockDefine(wx.Panel): def __init__(self, parent: MultiBlockDefine, translation_manager, collapsed=False): super().__init__(parent, style=wx.BORDER_SIMPLE) self.EXPAND = MAXIMIZE.bitmap(18, 18) self.COLLAPSE = MINIMIZE.bitmap(18, 18) self._collapsed = collapsed sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) header_sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(header_sizer, 0, wx.ALL, 5) self.expand_button = wx.BitmapButton(self, bitmap=self.EXPAND) header_sizer.Add(self.expand_button, 0, 5) self.up_button = wx.BitmapButton(self, bitmap=UP_CARET.bitmap(18, 18)) header_sizer.Add(self.up_button, 0, wx.LEFT, 5) self.up_button.Bind(wx.EVT_BUTTON, lambda evt: parent.move_up(self)) self.down_button = wx.BitmapButton(self, bitmap=DOWN_CARET.bitmap(18, 18)) header_sizer.Add(self.down_button, 0, wx.LEFT, 5) self.down_button.Bind(wx.EVT_BUTTON, lambda evt: parent.move_down(self)) self.delete_button = wx.BitmapButton(self, bitmap=TRASH.bitmap(18, 18)) header_sizer.Add(self.delete_button, 0, wx.LEFT, 5) self.delete_button.Bind(wx.EVT_BUTTON, lambda evt: parent.delete(self)) self.block_define = BlockDefine(self, translation_manager, wx.HORIZONTAL) sizer.Add(self.block_define, 1, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5) self.collapsed = collapsed self.block_label = wx.StaticText( self, label=self._gen_block_string(), style=wx.ST_ELLIPSIZE_END | wx.ST_NO_AUTORESIZE, size=(500, -1), ) header_sizer.Add(self.block_label, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) self.expand_button.Bind(wx.EVT_BUTTON, lambda evt: self._toggle_block_expand(parent)) self.block_define.Bind(EVT_PROPERTIES_CHANGE, self._on_properties_change) @property def collapsed(self) -> bool: return self._collapsed @collapsed.setter def collapsed(self, collapsed: bool): self._collapsed = collapsed if self._collapsed: self.expand_button.SetBitmap(self.EXPAND) self.block_define.Hide() else: self.expand_button.SetBitmap(self.COLLAPSE) self.block_define.Show() self.TopLevelParent.Layout() def _toggle_block_expand(self, parent: MultiBlockDefine): if self.collapsed: parent.collapse() self.collapsed = not self.collapsed def _on_properties_change(self, evt): self.block_label.SetLabel(self._gen_block_string()) self.TopLevelParent.Layout() evt.Skip() def _gen_block_string(self): base = f"{self.block_define.namespace}:{self.block_define.block_name}" properties = ",".join( (f"{key}={value}" for key, value in self.block_define.str_properties.items())) return f"{base}[{properties}]" if properties else base
class Replace(SimpleScrollablePanel, OperationUI): def __init__( self, parent: wx.Window, canvas: "EditCanvas", world: "World", options_path: str ): SimpleScrollablePanel.__init__(self, parent) OperationUI.__init__(self, parent, canvas, world, options_path) self.Freeze() options = self._load_options({}) self._block_click_registered = 0 self._original_block = BlockDefine( self, world.world_wrapper.translation_manager, wx.VERTICAL, *( options.get("original_block_options", []) or [world.world_wrapper.platform] ), wildcard_properties=True, show_pick_block=True ) self._sizer.Add(self._original_block, 1, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self._original_block.Bind( EVT_PICK_BLOCK, lambda evt: self._on_pick_block_button(evt, 1) ) self._replacement_block = BlockDefine( self, world.world_wrapper.translation_manager, wx.VERTICAL, *( options.get("replacement_block_options", []) or [world.world_wrapper.platform] ), show_pick_block=True ) self._sizer.Add( self._replacement_block, 1, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5 ) self._replacement_block.Bind( EVT_PICK_BLOCK, lambda evt: self._on_pick_block_button(evt, 2) ) self._run_button = wx.Button(self, label="Run Operation") self._run_button.Bind(wx.EVT_BUTTON, self._run_operation) self._sizer.Add(self._run_button, 0, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self.Layout() self.Thaw() @property def wx_add_options(self) -> Tuple[int, ...]: return (1,) def _on_pick_block_button(self, evt, code): """Set up listening for the block click""" if not self._block_click_registered: self.canvas.Bind(EVT_BOX_CLICK, self._on_pick_block) self._block_click_registered = code evt.Skip() def _on_pick_block(self, evt): self.canvas.Unbind(EVT_BOX_CLICK, handler=self._on_pick_block) x, y, z = self.canvas.cursor_location if self._block_click_registered == 1: self._original_block.universal_block = ( self.world.get_block(x, y, z, self.canvas.dimension), None, ) elif self._block_click_registered == 2: self._replacement_block.universal_block = ( self.world.get_block(x, y, z, self.canvas.dimension), None, ) self._block_click_registered = 0 def _get_replacement_block(self) -> Block: return self._replacement_block.universal_block[0] def unload(self): self._save_options( { "original_block_options": ( self._original_block.platform, self._original_block.version_number, self._original_block.force_blockstate, self._original_block.namespace, self._original_block.block_name, self._original_block.str_properties, ), "replacement_block": self._get_replacement_block(), "replacement_block_options": ( self._replacement_block.platform, self._replacement_block.version_number, self._replacement_block.force_blockstate, self._replacement_block.namespace, self._replacement_block.block_name, self._replacement_block.str_properties, ), } ) def _run_operation(self, _): self.canvas.run_operation(lambda: self._replace()) def _replace(self): world = self.world selection = self.canvas.selection_group dimension = self.canvas.dimension ( original_platform, original_version, original_blockstate, original_namespace, original_base_name, original_properties, ) = ( self._original_block.platform, self._original_block.version_number, self._original_block.force_blockstate, self._original_block.namespace, self._original_block.block_name, self._original_block.str_properties, ) replacement_block = self._get_replacement_block() replacement_block_id = world.palette.get_add_block(replacement_block) original_block_matches = [] universal_block_count = 0 iter_count = len(list(world.get_chunk_slices(selection, dimension))) count = 0 for chunk, slices, _ in world.get_chunk_slices(selection, dimension): if universal_block_count < len(world.palette): for universal_block_id in range( universal_block_count, len(world.palette) ): version_block = world.translation_manager.get_version( original_platform, original_version ).block.from_universal( world.palette[universal_block_id], force_blockstate=original_blockstate, )[ 0 ] if ( version_block.namespace == original_namespace and version_block.base_name == original_base_name and all( original_properties.get(prop) in ["*", val.to_snbt()] for prop, val in version_block.properties.items() ) ): original_block_matches.append(universal_block_id) universal_block_count = len(world.palette) blocks = chunk.blocks[slices] blocks[numpy.isin(blocks, original_block_matches)] = replacement_block_id chunk.blocks[slices] = blocks chunk.changed = True count += 1 yield count / iter_count def DoGetBestClientSize(self): sizer = self.GetSizer() if sizer is None: return -1, -1 else: sx, sy = self.GetSizer().CalcMin() return ( sx + wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X), sy + wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y), )