diff --git a/build.gradle b/build.gradle index c04a4fb..f6687dd 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,11 @@ minecraft { // Default run configurations. // These can be tweaked, removed, or duplicated as needed. runs { + configureEach { + property 'mixin.env.remapRefMap', 'true' + property 'mixin.env.refMapRemappingFile', "${buildDir}/createSrgToMcp/output.srg" + } + client { workingDirectory project.file('run') @@ -123,6 +128,13 @@ repositories { // flatDir { // dir 'libs' // } + + maven { + url "https://squiddev.cc/maven/" + content { + includeGroup("org.squiddev") + } + } } dependencies { @@ -142,6 +154,14 @@ dependencies { // For more info... // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html // http://www.gradle.org/docs/current/userguide/dependency_management.html + + // Vanilla (i.e. for multi-loader systems) +// compileOnly("cc.tweaked:cc-tweaked-${minecraft_version}-common-api:${cct_version}") + + // Forge Gradle +// compileOnly("cc.tweaked:cc-tweaked-${minecraft_version}-core-api:${cct_version}") +// compileOnly(fg.deobf("cc.tweaked:cc-tweaked-${minecraft_version}-forge-api:${cct_version}")) + implementation fg.deobf("org.squiddev:cc-tweaked-${minecraft_version}:${cct_version}") } // This task will expand all declared properties from Gradle (gradle.properties) in the specified resource targets. @@ -158,7 +178,8 @@ tasks.named('processResources', ProcessResources).configure { mod_license: mod_license, mod_version: mod_version, mod_authors: mod_authors, - mod_description: mod_description + mod_description: mod_description, + cct_version: cct_version ] inputs.properties replaceProperties diff --git a/gradle.properties b/gradle.properties index 093f922..e5cfdac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -35,6 +35,11 @@ mapping_channel=official # This must match the format required by the mapping channel. mapping_version=1.19.2 +## Dependency Properties + +# The version of CC:Tweaked we're building against +cct_version=1.101.3 + ## Mod Properties # The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} diff --git a/src/main/java/net/banutama/utamacraft/CCRegistration.java b/src/main/java/net/banutama/utamacraft/CCRegistration.java new file mode 100644 index 0000000..e69fec8 --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/CCRegistration.java @@ -0,0 +1,35 @@ +package net.banutama.utamacraft; + +import com.mojang.logging.LogUtils; +import dan200.computercraft.api.ForgeComputerCraftAPI; +import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import net.banutama.utamacraft.integrations.computercraft.PeripheralProvider; +import net.banutama.utamacraft.integrations.computercraft.turtles.TurtlePlayerUpgrade; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.RegistryObject; +import org.slf4j.Logger; + +/** + * CC:Tweaked registration + */ +public class CCRegistration { + + public static final DeferredRegister> TURTLE_SERIALIZERS = + DeferredRegister.create(TurtleUpgradeSerialiser.REGISTRY_ID, Utamacraft.MOD_ID); + + public static final RegistryObject> PLAYER_TURTLE = + TURTLE_SERIALIZERS.register(ID.PLAYER_TURTLE.getPath(), () -> TurtleUpgradeSerialiser.simpleWithCustomItem(TurtlePlayerUpgrade::new)); + + public static PeripheralProvider peripheralProvider = new PeripheralProvider(); + + public static void register(IEventBus bus) { + TURTLE_SERIALIZERS.register(bus); + ForgeComputerCraftAPI.registerPeripheralProvider(peripheralProvider); + } + + public static class ID { + public static final ResourceLocation PLAYER_TURTLE = new ResourceLocation(Utamacraft.MOD_ID, "player_turtle"); + } +} diff --git a/src/main/java/net/banutama/utamacraft/Utamacraft.java b/src/main/java/net/banutama/utamacraft/Utamacraft.java index 4e2bdae..3d366fb 100644 --- a/src/main/java/net/banutama/utamacraft/Utamacraft.java +++ b/src/main/java/net/banutama/utamacraft/Utamacraft.java @@ -28,6 +28,7 @@ public class Utamacraft { ModItems.register(bus); ModBlocks.register(bus); + CCRegistration.register(bus); bus.addListener(this::commonSetup); MinecraftForge.EVENT_BUS.register(this); @@ -37,7 +38,7 @@ public class Utamacraft { } @Mod.EventBusSubscriber(modid = MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) - public static class ClientModEvents { + public static class ModEvents { @SubscribeEvent public static void onClientSetup(FMLClientSetupEvent event) { } diff --git a/src/main/java/net/banutama/utamacraft/block/ModBlocks.java b/src/main/java/net/banutama/utamacraft/block/ModBlocks.java index 5d9e248..77a1678 100644 --- a/src/main/java/net/banutama/utamacraft/block/ModBlocks.java +++ b/src/main/java/net/banutama/utamacraft/block/ModBlocks.java @@ -19,7 +19,7 @@ public class ModBlocks { DeferredRegister.create(ForgeRegistries.BLOCKS, Utamacraft.MOD_ID); public static final RegistryObject ETHEREAL_GLASS = - registerBlock("ethereal_glass", () -> new EtherealGlass()); + registerBlock("ethereal_glass", EtherealGlass::new); private static RegistryObject registerBlock(String name, Supplier block) { RegistryObject registered_block = BLOCKS.register(name, block); diff --git a/src/main/java/net/banutama/utamacraft/client/ClientRegistry.java b/src/main/java/net/banutama/utamacraft/client/ClientRegistry.java new file mode 100644 index 0000000..044122c --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/client/ClientRegistry.java @@ -0,0 +1,18 @@ +package net.banutama.utamacraft.client; + +import dan200.computercraft.api.client.ComputerCraftAPIClient; +import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; +import net.banutama.utamacraft.CCRegistration; +import net.banutama.utamacraft.Utamacraft; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; + +@Mod.EventBusSubscriber(modid = Utamacraft.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) +public class ClientRegistry { + @SubscribeEvent + public static void onClientSetup(FMLClientSetupEvent event) { + ComputerCraftAPIClient.registerTurtleUpgradeModeller(CCRegistration.PLAYER_TURTLE.get(), TurtleUpgradeModeller.flatItem()); + } +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/BoundMethod.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/BoundMethod.java new file mode 100644 index 0000000..f788502 --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/BoundMethod.java @@ -0,0 +1,49 @@ +package net.banutama.utamacraft.integrations.computercraft; + +import dan200.computercraft.api.lua.*; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.core.asm.NamedMethod; +import dan200.computercraft.core.asm.PeripheralMethod; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public class BoundMethod { + private final Object target; + private final String name; + private final PeripheralMethod method; + + public BoundMethod(@NotNull Object target, @NotNull NamedMethod method) { + this.target = target; + this.name = method.getName(); + this.method = method.getMethod(); + } + + @NotNull + public String getName() { + return name; + } + + @NotNull + public MethodResult apply(@NotNull IComputerAccess computer, @NotNull ILuaContext context, @NotNull IArguments arguments) throws LuaException { + return method.apply(target, context, computer, arguments); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (!(other instanceof BoundMethod boundMethod)) { + return false; + } + + return target.equals(boundMethod.target) && name.equals(boundMethod.name) && method.equals(boundMethod.method); + } + + @Override + public int hashCode() { + return Objects.hash(target, name, method); + } +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/PeripheralProvider.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/PeripheralProvider.java new file mode 100644 index 0000000..f7fe02a --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/PeripheralProvider.java @@ -0,0 +1,17 @@ +package net.banutama.utamacraft.integrations.computercraft; + +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.peripheral.IPeripheralProvider; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraftforge.common.util.LazyOptional; +import org.jetbrains.annotations.NotNull; + +public class PeripheralProvider implements IPeripheralProvider { + @NotNull + @Override + public LazyOptional getPeripheral(@NotNull Level world, @NotNull BlockPos pos, @NotNull Direction side) { + return LazyOptional.empty(); + } +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/BasePeripheral.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/BasePeripheral.java new file mode 100644 index 0000000..2d5b755 --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/BasePeripheral.java @@ -0,0 +1,72 @@ +package net.banutama.utamacraft.integrations.computercraft.peripheral; + +import dan200.computercraft.api.lua.IArguments; +import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IDynamicPeripheral; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.core.asm.PeripheralMethod; +import net.banutama.utamacraft.integrations.computercraft.BoundMethod; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +public abstract class BasePeripheral implements IPeripheral, IDynamicPeripheral { + protected final String type; + protected final BasePeripheralOwner owner; + protected final List methods; + protected final Set computers = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + protected BasePeripheral(String type, BasePeripheralOwner owner) { + this.type = type; + this.owner = owner; + this.methods = PeripheralMethod.GENERATOR.getMethods(this.getClass()).stream().map(named -> new BoundMethod(this, named)).collect(Collectors.toList()); + } + + @NotNull + @Override + public String @NotNull [] getMethodNames() { + return methods.stream().map(BoundMethod::getName).toArray(String[]::new); + } + + @NotNull + @Override + public MethodResult callMethod(@NotNull IComputerAccess computer, @NotNull ILuaContext context, int method, @NotNull IArguments arguments) throws LuaException { + return methods.get(method).apply(computer, context, arguments); + } + + @NotNull + @Override + public String getType() { + return type; + } + + @Override + public void attach(@NotNull IComputerAccess computer) { + this.computers.add(computer); + } + + @Override + public void detach(@NotNull IComputerAccess computer) { + this.computers.remove(computer); + } + + @Nullable + @Override + public Object getTarget() { + return owner; + } + + @Override + public boolean equals(@Nullable IPeripheral other) { + return Objects.equals(this, other); + } +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/BasePeripheralOwner.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/BasePeripheralOwner.java new file mode 100644 index 0000000..421696d --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/BasePeripheralOwner.java @@ -0,0 +1,4 @@ +package net.banutama.utamacraft.integrations.computercraft.peripheral; + +public class BasePeripheralOwner { +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/BlockEntityPeripheralOwner.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/BlockEntityPeripheralOwner.java new file mode 100644 index 0000000..57de81b --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/BlockEntityPeripheralOwner.java @@ -0,0 +1,5 @@ +package net.banutama.utamacraft.integrations.computercraft.peripheral; + +public class BlockEntityPeripheralOwner extends BasePeripheralOwner { + // TODO: After we add PeripheralBlockEntity we want to add it to a field and constructor here. +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/PlayerPeripheral.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/PlayerPeripheral.java new file mode 100644 index 0000000..2dd9876 --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/PlayerPeripheral.java @@ -0,0 +1,36 @@ +package net.banutama.utamacraft.integrations.computercraft.peripheral; + +import com.mojang.logging.LogUtils; +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.TurtleSide; +import net.banutama.utamacraft.integrations.computercraft.turtles.TurtlePlayerCache; +import net.minecraft.world.InteractionResult; +import org.slf4j.Logger; + +public class PlayerPeripheral extends BasePeripheral { + public static final String PERIPHERAL_TYPE = "player"; + private static final Logger LOGGER = LogUtils.getLogger(); + + protected PlayerPeripheral(BasePeripheralOwner owner) { + super(PERIPHERAL_TYPE, owner); + } + + public PlayerPeripheral(ITurtleAccess turtle, TurtleSide side) { + this(new TurtlePeripheralOwner(turtle, side)); + } + + @LuaFunction(mainThread = true) + public final MethodResult use() { + if (!(owner instanceof TurtlePeripheralOwner turtleOwner)) { + LOGGER.info("Owner of this PlayerPeripheral is not a TurtlePeripheralOwner"); + return MethodResult.of(); + } + + InteractionResult result = TurtlePlayerCache.withPlayer(turtleOwner.getTurtle(), + player -> player.use(5, true, false, null)); + + return MethodResult.of(result.name()); + } +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/PocketPeripheralOwner.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/PocketPeripheralOwner.java new file mode 100644 index 0000000..850c852 --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/PocketPeripheralOwner.java @@ -0,0 +1,4 @@ +package net.banutama.utamacraft.integrations.computercraft.peripheral; + +public class PocketPeripheralOwner extends BasePeripheralOwner { +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/TurtlePeripheralOwner.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/TurtlePeripheralOwner.java new file mode 100644 index 0000000..c46b29a --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/peripheral/TurtlePeripheralOwner.java @@ -0,0 +1,18 @@ +package net.banutama.utamacraft.integrations.computercraft.peripheral; + +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.TurtleSide; + +public class TurtlePeripheralOwner extends BasePeripheralOwner { + private final ITurtleAccess turtle; + private final TurtleSide side; + + public TurtlePeripheralOwner(ITurtleAccess turtle, TurtleSide side) { + this.turtle = turtle; + this.side = side; + } + + public ITurtleAccess getTurtle() { + return turtle; + } +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/turtles/PeripheralTurtleUpgrade.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/turtles/PeripheralTurtleUpgrade.java new file mode 100644 index 0000000..871531d --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/turtles/PeripheralTurtleUpgrade.java @@ -0,0 +1,27 @@ +package net.banutama.utamacraft.integrations.computercraft.turtles; + +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.turtle.AbstractTurtleUpgrade; +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.api.turtle.TurtleUpgradeType; +import net.banutama.utamacraft.integrations.computercraft.peripheral.BasePeripheral; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class PeripheralTurtleUpgrade extends AbstractTurtleUpgrade { + + protected PeripheralTurtleUpgrade(ResourceLocation id, ItemStack item) { + super(id, TurtleUpgradeType.PERIPHERAL, String.format("turtle.utamacraft.%s", id.getPath()), item); + } + + protected abstract T buildPeripheral(@NotNull ITurtleAccess turtle, @NotNull TurtleSide side); + + @Nullable + @Override + public IPeripheral createPeripheral(@NotNull ITurtleAccess turtle, @NotNull TurtleSide side) { + return buildPeripheral(turtle, side); + } +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/turtles/TurtlePlayerCache.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/turtles/TurtlePlayerCache.java new file mode 100644 index 0000000..cb86aae --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/turtles/TurtlePlayerCache.java @@ -0,0 +1,52 @@ +package net.banutama.utamacraft.integrations.computercraft.turtles; + +import com.mojang.authlib.GameProfile; +import dan200.computercraft.api.turtle.ITurtleAccess; +import net.banutama.utamacraft.util.FakeGameProfile; +import net.banutama.utamacraft.util.SimpleFakePlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; +import net.minecraft.server.level.ServerLevel; + +import java.util.WeakHashMap; +import java.util.function.Function; + +public class TurtlePlayerCache { + private static final WeakHashMap PLAYERS = new WeakHashMap<>(); + + private static SimpleFakePlayer getPlayerFor(ITurtleAccess turtle, GameProfile profile) { + SimpleFakePlayer player = PLAYERS.get(turtle); + if (player == null) { + player = new SimpleFakePlayer((ServerLevel)turtle.getLevel(), profile); + PLAYERS.put(turtle, player); + } + + return player; + } + + public static T withPlayer(ITurtleAccess turtle, Function callback) { + GameProfile profile = turtle.getOwningPlayer(); + if (profile == null) { + profile = new FakeGameProfile(); + } + + SimpleFakePlayer player = getPlayerFor(turtle, profile); + BlockPos position = turtle.getPosition(); + Direction direction = turtle.getDirection(); + + float pitch = direction == Direction.UP ? -90.0f : direction == Direction.DOWN ? 90.0f : 0.0f; + float yaw = direction == Direction.SOUTH ? 0.0f : direction == Direction.WEST ? 90.0f : direction == Direction.NORTH ? 180.0f : -90.0f; + + Vec3i normal = direction.getNormal(); + Direction.Axis axis = direction.getAxis(); + Direction.AxisDirection axis_dir = direction.getAxisDirection(); + + double x = (axis == Direction.Axis.X && axis_dir == Direction.AxisDirection.NEGATIVE) ? -0.5 : (0.5 * normal.getX() / 1.9); + double y = 0.5 + normal.getY() / 1.9; + double z = (axis == Direction.Axis.Z && axis_dir == Direction.AxisDirection.NEGATIVE) ? -0.5 : (0.5 + normal.getZ() / 1.9); + + player.moveTo(position.getX() + x, position.getY() + y, position.getZ() + z, yaw, pitch); + return callback.apply(player); + } +} diff --git a/src/main/java/net/banutama/utamacraft/integrations/computercraft/turtles/TurtlePlayerUpgrade.java b/src/main/java/net/banutama/utamacraft/integrations/computercraft/turtles/TurtlePlayerUpgrade.java new file mode 100644 index 0000000..d61368a --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/integrations/computercraft/turtles/TurtlePlayerUpgrade.java @@ -0,0 +1,24 @@ +package net.banutama.utamacraft.integrations.computercraft.turtles; + +import com.mojang.logging.LogUtils; +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.TurtleSide; +import net.banutama.utamacraft.integrations.computercraft.peripheral.PlayerPeripheral; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; + +public class TurtlePlayerUpgrade extends PeripheralTurtleUpgrade { + private static final Logger LOGGER = LogUtils.getLogger(); + + public TurtlePlayerUpgrade(ResourceLocation id, ItemStack item) { + super(id, item); + } + + @Override + protected PlayerPeripheral buildPeripheral(@NotNull ITurtleAccess turtle, @NotNull TurtleSide side) { + LOGGER.info("Building PlayerPeripheral for TurtlePlayerUpgrade"); + return new PlayerPeripheral(turtle, side); + } +} diff --git a/src/main/java/net/banutama/utamacraft/item/BaseItem.java b/src/main/java/net/banutama/utamacraft/item/BaseItem.java new file mode 100644 index 0000000..118a4a1 --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/item/BaseItem.java @@ -0,0 +1,10 @@ +package net.banutama.utamacraft.item; + +import net.minecraft.world.item.Item; +import org.jetbrains.annotations.NotNull; + +public abstract class BaseItem extends Item { + public BaseItem(@NotNull Properties properties) { + super(properties.tab(ModCreativeModeTab.TAB)); + } +} diff --git a/src/main/java/net/banutama/utamacraft/item/ModCreativeModeTab.java b/src/main/java/net/banutama/utamacraft/item/ModCreativeModeTab.java index e3cc5b4..b27f1db 100644 --- a/src/main/java/net/banutama/utamacraft/item/ModCreativeModeTab.java +++ b/src/main/java/net/banutama/utamacraft/item/ModCreativeModeTab.java @@ -3,7 +3,6 @@ package net.banutama.utamacraft.item; import net.banutama.utamacraft.Utamacraft; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import org.jetbrains.annotations.NotNull; @@ -11,7 +10,7 @@ import org.jetbrains.annotations.NotNull; public class ModCreativeModeTab { public static final CreativeModeTab TAB = new CreativeModeTab("utamacraft_tab") { @Override - public ItemStack makeIcon() { + public @NotNull ItemStack makeIcon() { return new ItemStack(ModItems.LOGO.get()); } }; diff --git a/src/main/java/net/banutama/utamacraft/item/ModItems.java b/src/main/java/net/banutama/utamacraft/item/ModItems.java index 3f45ec1..110028c 100644 --- a/src/main/java/net/banutama/utamacraft/item/ModItems.java +++ b/src/main/java/net/banutama/utamacraft/item/ModItems.java @@ -11,8 +11,11 @@ public class ModItems { public static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, Utamacraft.MOD_ID); - public static final RegistryObject LOGO = ITEMS.register("utamacraft_logo", - () -> new Item(new Item.Properties())); + public static final RegistryObject LOGO = + ITEMS.register("utamacraft_logo", () -> new Item(new Item.Properties())); + + public static final RegistryObject PLAYER_PERIPHERAL = + ITEMS.register("player_peripheral", PlayerPeripheralItem::new); public static void register(IEventBus eventBus) { ITEMS.register(eventBus); diff --git a/src/main/java/net/banutama/utamacraft/item/PlayerPeripheralItem.java b/src/main/java/net/banutama/utamacraft/item/PlayerPeripheralItem.java new file mode 100644 index 0000000..5ba14cb --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/item/PlayerPeripheralItem.java @@ -0,0 +1,30 @@ +package net.banutama.utamacraft.item; + +import dan200.computercraft.shared.Registry; +import net.banutama.utamacraft.CCRegistration; +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class PlayerPeripheralItem extends BaseItem { + public PlayerPeripheralItem() { + super(new Item.Properties().stacksTo(16)); + } + + @Override + public void fillItemCategory(@NotNull CreativeModeTab group, @NotNull NonNullList items) { + super.fillItemCategory(group, items); + + if (allowedIn(group)) { + ItemStack turtle_stack = new ItemStack(Registry.ModItems.TURTLE_NORMAL.get()); + turtle_stack.getOrCreateTag().putString("RightUpgrade", CCRegistration.ID.PLAYER_TURTLE.toString()); + items.add(turtle_stack); + + ItemStack advanced_turtle_stack = new ItemStack(Registry.ModItems.TURTLE_ADVANCED.get()); + advanced_turtle_stack.getOrCreateTag().putString("RightUpgrade", CCRegistration.ID.PLAYER_TURTLE.toString()); + items.add(advanced_turtle_stack); + } + } +} diff --git a/src/main/java/net/banutama/utamacraft/util/FakeGameProfile.java b/src/main/java/net/banutama/utamacraft/util/FakeGameProfile.java new file mode 100644 index 0000000..73c2f67 --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/util/FakeGameProfile.java @@ -0,0 +1,16 @@ +package net.banutama.utamacraft.util; + +import com.mojang.authlib.GameProfile; +import net.banutama.utamacraft.Utamacraft; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +public class FakeGameProfile extends GameProfile { + private static final UUID GAME_PROFILE_UUID = UUID.nameUUIDFromBytes("Utamacraft".getBytes(StandardCharsets.UTF_8)); + private static final String GAME_PROFILE_NAME = String.format("[%s]", Utamacraft.MOD_ID); + + public FakeGameProfile() { + super(GAME_PROFILE_UUID, GAME_PROFILE_NAME); + } +} diff --git a/src/main/java/net/banutama/utamacraft/util/SimpleFakePlayer.java b/src/main/java/net/banutama/utamacraft/util/SimpleFakePlayer.java new file mode 100644 index 0000000..e80329f --- /dev/null +++ b/src/main/java/net/banutama/utamacraft/util/SimpleFakePlayer.java @@ -0,0 +1,192 @@ +package net.banutama.utamacraft.util; + +import com.mojang.authlib.GameProfile; +import net.banutama.utamacraft.Utamacraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.phys.*; +import net.minecraftforge.common.util.FakePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Predicate; + +public class SimpleFakePlayer extends FakePlayer { + private static WeakReference INSTANCE; + + public SimpleFakePlayer(ServerLevel world, GameProfile profile) { + super(world, profile); + } + + public SimpleFakePlayer(ServerLevel world) { + this(world, new FakeGameProfile()); + } + + public static R withFakePlayer(ServerLevel world, Function consumer) { + SimpleFakePlayer player = INSTANCE == null ? null : INSTANCE.get(); + if (player == null) { + player = new SimpleFakePlayer(world); + INSTANCE = new WeakReference<>(player); + } + + return consumer.apply(player); + } + + public HitResult findHit(int range, boolean skipEntity, boolean skipBlock, Predicate entityFilter) { + Vec3 origin = new Vec3(getX(), getY(), getZ()); + Vec3 look = getLookAngle(); + Vec3 target = new Vec3(origin.x + look.x * range, origin.y + look.y * range, origin.z + look.z * range); + ClipContext traceContext = new ClipContext(origin, target, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this); + Vec3 directionVec = traceContext.getFrom().subtract(traceContext.getTo()); + Direction traceDirection = Direction.getNearest(directionVec.x, directionVec.y, directionVec.z); + + HitResult blockHit = null; + if (skipBlock) { + Vec3 to = traceContext.getTo(); + blockHit = BlockHitResult.miss(to, traceDirection, new BlockPos(to)); + } else { + blockHit = BlockGetter.traverseBlocks(traceContext.getFrom(), traceContext.getTo(), traceContext, (clipContext, pos) -> { + if (level.isEmptyBlock(pos)) { + return null; + } + + return new BlockHitResult(new Vec3(pos.getX(), pos.getY(), pos.getZ()), traceDirection, pos, false); + }, clipContext -> BlockHitResult.miss(clipContext.getTo(), traceDirection, new BlockPos(clipContext.getTo()))); + } + + if (skipEntity) { + return blockHit; + } + + List entities = + level.getEntities( + this, + this.getBoundingBox() + .expandTowards(look.x * range, look.y * range, look.z * range) + .inflate(1.0, 1.0, 1.0), + EntitySelector.NO_SPECTATORS); + + LivingEntity closestEntity = null; + Vec3 closestVec = null; + double closestDistance = 0.0; + + for (Entity entityHit : entities) { + if (!(entityHit instanceof LivingEntity living)) { + continue; + } + + if (entityFilter != null && !entityFilter.test(entityHit)) { + continue; + } + + AABB box = entityHit.getBoundingBox().inflate(entityHit.getPickRadius() + 0.5); + Optional clipResult = box.clip(origin, target); + + if (box.contains(origin)) { + if (closestDistance >= 0.0) { + closestEntity = living; + closestVec = clipResult.orElse(origin); + closestDistance = 0.0; + } + } else if (clipResult.isPresent()) { + Vec3 clipVec = clipResult.get(); + double distance = origin.distanceTo(clipVec); + + if (distance < closestDistance || closestDistance == 0.0) { + if (entityHit == entityHit.getRootVehicle()) { + if (closestDistance == 0.0) { + closestEntity = living; + closestVec = clipVec; + } + } else { + closestEntity = living; + closestVec = clipVec; + closestDistance = distance; + } + } + } + } + + if (closestEntity != null && + closestDistance <= range && + (blockHit.getType() == HitResult.Type.MISS || + distanceToSqr(blockHit.getLocation()) > closestDistance * closestDistance)) { + return new EntityHitResult(closestEntity, closestVec); + } else { + return blockHit; + } + } + + public InteractionResult use(int range, boolean skipEntity, boolean skipBlock, Predicate entityFilter) { + HitResult hit = findHit(range, skipEntity, skipBlock, entityFilter); + if (hit instanceof BlockHitResult blockHit) { + InteractionResult res = + gameMode.useItemOn(this, level, getMainHandItem(), InteractionHand.MAIN_HAND, blockHit); + if (res.consumesAction()) { + return res; + } + + return gameMode.useItem(this, level, getMainHandItem(), InteractionHand.MAIN_HAND); + } else if (hit instanceof EntityHitResult) { + // TODO: Interact with an entity? + return InteractionResult.FAIL; + } else { + return InteractionResult.FAIL; + } + } + + // + // Overrides to compensate for FakePlayer + // + + @Override + public boolean canBeAffected(@NotNull MobEffectInstance effect) { + return false; + } + + @Override + public boolean canHarmPlayer(Player player) { + return true; + } + + @Override + public boolean startRiding(@NotNull Entity entity, boolean b) { + return false; + } + + @Override + public @NotNull OptionalInt openMenu(@Nullable MenuProvider provider) { + return OptionalInt.empty(); + } + + @Override + public float getStandingEyeHeight(@NotNull Pose pose, @NotNull EntityDimensions dimensions) { + return 0.0f; + } + + @Override + public double getEyeY() { + return getY() + 0.2; + } + + @Override + public float getAttackStrengthScale(float f) { + return 1.0f; + } +} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index f88b91c..49f9085 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -46,7 +46,6 @@ authors="${mod_authors}" #optional # The description text for the mod (multi line!) (#mandatory) description='''${mod_description}''' -# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. [[dependencies.${mod_id}]] #optional # the modid of the dependency modId="forge" #mandatory @@ -58,11 +57,17 @@ description='''${mod_description}''' ordering="NONE" # Side this dependency is applied on - BOTH, CLIENT or SERVER side="BOTH" -# Here's another dependency + [[dependencies.${mod_id}]] modId="minecraft" mandatory=true -# This version range declares a minimum of the current minecraft version up to but not including the next major version versionRange="${minecraft_version_range}" ordering="NONE" side="BOTH" + +[[dependencies.${mod_id}]] +modId = "computercraft" +mandatory = true +versionRange = "[${cct_version},)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/src/main/resources/assets/utamacraft/lang/en_us.json b/src/main/resources/assets/utamacraft/lang/en_us.json index f5eeb1b..445fe9a 100644 --- a/src/main/resources/assets/utamacraft/lang/en_us.json +++ b/src/main/resources/assets/utamacraft/lang/en_us.json @@ -1,6 +1,8 @@ { + "block.utamacraft.ethereal_glass": "Ethereal Glass", + "item.utamacraft.player_peripheral": "Player peripheral", "item.utamacraft.utamacraft_logo": "Utamacraft", "itemGroup.utamacraft_tab": "Utamacraft", - "block.utamacraft.ethereal_glass": "Ethereal Glass", - "tooltip.utamacraft.ethereal_glass": "Glass that is not solid to players" + "tooltip.utamacraft.ethereal_glass": "Glass that is not solid to players", + "turtle.utamacraft.player_turtle": "Player" } diff --git a/src/main/resources/assets/utamacraft/models/item/player_peripheral.json b/src/main/resources/assets/utamacraft/models/item/player_peripheral.json new file mode 100644 index 0000000..94003da --- /dev/null +++ b/src/main/resources/assets/utamacraft/models/item/player_peripheral.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "utamacraft:item/player_peripheral" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/utamacraft/textures/item/player_peripheral.png b/src/main/resources/assets/utamacraft/textures/item/player_peripheral.png new file mode 100644 index 0000000..4c7615f Binary files /dev/null and b/src/main/resources/assets/utamacraft/textures/item/player_peripheral.png differ diff --git a/src/main/resources/data/utamacraft/computercraft/turtle_upgrades/player_turtle.json b/src/main/resources/data/utamacraft/computercraft/turtle_upgrades/player_turtle.json new file mode 100644 index 0000000..63ce426 --- /dev/null +++ b/src/main/resources/data/utamacraft/computercraft/turtle_upgrades/player_turtle.json @@ -0,0 +1,4 @@ +{ + "type": "utamacraft:player_turtle", + "item": "utamacraft:player_peripheral" +} \ No newline at end of file diff --git a/src/main/resources/data/utamacraft/recipes/player_peripheral.json b/src/main/resources/data/utamacraft/recipes/player_peripheral.json new file mode 100644 index 0000000..d9931a0 --- /dev/null +++ b/src/main/resources/data/utamacraft/recipes/player_peripheral.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "GRG", + "R.R", + "GRG" + ], + "key": { + "G": { + "item": "minecraft:gold_ingot" + }, + "R": { + "item": "minecraft:redstone" + }, + ".": { + "item": "minecraft:ender_eye" + } + }, + "result": { + "item": "utamacraft:player_peripheral", + "count": 1 + } +} diff --git a/textures/player_peripheral.afdesign b/textures/player_peripheral.afdesign new file mode 100644 index 0000000..a3450b6 Binary files /dev/null and b/textures/player_peripheral.afdesign differ