Skip to content

Commit

Permalink
Implement tool action transform module
Browse files Browse the repository at this point in the history
Replaces the old block transform modifier, for implementing effects such as tilling and stripping
This commit comes with three small behavioral changes:
* When using pathing, campfire extinguishing and making path blocks no longer connect as part of the same AOE
* axe_scrape and axe_wax_off were merged into stripping, meaning tools given stripping now can scrap copper
* Priorities of stripping, tilling, pathing, and firestarter are all 100 now, meaning they have the same precedence as most other interaction modifiers. Workstation lets you reorder them and they by default are later due to fallback in any case
  • Loading branch information
KnightMiner committed Dec 30, 2023
1 parent 717fc45 commit 7beb503
Show file tree
Hide file tree
Showing 22 changed files with 524 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"redirects": [
{
"id": "tconstruct:stripping"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"redirects": [
{
"id": "tconstruct:stripping"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"type": "tconstruct:composable",
"level_display": "tconstruct:no_levels",
"tooltip_display": "always",
"modules": [
{
"type": "tconstruct:show_offhand"
},
{
"type": "tconstruct:campfire_extinguish"
},
{
"type": "tconstruct:tool_action_transform",
"tool_action": "shovel_flatten",
"sound": "minecraft:item.shovel.flatten",
"require_ground": true
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"type": "tconstruct:composable",
"level_display": "tconstruct:default",
"tooltip_display": "always",
"modules": [
{
"type": "tconstruct:show_offhand"
},
{
"type": "tconstruct:tool_action_transform",
"tool_action": "axe_strip",
"sound": "minecraft:item.axe.strip",
"require_ground": false
},
{
"type": "tconstruct:tool_action_transform",
"tool_action": "axe_scrape",
"sound": "minecraft:item.axe.scrape",
"require_ground": false,
"event_id": 3005
},
{
"type": "tconstruct:tool_action_transform",
"tool_action": "axe_wax_off",
"sound": "minecraft:item.axe.wax_off",
"require_ground": false,
"event_id": 3004
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"type": "tconstruct:composable",
"level_display": "tconstruct:default",
"tooltip_display": "always",
"modules": [
{
"type": "tconstruct:show_offhand"
},
{
"type": "tconstruct:tool_action_transform",
"tool_action": "till",
"sound": "minecraft:item.hoe.till",
"require_ground": false
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,9 @@
"abilities": 1
},
"traits": [
{
"name": "tconstruct:axe_scrape",
"level": 1
},
{
"name": "tconstruct:stripping",
"level": 1
},
{
"name": "tconstruct:axe_wax_off",
"level": 1
}
],
"actions": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,9 @@
"abilities": 1
},
"traits": [
{
"name": "tconstruct:axe_scrape",
"level": 1
},
{
"name": "tconstruct:stripping",
"level": 1
},
{
"name": "tconstruct:axe_wax_off",
"level": 1
}
],
"actions": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,9 +291,9 @@ protected void generate() {
withL.accept(TinkerModifiers.bucketing);
withL.accept(TinkerModifiers.firestarter);
withL.accept(TinkerModifiers.glowing);
withL.accept(TinkerModifiers.pathing);
withL.accept(TinkerModifiers.stripping);
withL.accept(TinkerModifiers.tilling);
with.accept(ModifierIds.pathing);
with.accept(ModifierIds.stripping);
with.accept(ModifierIds.tilling);
// staff
withL.accept(TinkerModifiers.bonking);
withL.accept(TinkerModifiers.flinging);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected void addTags() {
modifierTag(ModifierIds.luck, "ensorcellation:hunter");
modifierTag(TinkerModifiers.multishot.getId(), "cyclic:multishot", "ensorcellation:volley");
modifierTag(ModifierIds.reach, "cyclic:reach", "ensorcellation:reach");
modifierTag(TinkerModifiers.tilling.getId(), "ensorcellation:tilling");
modifierTag(ModifierIds.tilling, "ensorcellation:tilling");
modifierTag(TinkerModifiers.reflecting.getId(), "parry:rebound");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protected void addTags() {
tag(TinkerTags.Modifiers.DUAL_INTERACTION)
.add(TinkerModifiers.bucketing.getId(), TinkerModifiers.spilling.getId(),
TinkerModifiers.glowing.getId(), TinkerModifiers.firestarter.getId(),
TinkerModifiers.stripping.getId(), TinkerModifiers.tilling.getId(), TinkerModifiers.pathing.getId(),
ModifierIds.stripping, ModifierIds.tilling, ModifierIds.pathing,
TinkerModifiers.shears.getId(), TinkerModifiers.harvest.getId())
.addOptional(ModifierIds.pockets);
tag(TinkerTags.Modifiers.SLIME_DEFENSE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package slimeknights.tconstruct.library.modifiers.modules.behavior;

import lombok.RequiredArgsConstructor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.ModifierHook;
import slimeknights.tconstruct.library.modifiers.TinkerHooks;
import slimeknights.tconstruct.library.modifiers.hook.interaction.BlockInteractionModifierHook;
import slimeknights.tconstruct.library.modifiers.hook.interaction.InteractionSource;
import slimeknights.tconstruct.library.modifiers.modules.ModifierModule;
import slimeknights.tconstruct.library.tools.definition.aoe.IAreaOfEffectIterator;
import slimeknights.tconstruct.library.tools.definition.module.ToolModuleHooks;
import slimeknights.tconstruct.library.tools.helper.ToolDamageUtil;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;
import slimeknights.tconstruct.library.utils.MutableUseOnContext;

import java.util.Iterator;
import java.util.List;

@RequiredArgsConstructor
public abstract class BlockTransformModule implements ModifierModule, BlockInteractionModifierHook {
private static final List<ModifierHook<?>> DEFAULT_HOOKS = List.of(TinkerHooks.BLOCK_INTERACT);

protected final boolean requireGround;

@Override
public List<ModifierHook<?>> getDefaultHooks() {
return DEFAULT_HOOKS;
}

@Override
public InteractionResult afterBlockUse(IToolStackView tool, ModifierEntry modifier, UseOnContext context, InteractionSource source) {
// tool must not be broken
if (tool.isBroken() || !tool.getDefinitionData().getModule(ToolModuleHooks.INTERACTION).canInteract(tool, modifier.getId(), source)) {
return InteractionResult.PASS;
}

Player player = context.getPlayer();
if (player != null && player.isShiftKeyDown()) {
return InteractionResult.PASS;
}

// for hoes and shovels, must have nothing but plants above
if (requireGround && context.getClickedFace() == Direction.DOWN) {
return InteractionResult.PASS;
}

// must actually transform
Level world = context.getLevel();
BlockPos pos = context.getClickedPos();
BlockState original = world.getBlockState(pos);
ItemStack stack = context.getItemInHand();
boolean didTransform = transform(tool, context, original, true);

// if we made a successful transform, client can stop early
EquipmentSlot slotType = source.getSlot(context.getHand());
if (didTransform) {
if (world.isClientSide) {
return InteractionResult.SUCCESS;
}

// if the tool breaks or it was a campfire, we are done
if (ToolDamageUtil.damage(tool, 1, player, stack)) {
if (player != null) {
player.broadcastBreakEvent(slotType);
}
return InteractionResult.CONSUME;
}
}

// AOE transforming, run even if we did not transform the center
// note we consider anything effective, as hoes are not effective on all tillable blocks
if (player != null && !tool.isBroken()) {
int totalTransformed = 0;
Iterator<BlockPos> aoePos = tool.getDefinition().getData().getAOE().getBlocks(tool, stack, player, original, world, pos, context.getClickedFace(), IAreaOfEffectIterator.AOEMatchType.TRANSFORM).iterator();
if (aoePos.hasNext()) {
MutableUseOnContext offsetContext = new MutableUseOnContext(context);
do {
BlockPos newPos = aoePos.next();
if (pos.equals(newPos)) {
continue;
}

// try interacting with the new position
offsetContext.setOffsetPos(newPos);

BlockState newTarget = world.getBlockState(newPos);

// limit to playing 40 sounds, that's more than enough for most transforms
if (transform(tool, offsetContext, newTarget, totalTransformed < 40)) {
totalTransformed++;
didTransform = true;

if (world.isClientSide) {
break;
}

// stop if the tool broke
if (ToolDamageUtil.damageAnimated(tool, 1, player, slotType)) {
break;
}
}
} while (aoePos.hasNext());

// sweep attack if we transformed any
if (totalTransformed > 0) {
player.sweepAttack();
}
}
}

// if anything happened, return success
return didTransform ? InteractionResult.sidedSuccess(world.isClientSide) : InteractionResult.PASS;
}

/** Applies this transformation */
protected abstract boolean transform(IToolStackView tool, UseOnContext context, BlockState original, boolean playSound);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package slimeknights.tconstruct.library.modifiers.modules.behavior;

import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.CampfireBlock;
import net.minecraft.world.level.block.state.BlockState;
import slimeknights.mantle.data.GenericLoaderRegistry.IGenericLoader;
import slimeknights.mantle.data.GenericLoaderRegistry.SingletonLoader;
import slimeknights.tconstruct.library.modifiers.modules.ModifierModule;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;

/**
* Module which performs AOE removing of campfires
*/
public class ExtinguishCampfireModule extends BlockTransformModule {
public static final ExtinguishCampfireModule INSTANCE = new ExtinguishCampfireModule();
public static final IGenericLoader<ExtinguishCampfireModule> LOADER = new SingletonLoader<>(INSTANCE);

private ExtinguishCampfireModule() {
super(false);
}

@Override
public IGenericLoader<? extends ModifierModule> getLoader() {
return LOADER;
}

@Override
protected boolean transform(IToolStackView tool, UseOnContext context, BlockState original, boolean playSound) {
if (original.getBlock() instanceof CampfireBlock && original.getValue(CampfireBlock.LIT)) {
Level level = context.getLevel();
BlockPos pos = context.getClickedPos();
if (!level.isClientSide) {
if (playSound) {
level.playSound(null, pos, SoundEvents.GENERIC_EXTINGUISH_FIRE, SoundSource.BLOCKS, 1.0F, 1.0F);
}
CampfireBlock.dowse(context.getPlayer(), level, pos, original);
}
level.setBlock(pos, original.setValue(CampfireBlock.LIT, false), Block.UPDATE_ALL_IMMEDIATE);
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package slimeknights.tconstruct.library.modifiers.modules.behavior;

import net.minecraft.world.entity.EquipmentSlot;
import slimeknights.mantle.data.GenericLoaderRegistry.IGenericLoader;
import slimeknights.mantle.data.GenericLoaderRegistry.SingletonLoader;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.ModifierHook;
import slimeknights.tconstruct.library.modifiers.TinkerHooks;
import slimeknights.tconstruct.library.modifiers.hook.armor.EquipmentChangeModifierHook;
import slimeknights.tconstruct.library.modifiers.modules.ModifierModule;
import slimeknights.tconstruct.library.tools.capability.TinkerDataKeys;
import slimeknights.tconstruct.library.tools.context.EquipmentChangeContext;
import slimeknights.tconstruct.library.tools.helper.ModifierUtil;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;

import java.util.List;

/** Module to show the offhand for a tool that can interact using the offhand */
public enum ShowOffhandModule implements ModifierModule, EquipmentChangeModifierHook {
INSTANCE;

private static final List<ModifierHook<?>> DEFAULT_HOOKS = List.of(TinkerHooks.EQUIPMENT_CHANGE);
public static final IGenericLoader<ShowOffhandModule> LOADER = new SingletonLoader<>(INSTANCE);

@Override
public List<ModifierHook<?>> getDefaultHooks() {
return DEFAULT_HOOKS;
}

@Override
public void onEquip(IToolStackView tool, ModifierEntry modifier, EquipmentChangeContext context) {
if (context.getChangedSlot() == EquipmentSlot.CHEST) {
ModifierUtil.addTotalArmorModifierLevel(tool, context, TinkerDataKeys.SHOW_EMPTY_OFFHAND, 1);
}
}

@Override
public void onUnequip(IToolStackView tool, ModifierEntry modifier, EquipmentChangeContext context) {
if (context.getChangedSlot() == EquipmentSlot.CHEST) {
ModifierUtil.addTotalArmorModifierLevel(tool, context, TinkerDataKeys.SHOW_EMPTY_OFFHAND, -1);
}
}

@Override
public IGenericLoader<? extends ModifierModule> getLoader() {
return LOADER;
}
}

0 comments on commit 7beb503

Please sign in to comment.