mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-03-29 14:56:58 +00:00
Make turtle tools a little more flexible
Turtle tools now accept two additional JSON fields - allowEnchantments: Whether items with enchantments (or any non-standard NBT) can be equipped. - consumesDurability: Whether durability will be consumed. This can be "never" (the current and default behaviour), "always", and "when_enchanted". Closes #1501.
This commit is contained in:
parent
943a9406b1
commit
a91ac6f214
@ -0,0 +1,48 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Indicates if an equipped turtle item will consume durability.
|
||||
*
|
||||
* @see TurtleUpgradeDataProvider.ToolBuilder#consumesDurability(TurtleToolDurability)
|
||||
*/
|
||||
public enum TurtleToolDurability implements StringRepresentable {
|
||||
/**
|
||||
* The equipped tool always consumes durability when using.
|
||||
*/
|
||||
ALWAYS("always"),
|
||||
|
||||
/**
|
||||
* The equipped tool consumes durability if it is {@linkplain ItemStack#isEnchanted() enchanted} or has
|
||||
* {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
|
||||
*/
|
||||
WHEN_ENCHANTED("when_enchanted"),
|
||||
|
||||
/**
|
||||
* The equipped tool never consumes durability. Tools which have been damaged cannot be used as upgrades.
|
||||
*/
|
||||
NEVER("never");
|
||||
|
||||
private final String serialisedName;
|
||||
|
||||
/**
|
||||
* The codec which may be used for serialising/deserialising {@link TurtleToolDurability}s.
|
||||
*/
|
||||
public static final StringRepresentable.EnumCodec<TurtleToolDurability> CODEC = StringRepresentable.fromEnum(TurtleToolDurability::values);
|
||||
|
||||
TurtleToolDurability(String serialisedName) {
|
||||
this.serialisedName = serialisedName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerializedName() {
|
||||
return serialisedName;
|
||||
}
|
||||
}
|
@ -13,8 +13,10 @@ import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -61,6 +63,8 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
private @Nullable Item craftingItem;
|
||||
private @Nullable Float damageMultiplier = null;
|
||||
private @Nullable TagKey<Block> breakable;
|
||||
private boolean allowEnchantments = false;
|
||||
private TurtleToolDurability consumesDurability = TurtleToolDurability.NEVER;
|
||||
|
||||
ToolBuilder(ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem) {
|
||||
this.id = id;
|
||||
@ -104,6 +108,28 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
|
||||
* {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
|
||||
*
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder allowEnchantments() {
|
||||
allowEnchantments = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when the tool will consume durability.
|
||||
*
|
||||
* @param durability The durability predicate.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder consumesDurability(TurtleToolDurability durability) {
|
||||
consumesDurability = durability;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
|
||||
* in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
|
||||
@ -132,6 +158,10 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
}
|
||||
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
|
||||
if (breakable != null) s.addProperty("breakable", breakable.location().toString());
|
||||
if (allowEnchantments) s.addProperty("allowEnchantments", true);
|
||||
if (consumesDurability != TurtleToolDurability.NEVER) {
|
||||
s.addProperty("consumesDurability", consumesDurability.getSerializedName());
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import dan200.computercraft.api.turtle.TurtleCommand;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
@ -35,7 +34,6 @@ import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.MoverType;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
@ -465,23 +463,6 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable DyeColor getDyeColour() {
|
||||
if (colourHex == -1) return null;
|
||||
var colour = Colour.fromHex(colourHex);
|
||||
return colour == null ? null : DyeColor.byId(15 - colour.ordinal());
|
||||
}
|
||||
|
||||
public void setDyeColour(@Nullable DyeColor dyeColour) {
|
||||
var newColour = -1;
|
||||
if (dyeColour != null) {
|
||||
newColour = Colour.values()[15 - dyeColour.getId()].getHex();
|
||||
}
|
||||
if (colourHex != newColour) {
|
||||
colourHex = newColour;
|
||||
BlockEntityHelpers.updateBlock(owner);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColour(int colour) {
|
||||
if (colour >= 0 && colour <= 0xFFFFFF) {
|
||||
|
@ -74,19 +74,7 @@ public class TurtlePlaceCommand implements TurtleCommand {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean deployCopiedItem(
|
||||
ItemStack stack, ITurtleAccess turtle, Direction direction, @Nullable Object[] extraArguments, @Nullable ErrorMessage outErrorMessage
|
||||
) {
|
||||
// Create a fake player, and orient it appropriately
|
||||
var playerPosition = turtle.getPosition().relative(direction);
|
||||
var turtlePlayer = TurtlePlayer.getWithPosition(turtle, playerPosition, direction);
|
||||
turtlePlayer.loadInventory(stack);
|
||||
var result = deploy(stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage);
|
||||
turtlePlayer.player().getInventory().clearContent();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean deploy(
|
||||
public static boolean deploy(
|
||||
ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction,
|
||||
@Nullable Object[] extraArguments, @Nullable ErrorMessage outErrorMessage
|
||||
) {
|
||||
|
@ -59,7 +59,7 @@ class UpgradeContainer implements Container {
|
||||
|
||||
private ItemStack setUpgradeStack(int slot, @Nullable UpgradeData<ITurtleUpgrade> upgrade) {
|
||||
var stack = upgrade == null ? ItemStack.EMPTY : upgrade.getUpgradeItem();
|
||||
lastUpgrade.set(slot, upgrade);
|
||||
lastUpgrade.set(slot, UpgradeData.copyOf(upgrade));
|
||||
lastStack.set(slot, stack);
|
||||
return stack;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package dan200.computercraft.shared.turtle.upgrades;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.api.turtle.*;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.turtle.TurtleUtil;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
|
||||
@ -17,14 +18,19 @@ import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.MobType;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
@ -33,6 +39,8 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static net.minecraft.nbt.Tag.TAG_COMPOUND;
|
||||
import static net.minecraft.nbt.Tag.TAG_LIST;
|
||||
@ -43,31 +51,39 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
|
||||
final ItemStack item;
|
||||
final float damageMulitiplier;
|
||||
@Nullable
|
||||
final TagKey<Block> breakable;
|
||||
final boolean allowsEnchantments;
|
||||
final TurtleToolDurability consumesDurability;
|
||||
final @Nullable TagKey<Block> breakable;
|
||||
|
||||
public TurtleTool(ResourceLocation id, String adjective, Item craftItem, ItemStack toolItem, float damageMulitiplier, @Nullable TagKey<Block> breakable) {
|
||||
public TurtleTool(
|
||||
ResourceLocation id, String adjective, Item craftItem, ItemStack toolItem, float damageMulitiplier,
|
||||
boolean allowsEnchantments, TurtleToolDurability consumesDurability, @Nullable TagKey<Block> breakable
|
||||
) {
|
||||
super(id, TurtleUpgradeType.TOOL, adjective, new ItemStack(craftItem));
|
||||
item = toolItem;
|
||||
this.damageMulitiplier = damageMulitiplier;
|
||||
this.allowsEnchantments = allowsEnchantments;
|
||||
this.consumesDurability = consumesDurability;
|
||||
this.breakable = breakable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemSuitable(ItemStack stack) {
|
||||
var tag = stack.getTag();
|
||||
if (tag == null || tag.isEmpty()) return true;
|
||||
|
||||
// Check we've not got anything vaguely interesting on the item. We allow other mods to add their
|
||||
// own NBT, with the understanding such details will be lost to the mist of time.
|
||||
if (stack.isDamaged() || stack.isEnchanted()) return false;
|
||||
if (tag.contains("AttributeModifiers", TAG_LIST) && !tag.getList("AttributeModifiers", TAG_COMPOUND).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (consumesDurability == TurtleToolDurability.NEVER && stack.isDamaged()) return false;
|
||||
if (!allowsEnchantments && isEnchanted(stack)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isEnchanted(ItemStack stack) {
|
||||
return !stack.isEmpty() && isEnchanted(stack.getTag());
|
||||
}
|
||||
|
||||
private static boolean isEnchanted(@Nullable CompoundTag tag) {
|
||||
if (tag == null || tag.isEmpty()) return false;
|
||||
return (tag.contains(ItemStack.TAG_ENCH, TAG_LIST) && !tag.getList(ItemStack.TAG_ENCH, TAG_COMPOUND).isEmpty())
|
||||
|| (tag.contains("AttributeModifiers", TAG_LIST) && !tag.getList("AttributeModifiers", TAG_COMPOUND).isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getUpgradeData(ItemStack stack) {
|
||||
// Just use the current item's tag.
|
||||
@ -83,11 +99,64 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
return item;
|
||||
}
|
||||
|
||||
private ItemStack getToolStack(ITurtleAccess turtle, TurtleSide side) {
|
||||
var item = getCraftingItem();
|
||||
var tag = turtle.getUpgradeNBTData(side);
|
||||
if (!tag.isEmpty()) item.setTag(tag);
|
||||
return item.copy();
|
||||
}
|
||||
|
||||
private void setToolStack(ITurtleAccess turtle, TurtleSide side, ItemStack stack) {
|
||||
var tag = turtle.getUpgradeNBTData(side);
|
||||
|
||||
var useDurability = switch (consumesDurability) {
|
||||
case NEVER -> false;
|
||||
case WHEN_ENCHANTED -> isEnchanted(tag);
|
||||
case ALWAYS -> true;
|
||||
};
|
||||
if (!useDurability) return;
|
||||
|
||||
// If the tool has broken, remove the upgrade!
|
||||
if (stack.isEmpty()) {
|
||||
turtle.setUpgradeWithData(side, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the tool has changed, no clue what's going on.
|
||||
if (stack.getItem() != item.getItem()) return;
|
||||
|
||||
var itemTag = stack.getTag();
|
||||
|
||||
// Early return if the item hasn't changed to avoid redundant syncs with the client.
|
||||
if ((itemTag == null && tag.isEmpty()) || Objects.equals(itemTag, tag)) return;
|
||||
|
||||
if (itemTag == null) {
|
||||
tag.getAllKeys().clear();
|
||||
} else {
|
||||
for (var key : itemTag.getAllKeys()) tag.put(key, Nullability.assertNonNull(itemTag.get(key)));
|
||||
tag.getAllKeys().removeIf(x -> !itemTag.contains(x));
|
||||
}
|
||||
|
||||
turtle.updateUpgradeNBTData(side);
|
||||
}
|
||||
|
||||
private <T> T withEquippedItem(ITurtleAccess turtle, TurtleSide side, Direction direction, Function<TurtlePlayer, T> action) {
|
||||
var turtlePlayer = TurtlePlayer.getWithPosition(turtle, turtle.getPosition(), direction);
|
||||
turtlePlayer.loadInventory(getToolStack(turtle, side));
|
||||
|
||||
var result = action.apply(turtlePlayer);
|
||||
|
||||
setToolStack(turtle, side, turtlePlayer.player().getItemInHand(InteractionHand.MAIN_HAND));
|
||||
turtlePlayer.player().getInventory().clearContent();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TurtleCommandResult useTool(ITurtleAccess turtle, TurtleSide side, TurtleVerb verb, Direction direction) {
|
||||
return switch (verb) {
|
||||
case ATTACK -> attack(turtle, direction);
|
||||
case DIG -> dig(turtle, direction);
|
||||
case ATTACK -> attack(turtle, side, direction);
|
||||
case DIG -> dig(turtle, side, direction);
|
||||
};
|
||||
}
|
||||
|
||||
@ -102,16 +171,14 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
}
|
||||
|
||||
/**
|
||||
* Attack an entity. This is a <em>very</em> cut down version of {@link Player#attack(Entity)}, which doesn't handle
|
||||
* enchantments, knockback, etc... Unfortunately we can't call attack directly as damage calculations are rather
|
||||
* different (and we don't want to play sounds/particles).
|
||||
* Attack an entity.
|
||||
*
|
||||
* @param turtle The current turtle.
|
||||
* @param side The side the tool is on.
|
||||
* @param direction The direction we're attacking in.
|
||||
* @return Whether an attack occurred.
|
||||
* @see Player#attack(Entity)
|
||||
*/
|
||||
private TurtleCommandResult attack(ITurtleAccess turtle, Direction direction) {
|
||||
private TurtleCommandResult attack(ITurtleAccess turtle, TurtleSide side, Direction direction) {
|
||||
// Create a fake player, and orient it appropriately
|
||||
var world = turtle.getLevel();
|
||||
var position = turtle.getPosition();
|
||||
@ -123,10 +190,11 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
var turtlePos = player.position();
|
||||
var rayDir = player.getViewVector(1.0f);
|
||||
var hit = WorldUtil.clip(world, turtlePos, rayDir, 1.5, null);
|
||||
var attacked = false;
|
||||
if (hit instanceof EntityHitResult entityHit) {
|
||||
// Load up the turtle's inventory
|
||||
var stackCopy = item.copy();
|
||||
turtlePlayer.loadInventory(stackCopy);
|
||||
var stack = getToolStack(turtle, side);
|
||||
turtlePlayer.loadInventory(stack);
|
||||
|
||||
var hitEntity = entityHit.getEntity();
|
||||
|
||||
@ -134,62 +202,120 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
DropConsumer.set(hitEntity, TurtleUtil.dropConsumer(turtle));
|
||||
|
||||
// Attack the entity
|
||||
var attacked = false;
|
||||
var result = PlatformHelper.get().canAttackEntity(player, hitEntity);
|
||||
if (result.consumesAction()) {
|
||||
attacked = true;
|
||||
} else if (result == InteractionResult.PASS && hitEntity.isAttackable() && !hitEntity.skipAttackInteraction(player)) {
|
||||
var damage = (float) player.getAttributeValue(Attributes.ATTACK_DAMAGE) * damageMulitiplier;
|
||||
if (damage > 0.0f) {
|
||||
var source = player.damageSources().playerAttack(player);
|
||||
if (hitEntity instanceof ArmorStand) {
|
||||
// Special case for armor stands: attack twice to guarantee destroy
|
||||
hitEntity.hurt(source, damage);
|
||||
if (hitEntity.isAlive()) hitEntity.hurt(source, damage);
|
||||
attacked = true;
|
||||
} else {
|
||||
if (hitEntity.hurt(source, damage)) attacked = true;
|
||||
}
|
||||
}
|
||||
attacked = attack(player, direction, hitEntity);
|
||||
}
|
||||
|
||||
// Stop claiming drops
|
||||
TurtleUtil.stopConsuming(turtle);
|
||||
|
||||
// Put everything we collected into the turtles inventory, then return
|
||||
// Put everything we collected into the turtles inventory.
|
||||
setToolStack(turtle, side, player.getItemInHand(InteractionHand.MAIN_HAND));
|
||||
player.getInventory().clearContent();
|
||||
if (attacked) return TurtleCommandResult.success();
|
||||
}
|
||||
|
||||
return TurtleCommandResult.failure("Nothing to attack here");
|
||||
return attacked ? TurtleCommandResult.success() : TurtleCommandResult.failure("Nothing to attack here");
|
||||
}
|
||||
|
||||
private TurtleCommandResult dig(ITurtleAccess turtle, Direction direction) {
|
||||
if (PlatformHelper.get().hasToolUsage(item) && TurtlePlaceCommand.deployCopiedItem(item.copy(), turtle, direction, null, null)) {
|
||||
return TurtleCommandResult.success();
|
||||
/**
|
||||
* Attack an entity. This is a copy of {@link Player#attack(Entity)}, with some unwanted features removed (sweeping
|
||||
* edge). This is a little limited.
|
||||
* <p>
|
||||
* Ideally we'd use attack directly (if other mods mixin to that method, we won't support their features).
|
||||
* Unfortunately,that doesn't give us any feedback to whether the attack occurred or not (and we don't want to play
|
||||
* sounds/particles).
|
||||
*
|
||||
* @param player The fake player doing the attacking.
|
||||
* @param direction The direction the turtle is attacking.
|
||||
* @param entity The entity to attack.
|
||||
* @return Whether we attacked or not.
|
||||
* @see Player#attack(Entity)
|
||||
*/
|
||||
private boolean attack(ServerPlayer player, Direction direction, Entity entity) {
|
||||
var baseDamage = (float) player.getAttributeValue(Attributes.ATTACK_DAMAGE) * damageMulitiplier;
|
||||
var bonusDamage = EnchantmentHelper.getDamageBonus(
|
||||
player.getItemInHand(InteractionHand.MAIN_HAND), entity instanceof LivingEntity target ? target.getMobType() : MobType.UNDEFINED
|
||||
);
|
||||
var damage = baseDamage + bonusDamage;
|
||||
if (damage <= 0) return false;
|
||||
|
||||
var knockBack = EnchantmentHelper.getKnockbackBonus(player);
|
||||
|
||||
// We follow the logic in Player.attack of setting the entity on fire before attacking, so it's burning when it
|
||||
// (possibly) dies.
|
||||
var fireAspect = EnchantmentHelper.getFireAspect(player);
|
||||
var onFire = false;
|
||||
if (entity instanceof LivingEntity target && fireAspect > 0 && !target.isOnFire()) {
|
||||
onFire = true;
|
||||
target.setSecondsOnFire(1);
|
||||
}
|
||||
|
||||
var level = (ServerLevel) turtle.getLevel();
|
||||
var turtlePosition = turtle.getPosition();
|
||||
var source = player.damageSources().playerAttack(player);
|
||||
if (!entity.hurt(source, damage)) {
|
||||
// If we failed to damage the entity, undo us setting the entity on fire.
|
||||
if (onFire) entity.clearFire();
|
||||
return false;
|
||||
}
|
||||
|
||||
var blockPosition = turtlePosition.relative(direction);
|
||||
// Special case for armor stands: attack twice to guarantee destroy
|
||||
if (entity.isAlive() && entity instanceof ArmorStand) entity.hurt(source, damage);
|
||||
|
||||
// Apply knockback
|
||||
if (knockBack > 0) {
|
||||
if (entity instanceof LivingEntity target) {
|
||||
target.knockback(knockBack * 0.5, -direction.getStepX(), -direction.getStepZ());
|
||||
} else {
|
||||
entity.push(direction.getStepX() * knockBack * 0.5, 0.1, direction.getStepZ() * knockBack * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply remaining enchantments
|
||||
if (entity instanceof LivingEntity target) EnchantmentHelper.doPostHurtEffects(target, player);
|
||||
EnchantmentHelper.doPostDamageEffects(player, entity);
|
||||
|
||||
// Damage the original item stack.
|
||||
if (entity instanceof LivingEntity target) {
|
||||
player.getItemInHand(InteractionHand.MAIN_HAND).hurtEnemy(target, player);
|
||||
}
|
||||
|
||||
// Apply fire aspect
|
||||
if (entity instanceof LivingEntity target && fireAspect > 0 && !target.isOnFire()) {
|
||||
target.setSecondsOnFire(4 * fireAspect);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private TurtleCommandResult dig(ITurtleAccess turtle, TurtleSide side, Direction direction) {
|
||||
var level = (ServerLevel) turtle.getLevel();
|
||||
|
||||
var blockPosition = turtle.getPosition().relative(direction);
|
||||
if (level.isEmptyBlock(blockPosition) || WorldUtil.isLiquidBlock(level, blockPosition)) {
|
||||
return TurtleCommandResult.failure("Nothing to dig here");
|
||||
}
|
||||
|
||||
var turtlePlayer = TurtlePlayer.getWithPosition(turtle, turtlePosition, direction);
|
||||
turtlePlayer.loadInventory(item.copy());
|
||||
return withEquippedItem(turtle, side, direction, turtlePlayer -> {
|
||||
var stack = turtlePlayer.player().getItemInHand(InteractionHand.MAIN_HAND);
|
||||
|
||||
// Check if we can break the block
|
||||
var breakable = checkBlockBreakable(level, blockPosition, turtlePlayer);
|
||||
if (!breakable.isSuccess()) return breakable;
|
||||
// Right-click the block when using a shovel/hoe.
|
||||
if (PlatformHelper.get().hasToolUsage(item) && TurtlePlaceCommand.deploy(stack, turtle, turtlePlayer, direction, null, null)) {
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
|
||||
DropConsumer.set(level, blockPosition, TurtleUtil.dropConsumer(turtle));
|
||||
var broken = !turtlePlayer.isBlockProtected(level, blockPosition) && turtlePlayer.player().gameMode.destroyBlock(blockPosition);
|
||||
TurtleUtil.stopConsuming(turtle);
|
||||
// Check if we can break the block
|
||||
var breakable = checkBlockBreakable(level, blockPosition, turtlePlayer);
|
||||
if (!breakable.isSuccess()) return breakable;
|
||||
|
||||
// Check spawn protection
|
||||
return broken ? TurtleCommandResult.success() : TurtleCommandResult.failure("Cannot break protected block");
|
||||
// And break it!
|
||||
DropConsumer.set(level, blockPosition, TurtleUtil.dropConsumer(turtle));
|
||||
var broken = !turtlePlayer.isBlockProtected(level, blockPosition) && turtlePlayer.player().gameMode.destroyBlock(blockPosition);
|
||||
TurtleUtil.stopConsuming(turtle);
|
||||
|
||||
return broken ? TurtleCommandResult.success() : TurtleCommandResult.failure("Cannot break protected block");
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isTriviallyBreakable(BlockGetter reader, BlockPos pos, BlockState state) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.shared.turtle.upgrades;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.api.turtle.TurtleToolDurability;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
@ -28,6 +29,8 @@ public final class TurtleToolSerialiser implements TurtleUpgradeSerialiser<Turtl
|
||||
var toolItem = GsonHelper.getAsItem(object, "item");
|
||||
var craftingItem = GsonHelper.getAsItem(object, "craftingItem", toolItem);
|
||||
var damageMultiplier = GsonHelper.getAsFloat(object, "damageMultiplier", 3.0f);
|
||||
var allowsEnchantments = GsonHelper.getAsBoolean(object, "allowsEnchantments", false);
|
||||
var consumesDurability = TurtleToolDurability.CODEC.byName(GsonHelper.getAsString(object, "consumesDurability", null), TurtleToolDurability.NEVER);
|
||||
|
||||
TagKey<Block> breakable = null;
|
||||
if (object.has("breakable")) {
|
||||
@ -35,7 +38,7 @@ public final class TurtleToolSerialiser implements TurtleUpgradeSerialiser<Turtl
|
||||
breakable = TagKey.create(Registries.BLOCK, tag);
|
||||
}
|
||||
|
||||
return new TurtleTool(id, adjective, craftingItem, new ItemStack(toolItem), damageMultiplier, breakable);
|
||||
return new TurtleTool(id, adjective, craftingItem, new ItemStack(toolItem), damageMultiplier, allowsEnchantments, consumesDurability, breakable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,9 +49,11 @@ public final class TurtleToolSerialiser implements TurtleUpgradeSerialiser<Turtl
|
||||
// damageMultiplier and breakable aren't used by the client, but we need to construct the upgrade exactly
|
||||
// as otherwise syncing on an SP world will overwrite the (shared) upgrade registry with an invalid upgrade!
|
||||
var damageMultiplier = buffer.readFloat();
|
||||
var allowsEnchantments = buffer.readBoolean();
|
||||
var consumesDurability = buffer.readEnum(TurtleToolDurability.class);
|
||||
|
||||
var breakable = buffer.readBoolean() ? TagKey.create(Registries.BLOCK, buffer.readResourceLocation()) : null;
|
||||
return new TurtleTool(id, adjective, craftingItem, toolItem, damageMultiplier, breakable);
|
||||
return new TurtleTool(id, adjective, craftingItem, toolItem, damageMultiplier, allowsEnchantments, consumesDurability, breakable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -57,6 +62,8 @@ public final class TurtleToolSerialiser implements TurtleUpgradeSerialiser<Turtl
|
||||
RegistryWrappers.writeId(buffer, RegistryWrappers.ITEMS, upgrade.getCraftingItem().getItem());
|
||||
buffer.writeItem(upgrade.item);
|
||||
buffer.writeFloat(upgrade.damageMulitiplier);
|
||||
buffer.writeBoolean(upgrade.allowsEnchantments);
|
||||
buffer.writeEnum(upgrade.consumesDurability);
|
||||
buffer.writeBoolean(upgrade.breakable != null);
|
||||
if (upgrade.breakable != null) buffer.writeResourceLocation(upgrade.breakable.location());
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package dan200.computercraft.shared.platform;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
@ -39,4 +40,9 @@ public final class FakePlayer extends net.fabricmc.fabric.api.entity.FakePlayer
|
||||
public double getBlockReach() {
|
||||
return MAX_REACH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean broadcastToPlayer(ServerPlayer player) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package dan200.computercraft.shared.platform;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
@ -57,4 +58,9 @@ class FakePlayerExt extends FakePlayer {
|
||||
public double getEntityReach() {
|
||||
return MAX_REACH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean broadcastToPlayer(ServerPlayer player) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user