From 600463208fb79002f3dc8d28b946dfb152b2d472 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 18 Aug 2021 00:01:37 +0700 Subject: [PATCH] Matter task viewing and managing --- .../block/entity/BlockEntityMatterPanel.java | 41 ++- .../entity/BlockEntityMatterReplicator.java | 8 +- .../otm/block/entity/BlockEntityMattery.java | 1 + .../mc/otm/capability/MatterTask.java | 32 +- .../dbotthepony/mc/otm/matter/MatterGrid.java | 2 +- .../mc/otm/menu/MatterPanelMenu.java | 75 +++- .../otm/network/CancelMatterTaskPacket.java | 42 +++ .../mc/otm/network/MatterTaskPacket.java | 53 +++ .../mc/otm/network/MatteryNetworking.java | 18 + .../mc/otm/network/PatternGridPacket.java | 13 +- .../PatternReplicationRequestPacket.java | 6 +- .../mc/otm/screen/MatterPanelScreen.java | 326 ++++++++++++++---- .../overdrive_that_matters/lang/en_us.json | 9 + .../textures/gui/matter_panel.png | Bin 11226 -> 12164 bytes .../textures/gui/matter_panel.xcf | Bin 37782 -> 42030 bytes 15 files changed, 541 insertions(+), 85 deletions(-) create mode 100644 src/main/java/ru/dbotthepony/mc/otm/network/CancelMatterTaskPacket.java create mode 100644 src/main/java/ru/dbotthepony/mc/otm/network/MatterTaskPacket.java diff --git a/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMatterPanel.java b/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMatterPanel.java index 219ae9c8e..897ba7271 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMatterPanel.java +++ b/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMatterPanel.java @@ -31,6 +31,16 @@ public class BlockEntityMatterPanel extends BlockEntityMattery implements IMatte private static final TranslatableComponent NAME = new TranslatableComponent("block.overdrive_that_matters.matter_panel"); + private final ArrayList listeners = new ArrayList<>(); + + public void attachMenu(MatterPanelMenu menu) { + listeners.add(menu); + } + + public void deatachMenu(MatterPanelMenu menu) { + listeners.remove(menu); + } + @Override protected Component getDefaultDisplayName() { return NAME; @@ -137,7 +147,7 @@ public class BlockEntityMatterPanel extends BlockEntityMattery implements IMatte if (!simulate) { var newer = task.shrinkRequired(1); tasks.put(entry.getKey(), newer); - + listeners.forEach(menu -> menu.taskUpdated(newer)); grid.onMatterTaskUpdated(newer, task); } @@ -165,11 +175,18 @@ public class BlockEntityMatterPanel extends BlockEntityMattery implements IMatte if (grid != null) grid.onMatterTaskCreated(task); + + MatterTask finalGet_task1 = get_task; + listeners.forEach(menu -> menu.taskRemoved(finalGet_task1)); } else { tasks.put(task.id(), get_task); - if (grid != null) + if (grid != null) { grid.onMatterTaskUpdated(get_task, old_task); + } + + MatterTask finalGet_task = get_task; + listeners.forEach(menu -> menu.taskUpdated(finalGet_task)); } return true; @@ -210,6 +227,24 @@ public class BlockEntityMatterPanel extends BlockEntityMattery implements IMatte return tasks.get(id); } + public void removeTask(UUID id) { + var task = tasks.get(id); + + if (task == null) + return; + + tasks.remove(id); + + if (grid != null) + grid.onMatterTaskRemoved(task); + + listeners.forEach(menu -> menu.taskRemoved(task)); + } + + public void removeTask(PatternState state) { + removeTask(state.id()); + } + public MatterTask addTask(PatternState state, int how_much) { var task = new MatterTask(UUID.randomUUID(), state.id(), state.item(), 0, 0, how_much); tasks.put(task.id(), task); @@ -217,6 +252,8 @@ public class BlockEntityMatterPanel extends BlockEntityMattery implements IMatte if (grid != null) grid.onMatterTaskCreated(task); + listeners.forEach(menu -> menu.taskUpdated(task)); + return task; } diff --git a/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMatterReplicator.java b/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMatterReplicator.java index 0b24d33c3..00819c4e0 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMatterReplicator.java +++ b/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMatterReplicator.java @@ -69,11 +69,11 @@ public class BlockEntityMatterReplicator extends BlockEntityMatteryPoweredWorker @Override protected MachineJobStatus onJobFinish(MachineJob job) { if (!regular_slots.addItem(job.stack()).isEmpty()) { - if (grid != null) { - grid.notifyTaskCompletion(MatterTask.deserializeNBT(job.data().get("task"))); - } + return new MachineJobStatus(false, 20); + } - return new MachineJobStatus(false); + if (grid != null) { + grid.notifyTaskCompletion(MatterTask.deserializeNBT(job.data().get("task"))); } return new MachineJobStatus(); diff --git a/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMattery.java b/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMattery.java index d161290fc..8029d2136 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMattery.java +++ b/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityMattery.java @@ -16,6 +16,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import javax.annotation.Nullable; +import java.util.UUID; public abstract class BlockEntityMattery extends BlockEntity implements MenuProvider { protected Component display_name; diff --git a/src/main/java/ru/dbotthepony/mc/otm/capability/MatterTask.java b/src/main/java/ru/dbotthepony/mc/otm/capability/MatterTask.java index 80a167a95..7cdb79b7d 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/MatterTask.java +++ b/src/main/java/ru/dbotthepony/mc/otm/capability/MatterTask.java @@ -6,6 +6,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.registries.ForgeRegistry; import net.minecraftforge.registries.RegistryManager; @@ -25,6 +26,19 @@ public record MatterTask(@Nonnull UUID id, @Nullable UUID pattern, @Nonnull Item this.required = Math.max(0, required); } + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof MatterTask obj1) + return obj1.id.equals(id); + + return false; + } + public ItemStack stack() { return new ItemStack(item, 1); } @@ -72,7 +86,7 @@ public record MatterTask(@Nonnull UUID id, @Nullable UUID pattern, @Nonnull Item if (nbt instanceof CompoundTag tag) { Item get_item = RegistryManager.ACTIVE.getRegistry(Item.class).getValue(new ResourceLocation(tag.getString("item"))); - if (get_item != null) { + if (get_item != null && get_item != Items.AIR) { long a = tag.getLong("pattern_u"); long b = tag.getLong("pattern_l"); @@ -110,4 +124,20 @@ public record MatterTask(@Nonnull UUID id, @Nullable UUID pattern, @Nonnull Item buffer.writeInt(finished); buffer.writeInt(required); } + + @Nullable + public static MatterTask read(FriendlyByteBuf buffer) { + var id = new UUID(buffer.readLong(), buffer.readLong()); + var pattern = buffer.readBoolean() ? new UUID(buffer.readLong(), buffer.readLong()) : null; + var item = ((ForgeRegistry) RegistryManager.ACTIVE.getRegistry(Item.class)).getValue(buffer.readInt()); + + if (item == null) + return null; + + var in_progress = buffer.readInt(); + var finished = buffer.readInt(); + var required = buffer.readInt(); + + return new MatterTask(id, pattern, item, in_progress, finished, required); + } } diff --git a/src/main/java/ru/dbotthepony/mc/otm/matter/MatterGrid.java b/src/main/java/ru/dbotthepony/mc/otm/matter/MatterGrid.java index ab02f4552..d2847c614 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/matter/MatterGrid.java +++ b/src/main/java/ru/dbotthepony/mc/otm/matter/MatterGrid.java @@ -46,7 +46,7 @@ public class MatterGrid implements IMatterGridListener { var set = discovering_neighbours.get(event.world); - if (set != null && set.size() != 0) { + if (set != null) { ArrayList> invalid = new ArrayList<>(); for (Supplier cell : set) { diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterPanelMenu.java b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterPanelMenu.java index a10c2ba44..0609cc0f4 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/menu/MatterPanelMenu.java +++ b/src/main/java/ru/dbotthepony/mc/otm/menu/MatterPanelMenu.java @@ -8,12 +8,17 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters; import ru.dbotthepony.mc.otm.Registry; import ru.dbotthepony.mc.otm.block.entity.BlockEntityMatterPanel; import ru.dbotthepony.mc.otm.capability.IMatterGridListener; +import ru.dbotthepony.mc.otm.capability.MatterTask; import ru.dbotthepony.mc.otm.capability.PatternState; import ru.dbotthepony.mc.otm.matter.MatterGrid; +import ru.dbotthepony.mc.otm.network.CancelMatterTaskPacket; +import ru.dbotthepony.mc.otm.network.MatterTaskPacket; import ru.dbotthepony.mc.otm.network.MatteryNetworking; import ru.dbotthepony.mc.otm.network.PatternGridPacket; import java.util.ArrayList; +import java.util.UUID; +import java.util.function.Consumer; public class MatterPanelMenu extends MatteryMenu implements IMatterGridListener { public MatterPanelMenu(int p_38852_, Inventory inventory) { @@ -29,6 +34,7 @@ public class MatterPanelMenu extends MatteryMenu implements IMatterGridListener if (tile != null) { grid = tile.getMatterGrid(); + tile.attachMenu(this); if (grid != null) { grid.attach(this); @@ -36,8 +42,17 @@ public class MatterPanelMenu extends MatteryMenu implements IMatterGridListener } } + public void taskUpdated(MatterTask task) { + sendNetwork(new MatterTaskPacket(true, task)); + } + + public void taskRemoved(MatterTask task) { + sendNetwork(new MatterTaskPacket(false, task)); + } + // client code public ArrayList patterns = new ArrayList<>(); + public ArrayList tasks = new ArrayList<>(); public int changeset = 0; public void networkPatternsUpdated(PatternState[] patterns) { @@ -62,6 +77,42 @@ public class MatterPanelMenu extends MatteryMenu implements IMatterGridListener } } + public void networkTasksUpdated(MatterTask[] tasks) { + changeset++; + + for (var task : tasks) { + var index_of = this.tasks.indexOf(task); + + if (index_of != -1) { + this.tasks.set(index_of, task); + } else { + this.tasks.add(task); + } + + if (watcher_update != null) + watcher_update.accept(task); + } + } + + public void networkTasksRemoved(MatterTask[] tasks) { + changeset++; + + for (var task : tasks) { + this.tasks.remove(task); + + if (watcher_delete != null) + watcher_delete.accept(task); + } + } + + private Consumer watcher_delete; + private Consumer watcher_update; + + public void networkTaskWatcher(Consumer watcher_update, Consumer watcher_delete) { + this.watcher_delete = watcher_delete; + this.watcher_update = watcher_update; + } + // server code public void requestReplication(ServerPlayer ply, PatternState state, int how_much) { if (tile == null) @@ -82,19 +133,30 @@ public class MatterPanelMenu extends MatteryMenu implements IMatterGridListener tile.addTask(state, how_much); } + public void receiveTaskCancel(ServerPlayer ply, UUID id) { + if (tile == null) + return; + + tile.removeTask(id); + } + + public void requestTaskCancel(UUID id) { + MatteryNetworking.CHANNEL.sendToServer(new CancelMatterTaskPacket(id)); + } + @Override public void onPatternAdded(PatternState state) { - sendNetwork(new PatternGridPacket(containerId, true, state)); + sendNetwork(new PatternGridPacket(true, state)); } @Override public void onPatternRemoved(PatternState state) { - sendNetwork(new PatternGridPacket(containerId, false, state)); + sendNetwork(new PatternGridPacket(false, state)); } @Override public void onPatternUpdated(PatternState new_state, PatternState old_state) { - sendNetwork(new PatternGridPacket(containerId, true, new_state)); + sendNetwork(new PatternGridPacket(true, new_state)); } private boolean initial_send = false; @@ -103,6 +165,9 @@ public class MatterPanelMenu extends MatteryMenu implements IMatterGridListener public void removed(Player p_38940_) { super.removed(p_38940_); + if (tile != null) + tile.deatachMenu(this); + if (grid != null) grid.detach(this); } @@ -130,8 +195,10 @@ public class MatterPanelMenu extends MatteryMenu implements IMatterGridListener if (grid != null) { initial_send = true; - sendNetwork(new PatternGridPacket(containerId, true, grid.getStoredPatterns().toArray(new PatternState[0]))); + sendNetwork(new PatternGridPacket(true, grid.getStoredPatterns().toArray(new PatternState[0]))); } + + sendNetwork(new MatterTaskPacket(true, tile.getAllTasks().toArray(new MatterTask[0]))); } } diff --git a/src/main/java/ru/dbotthepony/mc/otm/network/CancelMatterTaskPacket.java b/src/main/java/ru/dbotthepony/mc/otm/network/CancelMatterTaskPacket.java new file mode 100644 index 000000000..2e3bd4eb9 --- /dev/null +++ b/src/main/java/ru/dbotthepony/mc/otm/network/CancelMatterTaskPacket.java @@ -0,0 +1,42 @@ +package ru.dbotthepony.mc.otm.network; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fmllegacy.network.NetworkEvent; +import ru.dbotthepony.mc.otm.capability.MatterTask; +import ru.dbotthepony.mc.otm.menu.MatterPanelMenu; + +import java.util.UUID; +import java.util.function.Supplier; + +public record CancelMatterTaskPacket(UUID id) { + public CancelMatterTaskPacket(UUID id) { + this.id = id; + } + + public CancelMatterTaskPacket(MatterTask task) { + this(task.id()); + } + + public void write(FriendlyByteBuf buffer) { + buffer.writeLong(id.getMostSignificantBits()); + buffer.writeLong(id.getLeastSignificantBits()); + } + + public void play(Supplier context) { + context.get().setPacketHandled(true); + + context.get().enqueueWork(() -> { + var player = context.get().getSender(); + + if (player.containerMenu instanceof MatterPanelMenu menu) { + menu.receiveTaskCancel(player, id); + } + }); + } + + public static CancelMatterTaskPacket read(FriendlyByteBuf buffer) { + return new CancelMatterTaskPacket(new UUID(buffer.readLong(), buffer.readLong())); + } +} diff --git a/src/main/java/ru/dbotthepony/mc/otm/network/MatterTaskPacket.java b/src/main/java/ru/dbotthepony/mc/otm/network/MatterTaskPacket.java new file mode 100644 index 000000000..83fb13657 --- /dev/null +++ b/src/main/java/ru/dbotthepony/mc/otm/network/MatterTaskPacket.java @@ -0,0 +1,53 @@ +package ru.dbotthepony.mc.otm.network; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fmllegacy.network.NetworkEvent; +import ru.dbotthepony.mc.otm.OverdriveThatMatters; +import ru.dbotthepony.mc.otm.capability.MatterTask; +import ru.dbotthepony.mc.otm.capability.PatternState; +import ru.dbotthepony.mc.otm.menu.MatterPanelMenu; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Objects; +import java.util.function.Supplier; + +@ParametersAreNonnullByDefault +public record MatterTaskPacket(boolean action, MatterTask...state) { + public void write(FriendlyByteBuf buffer) { + buffer.writeBoolean(action); + buffer.writeInt(state.length); + + for (var state1 : state) + state1.write(buffer); + } + + public void play(Supplier context) { + context.get().setPacketHandled(true); + context.get().enqueueWork(() -> DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::playClient)); + } + + private void playClient() { + if (Minecraft.getInstance().player.containerMenu instanceof MatterPanelMenu menu) { + if (action) + menu.networkTasksUpdated(state); + else + menu.networkTasksRemoved(state); + } else { + OverdriveThatMatters.LOGGER.error("Receive task state list, but no valid container is open / open container is not MatterPanelMenu!"); + } + } + + public static MatterTaskPacket read(FriendlyByteBuf buffer) { + boolean action = buffer.readBoolean(); + int amount = buffer.readInt(); + var list = new MatterTask[amount]; + + for (int i = 0; i < amount; i++) + list[i] = Objects.requireNonNull(MatterTask.read(buffer)); + + return new MatterTaskPacket(action, list); + } +} diff --git a/src/main/java/ru/dbotthepony/mc/otm/network/MatteryNetworking.java b/src/main/java/ru/dbotthepony/mc/otm/network/MatteryNetworking.java index 58dda7508..24f392de1 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/network/MatteryNetworking.java +++ b/src/main/java/ru/dbotthepony/mc/otm/network/MatteryNetworking.java @@ -39,6 +39,24 @@ public class MatteryNetworking { Optional.of(NetworkDirection.PLAY_TO_CLIENT) ); + CHANNEL.registerMessage( + next_network_id++, + MatterTaskPacket.class, + MatterTaskPacket::write, + MatterTaskPacket::read, + MatterTaskPacket::play, + Optional.of(NetworkDirection.PLAY_TO_CLIENT) + ); + + CHANNEL.registerMessage( + next_network_id++, + CancelMatterTaskPacket.class, + CancelMatterTaskPacket::write, + CancelMatterTaskPacket::read, + CancelMatterTaskPacket::play, + Optional.of(NetworkDirection.PLAY_TO_SERVER) + ); + CHANNEL.registerMessage( next_network_id++, PatternReplicationRequestPacket.class, diff --git a/src/main/java/ru/dbotthepony/mc/otm/network/PatternGridPacket.java b/src/main/java/ru/dbotthepony/mc/otm/network/PatternGridPacket.java index dca0fa571..d67a848d8 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/network/PatternGridPacket.java +++ b/src/main/java/ru/dbotthepony/mc/otm/network/PatternGridPacket.java @@ -14,9 +14,8 @@ import java.util.Objects; import java.util.function.Supplier; @ParametersAreNonnullByDefault -public record PatternGridPacket(int container_id, boolean action, PatternState ...state) { +public record PatternGridPacket(boolean action, PatternState ...state) { public void write(FriendlyByteBuf buffer) { - buffer.writeInt(container_id); buffer.writeBoolean(action); buffer.writeInt(state.length); @@ -31,11 +30,6 @@ public record PatternGridPacket(int container_id, boolean action, PatternState . private void playClient() { if (Minecraft.getInstance().player.containerMenu instanceof MatterPanelMenu menu) { - if (menu.containerId != container_id) { - OverdriveThatMatters.LOGGER.error("Receive pattern state list for {}, but current container has id of {}!", container_id, menu.containerId); - return; - } - if (action) menu.networkPatternsUpdated(state); else @@ -44,11 +38,10 @@ public record PatternGridPacket(int container_id, boolean action, PatternState . return; } - OverdriveThatMatters.LOGGER.error("Receive pattern state list for {}, but no valid container is open!", container_id); + OverdriveThatMatters.LOGGER.error("Receive pattern state list, but no valid container is open / container is not MatterPanelMenu!"); } public static PatternGridPacket read(FriendlyByteBuf buffer) { - int container_id = buffer.readInt(); boolean action = buffer.readBoolean(); int amount = buffer.readInt(); var list = new PatternState[amount]; @@ -56,6 +49,6 @@ public record PatternGridPacket(int container_id, boolean action, PatternState . for (int i = 0; i < amount; i++) list[i] = Objects.requireNonNull(PatternState.read(buffer)); - return new PatternGridPacket(container_id, action, list); + return new PatternGridPacket(action, list); } } diff --git a/src/main/java/ru/dbotthepony/mc/otm/network/PatternReplicationRequestPacket.java b/src/main/java/ru/dbotthepony/mc/otm/network/PatternReplicationRequestPacket.java index db0606ae3..62885cd54 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/network/PatternReplicationRequestPacket.java +++ b/src/main/java/ru/dbotthepony/mc/otm/network/PatternReplicationRequestPacket.java @@ -27,10 +27,8 @@ public record PatternReplicationRequestPacket(PatternState state, int how_much) var ply = context.get().getSender(); - if (!(ply.containerMenu instanceof MatterPanelMenu menu)) - return; - - menu.requestReplication(ply, state, how_much); + if (ply.containerMenu instanceof MatterPanelMenu menu) + menu.requestReplication(ply, state, how_much); }); } diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterPanelScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterPanelScreen.java index 463708ca9..56e348793 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/screen/MatterPanelScreen.java +++ b/src/main/java/ru/dbotthepony/mc/otm/screen/MatterPanelScreen.java @@ -7,12 +7,16 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvents; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.item.ItemStack; import ru.dbotthepony.mc.otm.OverdriveThatMatters; +import ru.dbotthepony.mc.otm.capability.MatterTask; import ru.dbotthepony.mc.otm.capability.PatternState; import ru.dbotthepony.mc.otm.menu.MatterPanelMenu; import ru.dbotthepony.mc.otm.network.MatteryNetworking; @@ -28,6 +32,8 @@ public class MatterPanelScreen extends MatteryScreen { private static final int modal_width = 213; private static final int modal_height = 110; + private boolean open_task_list = false; + protected static final ResourceLocation CONTAINER = new ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/matter_panel.png"); protected ResourceLocation CONTAINER_BACKGROUND() { @@ -41,10 +47,22 @@ public class MatterPanelScreen extends MatteryScreen { imageHeight = 187; titleLabelY = 5; + + p_97741_.networkTaskWatcher(task -> { + if (open_task != null && open_task.equals(task)) { + open_task = task; + } + }, task -> { + if (open_task != null && open_task.equals(task)) { + closeTask(); + } + }); } private PatternState open_pattern; + private MatterTask open_task; private EditBox input_amount; + private Button inc_8; private Button inc_64; private Button inc_256; @@ -53,12 +71,16 @@ public class MatterPanelScreen extends MatteryScreen { private Button dec_64; private Button dec_256; - private Button send; - private Button cancel; + private Button send_modal; + private Button cancel_modal; + + private Button cancel_task; + private Button close_task; private PatternState hovered_pattern; + private MatterTask hovered_task; - private double dragger_position = 0; + private double scroller_position = 0; @Override protected void init() { @@ -67,6 +89,7 @@ public class MatterPanelScreen extends MatteryScreen { int x = (width - modal_width) / 2; int y = (height - modal_height) / 2; + // окно на создание запроса int top_level = 15; int button_width = 40; int right = 5 + button_width; @@ -101,10 +124,10 @@ public class MatterPanelScreen extends MatteryScreen { top_level += 24; right = 5 + button_width; - cancel = new Button(x + modal_width - right, y + top_level, button_width, 20, new TranslatableComponent("otm.container.matter_panel.cancel"), (btn) -> closePattern()); + cancel_modal = new Button(x + modal_width - right, y + top_level, button_width, 20, new TranslatableComponent("otm.container.matter_panel.cancel"), (btn) -> closePattern()); right += 2 + button_width; - send = new Button(x + modal_width - right, y + top_level, button_width, 20, new TranslatableComponent("otm.container.matter_panel.send"), (btn) -> onSend()); + send_modal = new Button(x + modal_width - right, y + top_level, button_width, 20, new TranslatableComponent("otm.container.matter_panel.send"), (btn) -> onSend()); inc_8.visible = false; inc_64.visible = false; @@ -114,8 +137,8 @@ public class MatterPanelScreen extends MatteryScreen { dec_64.visible = false; dec_256.visible = false; - send.visible = false; - cancel.visible = false; + send_modal.visible = false; + cancel_modal.visible = false; addWidget(input_amount); addWidget(inc_8); @@ -124,8 +147,24 @@ public class MatterPanelScreen extends MatteryScreen { addWidget(dec_8); addWidget(dec_64); addWidget(dec_256); - addWidget(send); - addWidget(cancel); + addWidget(send_modal); + addWidget(cancel_modal); + + // окно на отмену запроса + right = 5 + button_width; + top_level = modal_height - 8 - 20; + + close_task = new Button(x + modal_width - right, y + top_level, button_width, 20, new TranslatableComponent("otm.container.matter_panel.close"), (btn) -> closeTask()); + + button_width = 90; + right += 2 + button_width; + cancel_task = new Button(x + modal_width - right, y + top_level, button_width, 20, new TranslatableComponent("otm.container.matter_panel.cancel_task"), (btn) -> cancelTask()); + + close_task.visible = false; + cancel_task.visible = false; + + addWidget(close_task); + addWidget(cancel_task); } private void onChangeAmountPress(int amount) { @@ -164,14 +203,33 @@ public class MatterPanelScreen extends MatteryScreen { inc_64.visible = false; inc_256.visible = false; - send.visible = false; - cancel.visible = false; + send_modal.visible = false; + cancel_modal.visible = false; dec_8.visible = false; dec_64.visible = false; dec_256.visible = false; } + private void openTask(MatterTask task) { + open_task = task; + + close_task.visible = true; + cancel_task.visible = true; + } + + private void closeTask() { + open_task = null; + + close_task.visible = false; + cancel_task.visible = false; + } + + private void cancelTask() { + menu.requestTaskCancel(open_task.id()); + closeTask(); + } + @Override protected void containerTick() { super.containerTick(); @@ -182,7 +240,17 @@ public class MatterPanelScreen extends MatteryScreen { @Override public void resize(Minecraft p_96575_, int p_96576_, int p_96577_) { var input_amount = this.input_amount; + super.resize(p_96575_, p_96576_, p_96577_); + + if (open_task != null) { + openTask(open_task); + } + + if (open_pattern != null) { + openPattern(open_pattern); + } + if (input_amount != null) { this.input_amount.setValue(input_amount.getValue()); } @@ -209,8 +277,16 @@ public class MatterPanelScreen extends MatteryScreen { dec_256.render(stack, mouseX, mouseY, p_98421_); dec_8.render(stack, mouseX, mouseY, p_98421_); - send.render(stack, mouseX, mouseY, p_98421_); - cancel.render(stack, mouseX, mouseY, p_98421_); + send_modal.render(stack, mouseX, mouseY, p_98421_); + cancel_modal.render(stack, mouseX, mouseY, p_98421_); + + RenderSystem.depthFunc(GL_LESS); + } else if (open_task != null) { + RenderSystem.enableDepthTest(); + RenderSystem.depthFunc(GL_ALWAYS); + + cancel_task.render(stack, mouseX, mouseY, p_98421_); + close_task.render(stack, mouseX, mouseY, p_98421_); RenderSystem.depthFunc(GL_LESS); } @@ -226,6 +302,11 @@ public class MatterPanelScreen extends MatteryScreen { if (hovered_pattern != null) { get_list.add(new TranslatableComponent("otm.item.pattern.research", String.format("%.2f", hovered_pattern.research_percent() * 100d)).withStyle(ChatFormatting.AQUA)); + } else if (hovered_task != null) { + get_list.add(new TranslatableComponent("otm.gui.matter_task.total", hovered_task.total()).withStyle(ChatFormatting.GRAY)); + get_list.add(new TranslatableComponent("otm.gui.matter_task.required", hovered_task.required()).withStyle(ChatFormatting.GRAY)); + get_list.add(new TranslatableComponent("otm.gui.matter_task.in_progress", hovered_task.in_progress()).withStyle(ChatFormatting.GRAY)); + get_list.add(new TranslatableComponent("otm.gui.matter_task.finished", hovered_task.finished()).withStyle(ChatFormatting.GRAY)); } return get_list; @@ -244,6 +325,9 @@ public class MatterPanelScreen extends MatteryScreen { } else if (hovered_pattern != null) { this.renderTooltip(pose, new ItemStack(hovered_pattern.item(), 1), mouseX, mouseY); return; + } else if (hovered_task != null) { + this.renderTooltip(pose, new ItemStack(hovered_task.item(), hovered_task.total()), mouseX, mouseY); + return; } super.renderTooltip(pose, mouseX, mouseY); @@ -252,7 +336,7 @@ public class MatterPanelScreen extends MatteryScreen { private void openPattern(PatternState state) { open_pattern = state; input_amount.setValue("1"); - input_amount.setFocus(true); + // input_amount.setFocus(true); // bugged scrolling = false; input_amount.visible = true; @@ -264,26 +348,67 @@ public class MatterPanelScreen extends MatteryScreen { dec_64.visible = true; dec_256.visible = true; - send.visible = true; - cancel.visible = true; + send_modal.visible = true; + cancel_modal.visible = true; + } + + private double scroller_position_regular = 0; + private double scroller_position_tasks = 0; + + private void openTaskList() { + open_task_list = true; + scroller_position_regular = scroller_position; + scroller_position = scroller_position_tasks; + + } + + private void closeTaskList() { + open_task_list = false; + scroller_position_tasks = scroller_position; + scroller_position = scroller_position_regular; } @Override public boolean mouseClicked(double mouse_x, double mouse_y, int p_97750_) { - if (hovered_pattern != null && open_pattern == null) { - openPattern(hovered_pattern); - return true; + if (open_task == null && open_pattern == null) { + if (hovered_pattern != null) { + openPattern(hovered_pattern); + return true; + } else if (hovered_task != null) { + openTask(hovered_task); + return true; + } } - double div_scroll = menu.patterns.size() < 1 ? 1 : menu.patterns.size() - 1; + int button_x = leftPos + 157; + int button_y = topPos + 20 + (int) ((scroller_position / maxScroll()) * 145); + int button_width = 12; + int button_height = 15; - int scroll_x = leftPos + 157; - int scroll_y = topPos + 20 + (int) ((dragger_position / maxScroll()) * 145); + scrolling = maxScroll() > 0 && mouse_x >= button_x && button_x + button_width >= mouse_x && mouse_y >= button_y && button_y + button_height >= mouse_y; - if (maxScroll() > 0 && mouse_x >= scroll_x && scroll_x + 12 >= mouse_x && mouse_y >= scroll_y && scroll_y + 15 >= mouse_y) { - scrolling = true; - } else { - scrolling = false; + if (open_task == null && open_pattern == null) { + if (open_task_list) { + button_x = leftPos; + button_y = topPos - 28; + button_width = 28; + button_height = 30; + + if (mouse_x >= button_x && button_x + button_width >= mouse_x && mouse_y >= button_y && button_y + button_height >= mouse_y) { + closeTaskList(); + Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); + } + } else { + button_x = leftPos + 28; + button_y = topPos - 28; + button_width = 28; + button_height = 30; + + if (mouse_x >= button_x && button_x + button_width >= mouse_x && mouse_y >= button_y && button_y + button_height >= mouse_y) { + openTaskList(); + Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); + } + } } return super.mouseClicked(mouse_x, mouse_y, p_97750_); @@ -305,7 +430,7 @@ public class MatterPanelScreen extends MatteryScreen { return true; } - dragger_position = Math.max(0, Math.min(maxScroll(), dragger_position - scroll)); + scroller_position = Math.max(0, Math.min(maxScroll(), scroller_position - scroll)); return true; } @@ -314,11 +439,11 @@ public class MatterPanelScreen extends MatteryScreen { public boolean mouseDragged(double mouse_x, double mouse_y, int flag, double drag_x, double drag_y) { if (scrolling && maxScroll() > 0) { if (mouse_y < topPos + 20) { - dragger_position = 0; + scroller_position = 0; } else if (mouse_y > topPos + 20 + 160) { - dragger_position = maxScroll(); + scroller_position = maxScroll(); } else { - dragger_position = maxScroll() * (mouse_y - topPos - 20) / 160d; + scroller_position = maxScroll() * (mouse_y - topPos - 20) / 160d; } } @@ -328,6 +453,14 @@ public class MatterPanelScreen extends MatteryScreen { private boolean scrolling = false; private int maxScroll() { + if (open_task_list) { + if (menu.tasks.size() < 16) { + return 0; + } + + return (int) Math.floor(menu.tasks.size() / 8d - 2d); + } + if (menu.patterns.size() < 16) { return 0; } @@ -337,6 +470,18 @@ public class MatterPanelScreen extends MatteryScreen { @Override protected void renderBg(PoseStack pose, float p_97788_, int mouseX, int mouseY) { + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + RenderSystem.setShaderTexture(0, CONTAINER_BACKGROUND()); + + if (open_task_list) { + // список паттернов + blit(pose, leftPos, topPos - 28, 176, 65, 28, 30); + } else { + // список задач + blit(pose, leftPos + 28, topPos - 28, 204, 65, 28, 30); + } + super.renderBg(pose, p_97788_, mouseX, mouseY); RenderSystem.setShader(GameRenderer::getPositionTexShader); @@ -345,17 +490,26 @@ public class MatterPanelScreen extends MatteryScreen { if (maxScroll() > 0) { if (scrolling) { - blit(pose, leftPos + 157, topPos + 20 + (int) ((dragger_position / maxScroll()) * 145), 188, 0, 12, 15); + blit(pose, leftPos + 157, topPos + 20 + (int) ((scroller_position / maxScroll()) * 145), 188, 0, 12, 15); } else { - blit(pose, leftPos + 157, topPos + 20 + (int) ((dragger_position / maxScroll()) * 145), 176, 0, 12, 15); + blit(pose, leftPos + 157, topPos + 20 + (int) ((scroller_position / maxScroll()) * 145), 176, 0, 12, 15); } } else { blit(pose, leftPos + 157, topPos + 20, 176, 0, 12, 15); } + if (open_task_list) { + // список задач + blit(pose, leftPos + 28, topPos - 28, 204, 33, 28, 32); + } else { + // список паттернов + blit(pose, leftPos, topPos - 28, 176, 33, 28, 32); + } + this.font.draw(pose, this.title, (float) (leftPos + this.titleLabelX), (float) (topPos + this.titleLabelY), 4210752); hovered_pattern = null; + hovered_task = null; int cells_width = 8; int cells_height = 9; @@ -367,34 +521,63 @@ public class MatterPanelScreen extends MatteryScreen { RenderSystem.enableDepthTest(); - for (int index = ((int) dragger_position) * 9; index < menu.patterns.size(); index++) { - var state = menu.patterns.get(index); - var itemstack = new ItemStack(state.item(), 1); + if (open_task_list) { + for (int index = ((int) scroller_position) * 9; index < menu.tasks.size(); index++) { + var state = menu.tasks.get(index); + var itemstack = new ItemStack(state.item(), Math.max(1, state.required())); - // player, itemstack, x, y, ?????????? - this.itemRenderer.renderAndDecorateItem(this.minecraft.player, itemstack, render_x, render_y, -1); - // font, itemstack, x, y, string - this.itemRenderer.renderGuiItemDecorations(this.font, itemstack, render_x, render_y, null); + // player, itemstack, x, y, ?????????? + this.itemRenderer.renderAndDecorateItem(this.minecraft.player, itemstack, render_x, render_y, -1); + // font, itemstack, x, y, string + this.itemRenderer.renderGuiItemDecorations(this.font, itemstack, render_x, render_y, null); - if (hovered_pattern == null && mouseX >= render_x && mouseX <= render_x + 16 && mouseY >= render_y && mouseY <= render_y + 16) { - hovered_pattern = state; + if (hovered_task == null && mouseX >= render_x && mouseX <= render_x + 16 && mouseY >= render_y && mouseY <= render_y + 16) { + hovered_task = state; + } + + cell_x++; + render_x += 18; + + if (cell_x >= cells_width) { + render_x = leftPos + 10; + render_y += 18; + cell_x = 0; + cell_y++; + + if (cell_y >= cells_height) + break; + } } + } else { + for (int index = ((int) scroller_position) * 9; index < menu.patterns.size(); index++) { + var state = menu.patterns.get(index); + var itemstack = new ItemStack(state.item(), 1); - cell_x++; - render_x += 18; + // player, itemstack, x, y, ?????????? + this.itemRenderer.renderAndDecorateItem(this.minecraft.player, itemstack, render_x, render_y, -1); + // font, itemstack, x, y, string + this.itemRenderer.renderGuiItemDecorations(this.font, itemstack, render_x, render_y, null); - if (cell_x >= cells_width) { - render_x = leftPos + 10; - render_y += 18; - cell_x = 0; - cell_y++; + if (hovered_pattern == null && mouseX >= render_x && mouseX <= render_x + 16 && mouseY >= render_y && mouseY <= render_y + 16) { + hovered_pattern = state; + } - if (cell_y >= cells_height) - break; + cell_x++; + render_x += 18; + + if (cell_x >= cells_width) { + render_x = leftPos + 10; + render_y += 18; + cell_x = 0; + cell_y++; + + if (cell_y >= cells_height) + break; + } } } - if (open_pattern != null) { + if (open_pattern != null || open_task != null) { RenderSystem.depthFunc(GL_ALWAYS); this.renderBackground(pose); @@ -417,18 +600,43 @@ public class MatterPanelScreen extends MatteryScreen { this.blit(pose, x, y + dy, 0, 197, modal_width, 4); - this.blit(pose, x + 6, input_amount.y, 176, 15, 18, 18); + if (open_pattern != null) { + // фон слота + this.blit(pose, x + 6, input_amount.y, 176, 15, 18, 18); + } else { + this.blit(pose, x + 6, y + 19, 176, 15, 18, 18); + } - var itemstack = new ItemStack(open_pattern.item(), 1); + ItemStack itemstack; + + if (open_pattern != null) { + itemstack = new ItemStack(open_pattern.item(), 1); + } else { + itemstack = new ItemStack(open_task.item(), 1); + } RenderSystem.enableDepthTest(); - // player, itemstack, x, y, ?????????? - this.itemRenderer.renderAndDecorateItem(this.minecraft.player, itemstack, x + 7, input_amount.y + 1, 100); - // font, itemstack, x, y, string - this.itemRenderer.renderGuiItemDecorations(this.font, itemstack, 0, 0, null); - this.font.draw(pose, new TranslatableComponent("otm.container.matter_panel.label"), (float) (x + 6), (float) (y + 5), 4210752); - this.font.draw(pose, new TranslatableComponent("otm.item.pattern.line", itemstack.getDisplayName(), String.format("%.2f", open_pattern.research_percent() * 100d)), (float) (x + 6), (float) (cancel.y), ChatFormatting.AQUA.getColor()); + if (open_pattern != null) { + // player, itemstack, x, y, ?????????? + this.itemRenderer.renderAndDecorateItem(this.minecraft.player, itemstack, x + 7, input_amount.y + 1, 100); + // font, itemstack, x, y, string + this.itemRenderer.renderGuiItemDecorations(this.font, itemstack, x + 7, input_amount.y + 1, null); + } else { + // player, itemstack, x, y, ?????????? + this.itemRenderer.renderAndDecorateItem(this.minecraft.player, itemstack, x + 7, y + 20, 100); + // font, itemstack, x, y, string + // this.itemRenderer.renderGuiItemDecorations(this.font, itemstack, x + 7, input_amount.y + 1, null); + } + + if (open_pattern != null) { + this.font.draw(pose, new TranslatableComponent("otm.container.matter_panel.label"), (float) (x + 6), (float) (y + 5), 4210752); + this.font.draw(pose, new TranslatableComponent("otm.item.pattern.line", itemstack.getDisplayName(), String.format("%.2f", open_pattern.research_percent() * 100d)), (float) (x + 6), (float) (cancel_modal.y), ChatFormatting.AQUA.getColor()); + } else { + this.font.draw(pose, new TranslatableComponent("otm.container.matter_panel.task"), (float) (x + 6), (float) (y + 5), 4210752); + this.font.draw(pose, new TranslatableComponent("otm.container.matter_panel.task_line", itemstack.getDisplayName(), open_task.in_progress(), open_task.finished(), open_task.total()), (float) (x + 27), (float) (y + 20), 4210752); + this.font.draw(pose, new TextComponent(open_task.id().toString().substring(0, 8) + "..."), (float) (x + 27), (float) (y + 30), 4210752); + } RenderSystem.depthFunc(GL_LESS); } diff --git a/src/main/resources/assets/overdrive_that_matters/lang/en_us.json b/src/main/resources/assets/overdrive_that_matters/lang/en_us.json index 411294ded..603f083db 100644 --- a/src/main/resources/assets/overdrive_that_matters/lang/en_us.json +++ b/src/main/resources/assets/overdrive_that_matters/lang/en_us.json @@ -29,6 +29,11 @@ "otm.item.matter.infinite": "Stored matter: Infinity / Infinity", "otm.item.matter.normal": "Stored matter: %s / %s", + "otm.gui.matter_task.total": "Total: %s", + "otm.gui.matter_task.required": "Left to be done: %s", + "otm.gui.matter_task.in_progress": "In progress: %s", + "otm.gui.matter_task.finished": "Finished: %s", + "otm.suffix.merge": "%s %s", "otm.suffix.kilo": "%s k%s", @@ -66,8 +71,12 @@ "otm.container.matter_panel.increase_by": "+%s", "otm.container.matter_panel.decrease_by": "-%s", "otm.container.matter_panel.send": "Send", + "otm.container.matter_panel.close": "Close", + "otm.container.matter_panel.cancel_task": "Cancel task", "otm.container.matter_panel.cancel": "Cancel", "otm.container.matter_panel.label": "Replication request", + "otm.container.matter_panel.task": "Ongoing replication task", + "otm.container.matter_panel.task_line": "%s: %s | %s / %s", "item.overdrive_that_matters.pill_android": "Android pill", "item.overdrive_that_matters.pill_humane": "Humane pill", diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/matter_panel.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/matter_panel.png index 58546a545a00c0469a9340dbda114056da6ca994..c32d1892d6e14ef5ec8e5660bbf53acb831ba0aa 100644 GIT binary patch delta 8429 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

O$umj+CiJ%T3s9O|9 z|01!7Y2sS|wL}=bCAc!uc}R`Xp^6AOV{*h2!BI^0zsrucQyL z1YE`V&T(DhENb@ZrGo_%_vS1P&}0ablb{FGa-75`s6+WMp&WwXXh)!U6INbMtQ=H& zLMVmus(0e($#9!v^L?u`2R{K~dlkD1(_=lx1>MFUVD97xyt$2sXm$k2ZBToz0sO8l z+NgqjIQK}NW?D7MZJbNWyvc7U{DT6rV4YL@T<6pa6_aY`d9qK-A7TeF?YaSB{)jV= z3D~7#KRF>$TNbtSupX9WQ#{MAJB6rhsMjU)6WkLcxe|p^Qhp2D$iskn2fr7LGF-%4 z@iV}s@kx3U9(OmR-+MUy#3wri(EIdcT4aP2-UaY(lu;@YB%}Nh+Vg9GU!(jmbtoUs z&*bTxYLro)|7U?burB28e-DTulkxpd7{j+4&>DM?`N`P>1N%M(uoVM|KzD8Wkymft zL%eYmq=BL?TnG3asCKF#A1Vdxc3Biyst5 z+72Uai*>SMQj+gq7CsWKe+8{?lcsze`qqEB9DsX9yNYa+N!)H{tK6}LXR z{VBL3oxg#}+nD>7R-DsK4_I`wA|s&Sn!Lmck#>n#e=m45>uQj1NvEY&i>q0ng85t5`W=y5`S@4;unTeFVCgv4#;@Y%3{xmj-Kxef;xFvlIuy@|3Afv^Nw!!6W+!*%Ds99)3pLKa*{ z6d*!#X9brcj%qX)v<*>kDav!{Vw6R7NiIDfhnx@(+1v^9`JAjMOcjU%Qg%+ZK^C~J z+dwre*eEO_dV-~O;{tQSc1VL@an^{+z~#`2Cg#=E?{ojZI1n7z)_m#YJfRT3v-+X7od%aOvrJl zP9ik7pMwE88-@CFT+p`DPjZy6Wkm%di>kdRYRGd%VNrmiE-72s9B;B|d3jt7 zlrcv#?n;|)3Fg=bbL`V*1H)r|Fg(CqAJb+-=E%SWU`#a^9`s^=eW3G;38y> z%*7RjMS*72rE3r~p_wIhwx@A|=G~6e1e39ZDYt3JXsbU}f>Ng@_14ClNG92CO<3l)=OIqkq47kfg~FcLmin04gL&P zh?)$+$?@f4QfW}vr9(p}y%r*!zIL)>qtfKln7gkFNW%ELm