Add hook for custom damage logic ()

* Add hook for custom damage logic

* Address comments

* Change to use new FabricItemSettings

* Make CustomDamageHandler a functional interface
This commit is contained in:
Vincent Lee 2020-09-27 06:09:07 -05:00 committed by GitHub
parent c1aa8ed8d7
commit 616c01224d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 214 additions and 1 deletions
fabric-item-api-v1/src
main
testmod
java/net/fabricmc/fabric/test/item
resources
assets/fabric-item-api-v1-testmod/models/item
fabric.mod.json

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.item.v1;
import java.util.function.Consumer;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
/**
* Allows an item to run custom logic when {@link ItemStack#damage(int, LivingEntity, Consumer)} is called.
* This is useful for items that, for example, may drain durability from some other source before damaging
* the stack itself.
*
* <p>Custom damage handlers can be set with {@link FabricItemSettings#customDamage}.
*/
@FunctionalInterface
public interface CustomDamageHandler {
/**
* Called to apply damage to the given stack.
* This can be used to e.g. drain from a battery before actually damaging the item.
* @param amount The amount of damage originally requested
* @param breakCallback Callback when the stack reaches zero damage. See {@link ItemStack#damage(int, LivingEntity, Consumer)} and its callsites for more information.
* @return The amount of damage to pass to vanilla's logic
*/
int damage(ItemStack stack, int amount, LivingEntity entity, Consumer<LivingEntity> breakCallback);
}

View file

@ -19,6 +19,7 @@ package net.fabricmc.fabric.api.item.v1;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Rarity;
import net.fabricmc.fabric.impl.item.FabricItemInternals;
@ -42,6 +43,16 @@ public class FabricItemSettings extends Item.Settings {
return this;
}
/**
* Sets the custom damage handler of the item.
* Note that this is only called on an ItemStack if {@link ItemStack#isDamageable()} returns true.
* @see CustomDamageHandler
*/
public FabricItemSettings customDamage(CustomDamageHandler handler) {
FabricItemInternals.computeExtraData(this).customDamage(handler);
return this;
}
// Overrides of vanilla methods
@Override

View file

@ -20,6 +20,7 @@ import java.util.WeakHashMap;
import net.minecraft.item.Item;
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
public final class FabricItemInternals {
@ -37,14 +38,20 @@ public final class FabricItemInternals {
if (data != null) {
((ItemExtensions) item).fabric_setEquipmentSlotProvider(data.equipmentSlotProvider);
((ItemExtensions) item).fabric_setCustomDamageHandler(data.customDamageHandler);
}
}
public static final class ExtraData {
private /* @Nullable */ EquipmentSlotProvider equipmentSlotProvider;
private /* @Nullable */ CustomDamageHandler customDamageHandler;
public void equipmentSlot(EquipmentSlotProvider equipmentSlotProvider) {
this.equipmentSlotProvider = equipmentSlotProvider;
}
public void customDamage(CustomDamageHandler handler) {
this.customDamageHandler = handler;
}
}
}

View file

@ -16,9 +16,12 @@
package net.fabricmc.fabric.impl.item;
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
public interface ItemExtensions {
EquipmentSlotProvider fabric_getEquipmentSlotProvider();
/* @Nullable */ EquipmentSlotProvider fabric_getEquipmentSlotProvider();
void fabric_setEquipmentSlotProvider(EquipmentSlotProvider equipmentSlotProvider);
/* @Nullable */ CustomDamageHandler fabric_getCustomDamageHandler();
void fabric_setCustomDamageHandler(CustomDamageHandler handler);
}

View file

@ -24,6 +24,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.item.Item;
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
import net.fabricmc.fabric.impl.item.FabricItemInternals;
import net.fabricmc.fabric.impl.item.ItemExtensions;
@ -33,6 +34,9 @@ abstract class ItemMixin implements ItemExtensions {
@Unique
private EquipmentSlotProvider equipmentSlotProvider;
@Unique
private CustomDamageHandler customDamageHandler;
@Inject(method = "<init>", at = @At("RETURN"))
private void onConstruct(Item.Settings settings, CallbackInfo info) {
FabricItemInternals.onBuild(settings, (Item) (Object) this);
@ -47,4 +51,14 @@ abstract class ItemMixin implements ItemExtensions {
public void fabric_setEquipmentSlotProvider(EquipmentSlotProvider equipmentSlotProvider) {
this.equipmentSlotProvider = equipmentSlotProvider;
}
@Override
public CustomDamageHandler fabric_getCustomDamageHandler() {
return customDamageHandler;
}
@Override
public void fabric_setCustomDamageHandler(CustomDamageHandler handler) {
this.customDamageHandler = handler;
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.item;
import java.util.function.Consumer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.impl.item.ItemExtensions;
@Mixin(ItemStack.class)
public abstract class ItemStackMixin {
@Shadow public abstract Item getItem();
@Unique
private LivingEntity fabric_damagingEntity;
@Unique
private Consumer<LivingEntity> fabric_breakCallback;
@Inject(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At("HEAD"))
private void saveDamager(int amount, LivingEntity entity, Consumer<LivingEntity> breakCallback, CallbackInfo ci) {
this.fabric_damagingEntity = entity;
this.fabric_breakCallback = breakCallback;
}
@ModifyArg(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;damage(ILjava/util/Random;Lnet/minecraft/server/network/ServerPlayerEntity;)Z"), index = 0)
private int hookDamage(int amount) {
CustomDamageHandler handler = ((ItemExtensions) getItem()).fabric_getCustomDamageHandler();
if (handler != null) {
return handler.damage((ItemStack) (Object) this, amount, fabric_damagingEntity, fabric_breakCallback);
}
return amount;
}
@Inject(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At("RETURN"))
private <T extends LivingEntity> void clearDamager(int amount, T entity, Consumer<T> breakCallback, CallbackInfo ci) {
this.fabric_damagingEntity = null;
this.fabric_breakCallback = null;
}
}

View file

@ -3,6 +3,7 @@
"package": "net.fabricmc.fabric.mixin.item",
"compatibilityLevel": "JAVA_8",
"mixins": [
"ItemStackMixin",
"ItemMixin",
"MobEntityMixin"
],

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.PickaxeItem;
import net.minecraft.item.ToolMaterials;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
public class CustomDamageTest implements ModInitializer {
@Override
public void onInitialize() {
Registry.register(Registry.ITEM, new Identifier("fabric-item-api-v1-testmod", "weird_pickaxe"), new WeirdPick());
}
public static final CustomDamageHandler WEIRD_DAMAGE_HANDLER = (stack, amount, entity, breakCallback) -> {
// If sneaking, apply all damage to vanilla. Otherwise, increment a tag on the stack by one and don't apply any damage
if (entity.isSneaking()) {
return amount;
} else {
CompoundTag tag = stack.getOrCreateTag();
tag.putInt("weird", tag.getInt("weird") + 1);
return 0;
}
};
public static class WeirdPick extends PickaxeItem {
protected WeirdPick() {
super(ToolMaterials.GOLD, 1, -2.8F, new FabricItemSettings().customDamage(WEIRD_DAMAGE_HANDLER));
}
@Override
public Text getName(ItemStack stack) {
int v = stack.getOrCreateTag().getInt("weird");
return super.getName(stack).shallowCopy().append(" (Weird Value: " + v + ")");
}
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "minecraft:item/golden_pickaxe"
}
}

View file

@ -14,6 +14,9 @@
],
"client": [
"net.fabricmc.fabric.test.item.client.TooltipTests"
],
"main": [
"net.fabricmc.fabric.test.item.CustomDamageTest"
]
}
}