Add the player-use peripheral for CC:Tweaked #9

Merged
BlakeRain merged 3 commits from BlakeRain/utamacraft:cc-playerish-peripheral into main 2023-11-27 16:16:56 +00:00
26 changed files with 654 additions and 7 deletions
Showing only changes of commit 55c5016610 - Show all commits

View File

@ -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<TurtleUpgradeSerialiser<?>> TURTLE_SERIALIZERS =
DeferredRegister.create(TurtleUpgradeSerialiser.REGISTRY_ID, Utamacraft.MOD_ID);
public static final RegistryObject<TurtleUpgradeSerialiser<TurtlePlayerUpgrade>> 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");
}
}

View File

@ -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) {
}

View File

@ -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());
}
}

View File

@ -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<PeripheralMethod> 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);
}
}

View File

@ -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<IPeripheral> getPeripheral(@NotNull Level world, @NotNull BlockPos pos, @NotNull Direction side) {
return LazyOptional.empty();
}
}

View File

@ -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<BoundMethod> methods;
protected final Set<IComputerAccess> 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);
}
}

View File

@ -0,0 +1,4 @@
package net.banutama.utamacraft.integrations.computercraft.peripheral;
public class BasePeripheralOwner {
}

View File

@ -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.
}

View File

@ -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());
}
}

View File

@ -0,0 +1,4 @@
package net.banutama.utamacraft.integrations.computercraft.peripheral;
public class PocketPeripheralOwner extends BasePeripheralOwner {
}

View File

@ -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;
}
}

View File

@ -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<T extends BasePeripheral> 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);
}
}

View File

@ -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<ITurtleAccess, SimpleFakePlayer> 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> T withPlayer(ITurtleAccess turtle, Function<SimpleFakePlayer, T> 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);
}
}

View File

@ -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<PlayerPeripheral> {
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);
}
}

View File

@ -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));
}
}

View File

@ -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());
}
};

View File

@ -11,8 +11,11 @@ public class ModItems {
public static final DeferredRegister<Item> ITEMS =
DeferredRegister.create(ForgeRegistries.ITEMS, Utamacraft.MOD_ID);
public static final RegistryObject<Item> LOGO = ITEMS.register("utamacraft_logo",
() -> new Item(new Item.Properties()));
public static final RegistryObject<Item> LOGO =
ITEMS.register("utamacraft_logo", () -> new Item(new Item.Properties()));
public static final RegistryObject<Item> PLAYER_PERIPHERAL =
ITEMS.register("player_peripheral", PlayerPeripheralItem::new);
public static void register(IEventBus eventBus) {
ITEMS.register(eventBus);

View File

@ -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<ItemStack> 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);
}
}
}

View File

@ -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);
}
}

View File

@ -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<SimpleFakePlayer> INSTANCE;
public SimpleFakePlayer(ServerLevel world, GameProfile profile) {
super(world, profile);
}
public SimpleFakePlayer(ServerLevel world) {
this(world, new FakeGameProfile());
}
public static <R> R withFakePlayer(ServerLevel world, Function<SimpleFakePlayer, R> 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<Entity> 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<Entity> 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<Vec3> 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<Entity> 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;
}
}

View File

@ -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"
}

View File

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "utamacraft:item/player_peripheral"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,4 @@
{
"type": "utamacraft:player_turtle",
"item": "utamacraft:player_peripheral"
}

View File

@ -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
}
}

Binary file not shown.