Swap item instruction (for now) & freezin

This commit is contained in:
skippyall
2026-01-29 21:44:52 +01:00
parent 5bd7c08f83
commit 1e430ef506
17 changed files with 185 additions and 65 deletions
@@ -26,6 +26,7 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
@@ -150,6 +151,11 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock,
return PolymerUtil.isOnClient(context) ? state : net.minecraft.block.Blocks.COMPARATOR.getDefaultState().with(AbstractRedstoneGateBlock.POWERED, state.get(POWERED));
}
@Override
public boolean handleMiningOnServer(ItemStack tool, BlockState state, BlockPos pos, ServerPlayerEntity player) {
return false;
}
@Override
public @Nullable ElementHolder createElementHolder(ServerWorld world, BlockPos pos, BlockState initialBlockState) {
ElementHolder holder = new ElementHolder() {
@@ -105,9 +105,9 @@ public class ConfigureInstructionGui extends InstructionBoundSimpleGui {
}
private void updateSuppliers() {
int slot = 11;
int slot = 12;
for(Parameter<?> parameter : instruction.getInstruction().getParameters().reversed()) {
setSlot(slot, InstructionGui.createParameterElement(parameter, player.getRegistryManager())
setSlot(slot, InstructionGui.createParameterElement(parameter, instruction.getArguments().getArgument(parameter), player.getRegistryManager())
.setCallback(() -> InstructionGui.configureArgumentMenu(name, instruction, parameter, minion, player))
);
slot--;
@@ -221,19 +221,12 @@ public class InstructionGui {
return instructionBuilder;
}
public static GuiElementBuilder createParameterElement(Parameter<?> parameter, DynamicRegistryManager manager) {
return new GuiElementBuilder(GuiDisplay.getDisplayStack(MinionRegistries.VALUE_TYPES, parameter.type(), manager))
public static GuiElementBuilder createParameterElement(Parameter<?> parameter, @Nullable ValueSupplier<?,?> valueSupplier, DynamicRegistryManager manager) {
GuiElementBuilder builder = new GuiElementBuilder(GuiDisplay.getDisplayStack(MinionRegistries.VALUE_TYPES, parameter.type(), manager))
.setName(Text.translatable("minions.gui.instruction.parameter", parameter.name(), Text.translatable(TranslationUtil.getTranslationKey(parameter.type(), MinionRegistries.VALUE_TYPES))));
}
public static GuiElementBuilder createArgumentElement(ValueSupplier<?, MinionRuntime> valueSupplier, DynamicRegistryManager manager) {
GuiElementBuilder argumentBuilder;
if (valueSupplier != null) {
argumentBuilder = new GuiElementBuilder(GuiDisplay.getDisplayStack(MinionRegistries.VALUE_SUPPLIER_TYPES, valueSupplier.getType(), manager));
} else {
argumentBuilder = new GuiElementBuilder(Items.RED_WOOL)
.setName(Text.translatable("minions.gui.instruction.no_argument_set"));
if(valueSupplier != null) {
builder.addLoreLine(Text.translatable("minions.gui.instruction.argument", valueSupplier.getDisplayText()));
}
return argumentBuilder;
return builder;
}
}
@@ -71,4 +71,14 @@ public class ChoiceInput {
gui.open();
return future;
}
public static BiFunction<ServerPlayerEntity, Boolean, CompletableFuture<Boolean>> inputBoolean(Text title) {
return createDialogOpener(ScreenHandlerType.GENERIC_3X3, title, value -> {
if(value) {
return new GuiDisplay.ItemBased(Items.EMERALD_BLOCK);
} else {
return new GuiDisplay.ItemBased(Items.REDSTONE_BLOCK);
}
}, new Boolean[]{false, true}, false);
}
}
@@ -28,6 +28,16 @@ public class ActionExecution implements ContinuousInstructionExecution<MinionRun
minion.getMinion().getMinionActionPack().stop(action);
}
@Override
public void pause(MinionRuntime runtime) {
runtime.getMinion().getMinionActionPack().stop(action);
}
@Override
public void resume(MinionRuntime runtime) {
runtime.getMinion().getMinionActionPack().start(action, EntityPlayerActionPack.Action.continuous());
}
@Override
public void readArguments(ValueSupplierList<MinionRuntime> parameters, MinionRuntime minion) {}
@@ -35,7 +45,7 @@ public class ActionExecution implements ContinuousInstructionExecution<MinionRun
public void save(WriteView view, MinionRuntime minion) {}
@Override
public void load(ReadView view, MinionRuntime minion) {
minion.getMinion().getMinionActionPack().start(action, EntityPlayerActionPack.Action.continuous());
public void load(ReadView view, MinionRuntime runtime) {
runtime.getMinion().getMinionActionPack().start(action, EntityPlayerActionPack.Action.continuous());
}
}
@@ -4,22 +4,29 @@ import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.consumer.ValueConsumerList;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
import io.github.skippyall.minions.program.supplier.Parameter;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
import net.minecraft.component.EnchantmentEffectComponentTypes;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.player.PlayerInventory;
import io.github.skippyall.minions.registration.ValueTypes;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
public class SwapItemExecution implements InstructionExecution<MinionRuntime> {
public static final Parameter<Long> FROM_SLOT = new Parameter<>("from_slot", ValueTypes.LONG);
public static final Parameter<Boolean> FROM_SCREEN = new Parameter<>("from_screen", ValueTypes.BOOLEAN);
public static final Parameter<Long> TO_SLOT = new Parameter<>("to_slot", ValueTypes.LONG);
public static final Parameter<Boolean> TO_SCREEN = new Parameter<>("to_screen", ValueTypes.BOOLEAN);
private int fromSlot;
private boolean fromScreen;
private int toSlot;
private boolean toScreen;
private ItemStack cursor = ItemStack.EMPTY;
@Override
public void start(MinionRuntime runtime) {
MinionFakePlayer minion = runtime.getMinion();
@@ -38,35 +45,38 @@ public class SwapItemExecution implements InstructionExecution<MinionRuntime> {
return;
}
simulateClick(minion, fromSlot, fromScreen);
simulateClick(minion, toSlot, toScreen);
simulateClick(minion, fromSlot, fromScreen);
minion.getInventory().offerOrDrop(cursor);
}
private ScreenHandler getScreen(MinionFakePlayer minion, boolean screen) {
if(screen) {
return minion.currentScreenHandler;
} else {
return minion.playerScreenHandler;
}
}
private boolean checkBounds(MinionFakePlayer minion, int slot, boolean screen) {
if(screen) {
return slot >= 0 && slot < minion.currentScreenHandler.slots.size();
} else {
return slot >= 0 && slot <= PlayerInventory.OFF_HAND_SLOT;
}
return slot >= 0 && slot < getScreen(minion, screen).slots.size();
}
private ItemStack getStack(MinionFakePlayer minion, int slot, boolean screen) {
if(screen) {
return minion.currentScreenHandler.getSlot(slot).getStack();
} else {
return minion.getInventory().getStack(slot);
}
return getScreen(minion, screen).getSlot(slot).getStack();
}
private boolean canExchange(MinionFakePlayer minion, int slotIndex, boolean screen, ItemStack newStack) {
if(screen) {
Slot slot = minion.currentScreenHandler.getSlot(slotIndex);
if(!slot.getStack().isEmpty() && !slot.canTakeItems(minion)) {
return false;
}
if(!newStack.isEmpty() && !slot.canInsert(newStack)) {
return false;
}
} else {
ScreenHandler screenHandler = getScreen(minion, screen);
Slot slot = screenHandler.getSlot(slotIndex);
if(!slot.getStack().isEmpty() && !slot.canTakeItems(minion)) {
return false;
}
if(!newStack.isEmpty() && !slot.canInsert(newStack)) {
return false;
}
/*else {
if(slotIndex >= PlayerInventory.MAIN_SIZE && slotIndex < PlayerInventory.OFF_HAND_SLOT) {
if(!minion.canEquip(newStack, PlayerInventory.EQUIPMENT_SLOTS.get(slotIndex))) {
return false;
@@ -75,15 +85,17 @@ public class SwapItemExecution implements InstructionExecution<MinionRuntime> {
return false;
}
}
}
}*/
return true;
}
private void simulateClick(MinionFakePlayer minion, int slotIndex, boolean screen) {
if(screen) {
minion.currentScreenHandler.onSlotClick(slotIndex, 0, SlotActionType.SWAP, minion);
} else {
}
ScreenHandler screenHandler = getScreen(minion, screen);
ItemStack previousCursor = screenHandler.getCursorStack();
screenHandler.setCursorStack(cursor);
screenHandler.onSlotClick(slotIndex, 0, SlotActionType.SWAP, minion);
cursor = screenHandler.getCursorStack();
screenHandler.setCursorStack(previousCursor);
}
@Override
@@ -93,7 +105,7 @@ public class SwapItemExecution implements InstructionExecution<MinionRuntime> {
@Override
public boolean isDone(MinionRuntime runtime) {
return false;
return true;
}
@Override
@@ -103,7 +115,10 @@ public class SwapItemExecution implements InstructionExecution<MinionRuntime> {
@Override
public void readArguments(ValueSupplierList<MinionRuntime> arguments, MinionRuntime runtime) {
fromSlot = Math.clamp(arguments.getValue(FROM_SLOT, runtime), 0, Integer.MAX_VALUE);
fromScreen = arguments.getValue(FROM_SCREEN, runtime);
toSlot = Math.clamp(arguments.getValue(TO_SLOT, runtime), 0, Integer.MAX_VALUE);
toScreen = arguments.getValue(TO_SCREEN, runtime);
}
@Override
@@ -36,11 +36,24 @@ public class MinionRuntime implements InstructionRuntime<MinionRuntime> {
}
public void disableInstructionType(InstructionType<MinionRuntime> instructionType) {
updatePausedStatus(instructionType);
}
public void enableInstructionType(InstructionType<MinionRuntime> instructionType) {
updatePausedStatus(instructionType);
}
public void updatePausedStatus(InstructionType<MinionRuntime> instructionType) {
for(ConfiguredInstruction<MinionRuntime> instruction : configuredInstructions.values()) {
if(instruction.getInstruction() == instructionType) {
instruction.updatePauseStatus(this);
}
}
}
@Override
public boolean isInstructionEnabled(InstructionType<MinionRuntime> type) {
return minion.getModuleInventory().hasInstruction(type);
}
public Set<String> getInstructionNames() {
@@ -13,10 +13,8 @@ import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
import net.minecraft.text.Text;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ModuleInventory extends SimpleInventory {
@@ -66,6 +64,12 @@ public class ModuleInventory extends SimpleInventory {
instructions.addAll(module.instructions());
specialAbilities.addAll(module.specialAbilities());
for(InstructionType<MinionRuntime> instructionType : module.instructions()) {
if(!oldInstructions.contains(instructionType)) {
minion.getInstructionManager().enableInstructionType(instructionType);
}
}
for(SpecialAbility ability : module.specialAbilities()) {
if(!oldAbilities.contains(ability)) {
ability.onAdd(minion);
@@ -104,11 +108,11 @@ public class ModuleInventory extends SimpleInventory {
return specialAbilities.contains(ability);
}
public List<InstructionType<?>> getAllInstructions() {
ArrayList<InstructionType<?>> instructionTypes = new ArrayList<>();
for(MinionModule module : modules) {
instructionTypes.addAll(module.instructions());
}
return instructionTypes;
public boolean hasInstruction(InstructionType<MinionRuntime> instructionType) {
return instructions.contains(instructionType);
}
public Collection<InstructionType<MinionRuntime>> getAllInstructions() {
return instructions;
}
}
@@ -17,6 +17,8 @@ public interface InstructionRuntime<R extends InstructionRuntime<R>> {
Registry<ValueConsumerType<R>> getValueConsumerTypeRegistry();
boolean isInstructionEnabled(InstructionType<R> type);
default Codec<ValueSupplierType<R>> getArgumentTypeCodec() {
return getArgumentTypeRegistry().getCodec();
}
@@ -16,12 +16,14 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
private final ValueSupplierList<R> arguments;
private final ValueConsumerList<R> valueConsumers;
private @Nullable InstructionExecution<R> execution;
private boolean paused = false;
private SerializableListenerManager<ConfiguredInstructionListener> listeners = new SerializableListenerManager<>(MinionRegistries.INSTRUCTION_LISTENER_CODECS);
private ConfiguredInstruction(InstructionType<R> instruction, ValueSupplierList<R> arguments, ValueConsumerList<R> valueConsumers, @Nullable InstructionExecution<R> execution, SerializableListenerManager<ConfiguredInstructionListener> listeners) {
private ConfiguredInstruction(InstructionType<R> instruction, ValueSupplierList<R> arguments, ValueConsumerList<R> valueConsumers, @Nullable InstructionExecution<R> execution, SerializableListenerManager<ConfiguredInstructionListener> listeners, boolean paused) {
this(instruction, arguments, valueConsumers, execution);
this.listeners = listeners;
this.paused = paused;
}
private ConfiguredInstruction(InstructionType<R> instruction, ValueSupplierList<R> arguments, ValueConsumerList<R> valueConsumers, @Nullable InstructionExecution<R> execution) {
@@ -71,7 +73,7 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
}
public void tick(R minion) {
if(execution != null) {
if(execution != null && !paused) {
if(execution.isDone(minion)) {
stop(minion);
} else {
@@ -91,6 +93,21 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
}
}
public void updatePauseStatus(R runtime) {
boolean typeEnabled = runtime.isInstructionEnabled(instruction);
if(typeEnabled && paused) {
paused = false;
if(execution != null) {
execution.resume(runtime);
}
} else if(!typeEnabled && !paused) {
paused = true;
if(execution != null) {
execution.pause(runtime);
}
}
}
private void onSupplierChange(Parameter<?> parameter) {
listeners.forEach(listener -> listener.onSupplierChange(this, parameter));
}
@@ -116,6 +133,7 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
view.put("arguments", minion.getArgumentListCodec(), arguments);
view.put("valueConsumers", minion.getValueConsumerListCodec(), valueConsumers);
view.putBoolean("running", isRunning());
view.putBoolean("paused", paused);
if(execution != null) {
execution.save(view.get("execution"), minion);
}
@@ -130,6 +148,7 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
ValueConsumerList<R> valueConsumers = view.read("valueConsumers", minion.getValueConsumerListCodec()).orElseGet(ValueConsumerList::new);
boolean running = view.getBoolean("running", false);
boolean paused = view.getBoolean("paused", false);
SerializableListenerManager<ConfiguredInstructionListener> listeners = new SerializableListenerManager<>(MinionRegistries.INSTRUCTION_LISTENER_CODECS);
listeners.load(view);
@@ -138,12 +157,12 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
ReadView executionView = view.getReadView("execution");
try {
InstructionExecution<R> execution = instructionType.loadExecution(executionView, minion);
return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, execution, listeners);
return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, execution, listeners, paused);
} catch (Exception e) {
Minions.LOGGER.error("Error while loading execution", e);
}
}
return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, null, listeners);
return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, null, listeners, paused);
}
}
@@ -31,6 +31,10 @@ public interface InstructionExecution<R extends InstructionRuntime<R>> {
*/
boolean isDone(R runtime);
default void pause(R runtime) {}
default void resume(R runtime) {}
/**
* Stops this execution. Is called when isDone returns true, but it may also be called before that.
* This should undo changes to the minion unless they are supposed to be permanent.
@@ -2,6 +2,7 @@ package io.github.skippyall.minions.program.supplier;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.value.ValueType;
import net.minecraft.text.Text;
/**
* A supplier that always resolves to a fixed value
@@ -35,4 +36,9 @@ public class FixedValueSupplier<T, R extends InstructionRuntime<R>> implements V
public FixedValueSupplierType<R> getType() {
return type;
}
@Override
public Text getDisplayText() {
return Text.translatable("value_supplier_type.minions.fixed.display", valueType.getDisplayText(value));
}
}
@@ -4,6 +4,7 @@ import com.mojang.serialization.Codec;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.value.ValueType;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
/**
@@ -19,6 +20,8 @@ public interface ValueSupplier<T, R extends InstructionRuntime<R>> {
ValueSupplierType<R> getType();
Text getDisplayText();
default <U,A extends ValueSupplier<U,R>> @Nullable A cast(ValueType<U> type) {
if(getValueType() == type) {
//noinspection unchecked
@@ -2,12 +2,18 @@ package io.github.skippyall.minions.program.value;
import com.mojang.serialization.Codec;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;
public record ValueType<T>(Codec<T> codec, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener) {
public record ValueType<T>(Codec<T> codec, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener, Function<T, Text> textDisplay) {
public CompletableFuture<T> openValueDialog(ServerPlayerEntity player, T previousValue) {
return valueDialogOpener.apply(player, previousValue);
}
public Text getDisplayText(T value) {
return textDisplay.apply(value);
}
}
@@ -1,5 +1,6 @@
package io.github.skippyall.minions.registration;
import io.github.skippyall.minions.instruction.inventory.SwapItemExecution;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
import io.github.skippyall.minions.program.instruction.InstructionType;
import io.github.skippyall.minions.Minions;
@@ -57,6 +58,12 @@ public class Instructions {
() -> new ActionExecution(EntityPlayerActionPack.ActionType.USE)
);
public static final InstructionType<MinionRuntime> SWAP_ITEM = register(
"swap_item",
SwapItemExecution::new,
List.of(SwapItemExecution.FROM_SLOT, SwapItemExecution.FROM_SCREEN, SwapItemExecution.TO_SLOT, SwapItemExecution.TO_SCREEN)
);
private static InstructionType<MinionRuntime> register(String id, Supplier<InstructionExecution<MinionRuntime>> factory, Collection<Parameter<?>> parameters, Collection<Parameter<?>> returnParameters) {
Identifier identifier = Identifier.of(Minions.MOD_ID, id);
return Registry.register(MinionRegistries.INSTRUCTION_TYPES, identifier, new InstructionType<>(factory, parameters, returnParameters));
@@ -13,6 +13,7 @@ import net.minecraft.util.Identifier;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;
public class ValueTypes {
public static ValueType<Long> LONG = registerSimple(
@@ -23,7 +24,8 @@ public class ValueTypes {
player,
Text.literal("Integer"),
String.valueOf(oldValue)
)
),
value -> Text.literal(value.toString())
);
public static ValueType<Double> DOUBLE = registerSimple(
@@ -34,7 +36,16 @@ public class ValueTypes {
player,
Text.literal("Number"),
String.valueOf(oldValue)
)
),
value -> Text.literal(value.toString())
);
public static ValueType<Boolean> BOOLEAN = registerSimple(
"boolean",
Codec.BOOL,
false,
ChoiceInput.inputBoolean(Text.literal("")),
value -> Text.literal(value.toString())
);
public static ValueType<String> STRING = registerSimple(
@@ -45,17 +56,25 @@ public class ValueTypes {
player,
Text.literal("Text"),
oldValue)
)
),
value -> Text.literal("\"" + value + "\"")
);
public static ValueType<TurnDirection> TURN_DIRECTION = registerSimple(
"turn_direction",
TurnDirection.CODEC,
TurnDirection.RIGHT,
ChoiceInput.createDialogOpener(TurnDirection.values())
ChoiceInput.createDialogOpener(TurnDirection.values()),
value -> Text.literal(value.name)
);
private static <T> ValueType<T> registerSimple(String id, Codec<T> codec, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener) {
private static <T> ValueType<T> registerSimple(
String id,
Codec<T> codec,
T defaultValue,
BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener,
Function<T, Text> textDisplay
) {
Identifier identifier = Identifier.of(Minions.MOD_ID, id);
return Registry.register(
MinionRegistries.VALUE_TYPES,
@@ -63,7 +82,8 @@ public class ValueTypes {
new ValueType<>(
codec,
defaultValue,
valueDialogOpener
valueDialogOpener,
textDisplay
)
);
}