Skip to content

Commit

Permalink
Migrate inventory tick, done migrating hooks for now
Browse files Browse the repository at this point in the history
Also make documentaton changes
Only one not modified right now is processLoot, which I am considering just replacing with direct usages of global loot modifiers as that gives more flexability.
  • Loading branch information
KnightMiner committed Dec 28, 2023
1 parent 40be4b9 commit 5a99461
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ public void onRemoved(IToolStackView tool) {}
/* Hooks */

/** @deprecated use {@link ToolDamageModifierHook} */
@Deprecated
public int onDamageTool(IToolStackView tool, int level, int amount, @Nullable LivingEntity holder) {
return amount;
}
Expand All @@ -432,17 +433,8 @@ public float getRepairFactor(IToolStackView toolStack, int level, float factor)
return factor;
}

/**
* Called when the stack updates in the player inventory
* @param tool Current tool instance
* @param level Modifier level
* @param world World containing tool
* @param holder Entity holding tool
* @param itemSlot Slot containing this tool
* @param isSelected If true, this item is currently in the player's main hand
* @param isCorrectSlot If true, this item is in the proper slot. For tools, that is main hand or off hand. For armor, this means its in the correct armor slot
* @param stack Item stack instance to check other slots for the tool. Do not modify
*/
/** @deprecated use {@link slimeknights.tconstruct.library.modifiers.hook.interaction.InventoryTickModifierHook} */
@Deprecated
public void onInventoryTick(IToolStackView tool, int level, Level world, LivingEntity holder, int itemSlot, boolean isSelected, boolean isCorrectSlot, ItemStack stack) {}

/**
Expand All @@ -452,6 +444,7 @@ public void onInventoryTick(IToolStackView tool, int level, Level world, LivingE
* @param generatedLoot Current loot list before this modifier
* @param context Full loot context
* @return Loot replacement
* TODO: can we ditch this hook in favor of just using GLMs? Just need a loot condition to detect a modifier, and it gives us a lot more flexability
*/
public List<ItemStack> processLoot(IToolStackView tool, int level, List<ItemStack> generatedLoot, LootContext context) {
return generatedLoot;
Expand Down Expand Up @@ -606,7 +599,7 @@ public void attackWithArmor(IToolStackView tool, int level, EquipmentContext con
@Deprecated
public void onUnequip(IToolStackView tool, int level, EquipmentChangeContext context) {}

/** @deprecated use {@link EquipmentChangeModifierHook#onEquip(IToolStackView, ModifierEntry, EquipmentChangeContext)} (IToolStackView, ModifierEntry, EquipmentChangeContext)} */
/** @deprecated use {@link EquipmentChangeModifierHook#onEquip(IToolStackView, ModifierEntry, EquipmentChangeContext)} */
@Deprecated
public void onEquip(IToolStackView tool, int level, EquipmentChangeContext context) {}

Expand Down Expand Up @@ -639,7 +632,7 @@ public Boolean showDurabilityBar(IToolStackView tool, int level) {
return null;
}

/** @deprecated use {@link slimeknights.tconstruct.library.modifiers.hook.display.DurabilityDisplayModifierHook#getDurabilityRGB(IToolStackView, ModifierEntry)} (IToolStackView, ModifierEntry)} */
/** @deprecated use {@link slimeknights.tconstruct.library.modifiers.hook.display.DurabilityDisplayModifierHook#getDurabilityRGB(IToolStackView, ModifierEntry)} */
@Deprecated
public int getDurabilityRGB(IToolStackView tool, int level) {
return -1;
Expand Down Expand Up @@ -765,9 +758,13 @@ protected void addDamageTooltip(IToolStackView tool, float amount, List<Componen
TooltipModifierHook.addDamageBoost(tool, this, amount, tooltip);
}

/** Tries an expected module against the given module type, returning null if failing. Do not use if you extend another modifier with modules */
/**
* Tries an expected module against the given module type, returning null if failing. Do not use if you extend another modifier with modules
* @deprecated use {@link #registerHooks(Builder)} with the new hook system.
*/
@SuppressWarnings("unchecked")
@Nullable
@Deprecated
protected static <M, E> E tryModuleMatch(Class<E> expected, Class<M> moduleType, M module) {
if (moduleType == expected) {
return (E) module;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import slimeknights.tconstruct.library.modifiers.hook.interaction.BlockInteractionModifierHook;
import slimeknights.tconstruct.library.modifiers.hook.interaction.EntityInteractionModifierHook;
import slimeknights.tconstruct.library.modifiers.hook.interaction.GeneralInteractionModifierHook;
import slimeknights.tconstruct.library.modifiers.hook.interaction.InventoryTickModifierHook;
import slimeknights.tconstruct.library.modifiers.hook.mining.BlockBreakModifierHook;
import slimeknights.tconstruct.library.modifiers.hook.mining.BreakSpeedModifierHook;
import slimeknights.tconstruct.library.modifiers.hook.mining.FinishHarvestModifierHook;
Expand Down Expand Up @@ -105,6 +106,10 @@ public void onEquipmentChange(IToolStackView tool, ModifierEntry modifier, Equip
/** Hook for modifying the damage amount for tools */
public static final ModifierHook<ToolDamageModifierHook> ON_DAMAGE = register("on_damage", ToolDamageModifierHook.class, ToolDamageModifierHook.Merger::new, (tool, modifier, amount, holder) -> modifier.getModifier().onDamageTool(tool, modifier.getLevel(), amount, holder));

/** Hook running while the tool is in the inventory */
public static final ModifierHook<InventoryTickModifierHook> INVENTORY_TICK = register("inventory_tick", InventoryTickModifierHook.class, InventoryTickModifierHook.AllMerger::new,
(tool, modifier, world, holder, itemSlot, isSelected, isCorrectSlot, stack) -> modifier.getModifier().onInventoryTick(tool, modifier.getLevel(), world, holder, itemSlot, isSelected, isCorrectSlot, stack));


/* Composable only */

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
package slimeknights.tconstruct.library.modifiers.hook.behavior;

import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.EquipmentSlot.Type;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.Item;
import slimeknights.tconstruct.common.TinkerTags;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.TinkerHooks;
import slimeknights.tconstruct.library.modifiers.hook.build.ToolStatsModifierHook;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;
import slimeknights.tconstruct.library.tools.nbt.StatsNBT;
import slimeknights.tconstruct.library.tools.stat.ToolStats;

import java.util.Collection;
import java.util.UUID;
import java.util.function.BiConsumer;

/**
* Modifier hook for adding attributes to a tool when in the correct slot.
*/
public interface AttributesModifierHook {
/** UUIDs for armor attributes on held tools */
UUID[] HELD_ARMOR_UUID = new UUID[]{UUID.fromString("00a1a5fe-43b5-4849-8660-de9aa497736a"), UUID.fromString("6776fd7e-4b22-4cdf-a0bc-bb8d2ad1f0bf")};

/**
* Adds attributes from this modifier's effect. Called whenever the item stack refreshes attributes, typically on equipping and unequipping.
* It is important that you return the same list when equipping and unequipping the item.
Expand All @@ -29,6 +42,51 @@ public interface AttributesModifierHook {
*/
void addAttributes(IToolStackView tool, ModifierEntry modifier, EquipmentSlot slot, BiConsumer<Attribute,AttributeModifier> consumer);

/**
* Gets attribute modifiers for a weapon with melee capability
* @param tool Tool instance
* @param slot Held slot
* @return Map of attribute modifiers
*/
static Multimap<Attribute,AttributeModifier> getHeldAttributeModifiers(IToolStackView tool, EquipmentSlot slot) {
ImmutableMultimap.Builder<Attribute, AttributeModifier> builder = ImmutableMultimap.builder();
if (!tool.isBroken()) {
// base stats
StatsNBT statsNBT = tool.getStats();
if (slot == EquipmentSlot.MAINHAND && tool.hasTag(TinkerTags.Items.MELEE)) {
builder.put(Attributes.ATTACK_DAMAGE, new AttributeModifier(Item.BASE_ATTACK_DAMAGE_UUID, "tconstruct.tool.attack_damage", statsNBT.get(ToolStats.ATTACK_DAMAGE), AttributeModifier.Operation.ADDITION));
// base attack speed is 4, but our numbers start from 4
builder.put(Attributes.ATTACK_SPEED, new AttributeModifier(Item.BASE_ATTACK_SPEED_UUID, "tconstruct.tool.attack_speed", statsNBT.get(ToolStats.ATTACK_SPEED) - 4d, AttributeModifier.Operation.ADDITION));
}

if (slot.getType() == Type.HAND) {
// shields and slimestaffs can get armor
if (tool.hasTag(TinkerTags.Items.ARMOR)) {
UUID uuid = HELD_ARMOR_UUID[slot.getIndex()];
double value = statsNBT.get(ToolStats.ARMOR);
if (value != 0) {
builder.put(Attributes.ARMOR, new AttributeModifier(uuid, "tconstruct.held.armor", value, AttributeModifier.Operation.ADDITION));
}
value = statsNBT.get(ToolStats.ARMOR_TOUGHNESS);
if (value != 0) {
builder.put(Attributes.ARMOR_TOUGHNESS, new AttributeModifier(uuid, "tconstruct.held.toughness", value, AttributeModifier.Operation.ADDITION));
}
value = statsNBT.get(ToolStats.KNOCKBACK_RESISTANCE);
if (value != 0) {
builder.put(Attributes.KNOCKBACK_RESISTANCE, new AttributeModifier(uuid, "tconstruct.held.knockback_resistance", value, AttributeModifier.Operation.ADDITION));
}
}

// grab attributes from modifiers, only do for hands (other slots would just be weird)
BiConsumer<Attribute,AttributeModifier> attributeConsumer = builder::put;
for (ModifierEntry entry : tool.getModifierList()) {
entry.getHook(TinkerHooks.ATTRIBUTES).addAttributes(tool, entry, slot, attributeConsumer);
}
}
}
return builder.build();
}

/** Merger that runs all hooks */
record AllMerger(Collection<AttributesModifierHook> modules) implements AttributesModifierHook {
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package slimeknights.tconstruct.library.modifiers.hook.interaction;

import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.TinkerHooks;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;
import slimeknights.tconstruct.library.tools.nbt.ToolStack;

import java.util.Collection;
import java.util.List;

/** Hook that runs while the tool is in the inventory */
public interface InventoryTickModifierHook {
/**
* Called when the stack updates in the player inventory
* @param tool Current tool instance
* @param modifier Modifier running the hook
* @param world World containing tool
* @param holder Entity holding tool
* @param itemSlot Slot containing this tool. Note this may be from the hotbar, main inventory, or armor inventory
* @param isSelected If true, this item is currently in the player's main hand
* @param isCorrectSlot If true, this item is in the proper slot. For tools, that is main hand or off hand. For armor, this means its in the correct armor slot
* @param stack Item stack instance to check other slots for the tool. Do not modify
*/
void onInventoryTick(IToolStackView tool, ModifierEntry modifier, Level world, LivingEntity holder, int itemSlot, boolean isSelected, boolean isCorrectSlot, ItemStack stack);

/**
* Handles ticking a modifiable item that works when held. Armor uses different logic
* @param stack Modifiable stack
* @param worldIn World instance
* @param entityIn Entity holding the tool
* @param itemSlot Slot with the tool
* @param isSelected If true, the tool is selected
*/
static void heldInventoryTick(ItemStack stack, Level worldIn, Entity entityIn, int itemSlot, boolean isSelected) {
// don't care about non-living, they skip most tool context
if (entityIn instanceof LivingEntity) {
ToolStack tool = ToolStack.from(stack);
if (!worldIn.isClientSide) {
tool.ensureHasData();
}
List<ModifierEntry> modifiers = tool.getModifierList();
if (!modifiers.isEmpty()) {
LivingEntity living = (LivingEntity) entityIn;
// we pass in the stack for most custom context, but for the sake of armor it is easier to tell them that this is the correct slot for effects
boolean isHeld = isSelected || living.getOffhandItem() == stack;
for (ModifierEntry entry : modifiers) {
entry.getHook(TinkerHooks.INVENTORY_TICK).onInventoryTick(tool, entry, worldIn, living, itemSlot, isSelected, isHeld, stack);
}
}
}
}

/** Merger that runs all hooks one after another */
record AllMerger(Collection<InventoryTickModifierHook> modules) implements InventoryTickModifierHook {
@Override
public void onInventoryTick(IToolStackView tool, ModifierEntry modifier, Level world, LivingEntity holder, int itemSlot, boolean isSelected, boolean isCorrectSlot, ItemStack stack) {
for (InventoryTickModifierHook module : modules) {
module.onInventoryTick(tool, modifier, world, holder, itemSlot, isSelected, isCorrectSlot, stack);
}
}
}
}

0 comments on commit 5a99461

Please sign in to comment.