Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Dispenser Behavior addition to station-items-api-v0 #99

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public enum Blocks {
FREEZER("freezer", "freezer", id -> new BlockFreezer(id).setHardness(2.5F).setSoundGroup(TemplateBlock.DEFAULT_SOUND_GROUP)),
ALTAR("altar", "altar", id -> new BlockAltar(id, Material.STONE).setHardness(3)),
VARIATION_BLOCK("variation_block", "variationBlock", id -> new VariationBlock(id, Material.STONE).setHardness(.5F).setSoundGroup(Block.DEFAULT_SOUND_GROUP).disableAutoItemRegistration()),
EMISSION_CHECKER("emission_checker", "emissionChecker", LampBlock::new);
EMISSION_CHECKER("emission_checker", "emissionChecker", LampBlock::new),
INDISPENSABLE_BLOCK("indispensable_block", "indispensableBlock", IndispensableBlock::new);

private final Runnable register;
private Block block;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.modificationstation.sltest.block;

import net.minecraft.block.Material;
import net.modificationstation.sltest.item.IndispensableBlockItem;
import net.modificationstation.stationapi.api.block.HasCustomBlockItemFactory;
import net.modificationstation.stationapi.api.template.block.TemplateBlock;
import net.modificationstation.stationapi.api.util.Identifier;

@HasCustomBlockItemFactory(IndispensableBlockItem.class)
public class IndispensableBlock extends TemplateBlock {
public IndispensableBlock(Identifier identifier) {
super(identifier, Material.WOOD);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package net.modificationstation.sltest.dispenser;


import net.mine_diver.unsafeevents.listener.EventListener;
import net.minecraft.block.Block;
import net.minecraft.block.Material;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.modificationstation.stationapi.api.dispenser.DispenseEvent;
import net.modificationstation.stationapi.api.dispenser.ItemDispenseContext;

public class DispenserListener {
@EventListener
public static void changeDispenseBehavior(DispenseEvent event) {
ItemDispenseContext context = event.context;
World world = context.dispenserBlockEntity.world;

if (context.itemStack != null) {
// Make arrows drop as an item instead of shoot as an entity
if (context.itemStack.itemId == Item.ARROW.id) {
context.skipSpecial = true;
}

BlockPos facing = context.getFacingBlockPos();

// Make buckets pickup liquids
if (context.itemStack.itemId == Item.BUCKET.id) {
if (world.method_1779(facing.x, facing.y, facing.z) == Material.WATER && world.getBlockMeta(facing.x, facing.y, facing.z) == 0) {
world.setBlock(facing.x, facing.y, facing.z, 0);
context.dispenserBlockEntity.setStack(context.slot, new ItemStack(Item.WATER_BUCKET));
event.cancel();
}

if (world.method_1779(facing.x, facing.y, facing.z) == Material.LAVA && world.getBlockMeta(facing.x, facing.y, facing.z) == 0) {
world.setBlock(facing.x, facing.y, facing.z, 0);
context.dispenserBlockEntity.setStack(context.slot, new ItemStack(Item.LAVA_BUCKET));
event.cancel();
}
}

// Make water buckets place water
if (context.itemStack.itemId == Item.WATER_BUCKET.id) {
if (world.method_1779(facing.x, facing.y, facing.z) == Material.AIR) {
world.setBlockStateWithNotify(facing.x, facing.y, facing.z, Block.WATER.getDefaultState());
context.dispenserBlockEntity.setStack(context.slot, new ItemStack(Item.BUCKET));
event.cancel();
}
}

// Make lava buckets place lava
if (context.itemStack.itemId == Item.LAVA_BUCKET.id) {
if (world.method_1779(facing.x, facing.y, facing.z) == Material.AIR) {
world.setBlockStateWithNotify(facing.x, facing.y, facing.z, Block.LAVA.getDefaultState());
context.dispenserBlockEntity.setStack(context.slot, new ItemStack(Item.BUCKET));
event.cancel();
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package net.modificationstation.sltest.item;

import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.modificationstation.sltest.block.Blocks;
import net.modificationstation.stationapi.api.client.item.CustomTooltipProvider;
import net.modificationstation.stationapi.api.dispenser.ItemDispenseContext;
import net.modificationstation.stationapi.api.item.CustomDispenseBehavior;

public class IndispensableBlockItem extends BlockItem implements CustomDispenseBehavior, CustomTooltipProvider {
public IndispensableBlockItem(int i) {
super(i);
}

@Override
public void dispense(ItemDispenseContext context) {
World world = context.dispenserBlockEntity.world;
BlockPos pos = context.getFacingBlockPos();
if (world.getBlockId(pos.x, pos.y, pos.z) == 0) {
world.setBlock(pos.x, pos.y, pos.z, Blocks.INDISPENSABLE_BLOCK.get().id);
world.playSound((float)pos.x + 0.5f, (float)pos.y + 0.5f, (float)pos.z + 0.5f, Blocks.INDISPENSABLE_BLOCK.get().soundGroup.getSound(), (Blocks.INDISPENSABLE_BLOCK.get().soundGroup.method_1976() + 1.0f) / 2.0f, Blocks.INDISPENSABLE_BLOCK.get().soundGroup.method_1977() * 0.8f);
}
}

@Override
public String[] getTooltip(ItemStack stack, String originalTooltip) {
return new String[]{originalTooltip, "Dispensers can only place this block"};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
import net.minecraft.item.ItemStack;
import net.minecraft.item.ToolMaterial;
import net.minecraft.world.World;
import net.modificationstation.stationapi.api.util.Identifier;
import net.modificationstation.stationapi.api.dispenser.ItemDispenseContext;
import net.modificationstation.stationapi.api.item.CustomDispenseBehavior;
import net.modificationstation.stationapi.api.template.item.TemplatePickaxeItem;
import net.modificationstation.stationapi.api.util.Identifier;
import net.modificationstation.stationapi.api.util.math.Direction;

public class ModdedPickaxeItem extends TemplatePickaxeItem {
public class ModdedPickaxeItem extends TemplatePickaxeItem implements CustomDispenseBehavior {

public ModdedPickaxeItem(Identifier identifier, ToolMaterial material) {
super(identifier, material);
Expand All @@ -36,4 +38,10 @@ public boolean useOnBlock(ItemStack item, PlayerEntity player, World level, int
} else
return false;
}

@Override
public void dispense(ItemDispenseContext context) {
LivingEntity entity = (LivingEntity) EntityRegistry.create("GPoor", context.dispenserBlockEntity.world);
context.shootEntity(entity);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public void registerTextures(TextureRegisterEvent event) {
ExpandableAtlas terrain = Atlases.getTerrain();

TEST_BLOCK.get().textureId = terrain.addTexture(of(NAMESPACE, "blocks/testBlock")).index;
INDISPENSABLE_BLOCK.get().textureId = terrain.addTexture(of(NAMESPACE, "blocks/indispensableBlock")).index;
TEST_ANIMATED_BLOCK.get().textureId = terrain.addTexture(of(NAMESPACE, "blocks/testAnimatedBlock")).index;
FREEZER.get().textureId = terrain.addTexture(of(NAMESPACE, "blocks/FreezerTop")).index;
((BlockFreezer) FREEZER.get()).sideTexture = terrain.addTexture(of(NAMESPACE, "blocks/FreezerSide")).index;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ [email protected]=Test Animated Block
[email protected]=Charged Animated Block
[email protected]=Test Pickaxe
[email protected]_block.name=FarLands Block
[email protected]=Indispensable Block
[email protected]_item.name=NBT Item
[email protected]=Freezer
[email protected]=Altar
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion src/test/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"net.modificationstation.sltest.item.tool.ToolListener",
"net.modificationstation.sltest.datafixer.DataFixerListener",
"net.modificationstation.sltest.worldgen.TestWorldgenListener",
"net.modificationstation.sltest.bonemeal.BonemealListener"
"net.modificationstation.sltest.bonemeal.BonemealListener",
"net.modificationstation.sltest.dispenser.DispenserListener"
],
"stationapi:event_bus_client": [
"net.modificationstation.sltest.gui.GuiListener",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.modificationstation.stationapi.api.dispenser;

import lombok.experimental.SuperBuilder;
import net.mine_diver.unsafeevents.Event;
import net.mine_diver.unsafeevents.event.Cancelable;
import net.mine_diver.unsafeevents.event.EventPhases;
import net.modificationstation.stationapi.api.StationAPI;
import net.modificationstation.stationapi.api.item.CustomDispenseBehavior;

/**
* Event that gets called when dispenser is activated, precedes and overrides CustomDispenseBehavior.
* @author matthewperiut
* @see CustomDispenseBehavior
* @see ItemDispenseContext
*/
@Cancelable
@SuperBuilder
@EventPhases(StationAPI.INTERNAL_PHASE)
public class DispenseEvent extends Event {
public final ItemDispenseContext context;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package net.modificationstation.stationapi.api.dispenser;

import net.minecraft.block.entity.DispenserBlockEntity;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.modificationstation.stationapi.api.item.CustomDispenseBehavior;
import net.modificationstation.stationapi.api.util.math.Direction;
import net.modificationstation.stationapi.mixin.item.EntityAccessor;

import java.util.Random;

/**
* Utility class for changing dispenser behavior, providing tools to customize behavior
* @author matthewperiut
* @see DispenseEvent
* @see CustomDispenseBehavior
*/
public class ItemDispenseContext {
public final DispenserBlockEntity dispenserBlockEntity;
public final int slot;
public final Direction dispenseDirection;
public ItemStack itemStack;
/**
* Skips special vanilla behavior for arrows, snowballs and eggs.
*/
public boolean skipSpecial;

public ItemDispenseContext(ItemStack itemStack, DispenserBlockEntity dispenserBlockEntity, int slot) {
this.dispenserBlockEntity = dispenserBlockEntity;
this.itemStack = itemStack;
this.slot = slot;
dispenseDirection = Direction.byId(dispenserBlockEntity.world.getBlockMeta(dispenserBlockEntity.x, dispenserBlockEntity.y, dispenserBlockEntity.z));
}

public static void genericShootEntity(Entity entity, double velX, double velY, double velZ, float pitch, float yaw) {
float var9 = MathHelper.sqrt(velX * velX + velY * velY + velZ * velZ);
velX /= var9;
velY /= var9;
velZ /= var9;
Random random = ((EntityAccessor) entity).stationapi_getRandom();
velX += random.nextGaussian() * 0.007499999832361937 * yaw;
velY += random.nextGaussian() * 0.007499999832361937 * yaw;
velZ += random.nextGaussian() * 0.007499999832361937 * yaw;
velX *= pitch;
velY *= pitch;
velZ *= pitch;
entity.velocityX = velX;
entity.velocityY = velY;
entity.velocityZ = velZ;
float var10 = MathHelper.sqrt(velX * velX + velZ * velZ);
entity.prevYaw = entity.yaw = (float) (Math.atan2(velX, velZ) * 180.0 / 3.1415927410125732);
entity.prevPitch = entity.pitch = (float) (Math.atan2(velY, var10) * 180.0 / 3.1415927410125732);
}

public interface ShootEntityFunction {
void shoot(Entity entity, double velX, double velY, double velZ, float pitch, float yaw);
}

public void shootEntity(Entity entity) {
shootEntity(entity, ItemDispenseContext::genericShootEntity);
}

public void shootEntity(Entity entity, ShootEntityFunction shootFunc) {
entity.method_1340(dispenserBlockEntity.x + 0.5, dispenserBlockEntity.y + 0.5, dispenserBlockEntity.z + 0.5);
shootFunc.shoot(entity, dispenseDirection.getOffsetX(), dispenseDirection.getOffsetY() + 0.1, dispenseDirection.getOffsetZ(), 1.1F, 6.0F);
dispenserBlockEntity.world.method_210(entity);
dispenserBlockEntity.world.method_230(1002, dispenserBlockEntity.x, dispenserBlockEntity.y, dispenserBlockEntity.z, 0);
dispenserBlockEntity.world.method_230(2000, dispenserBlockEntity.x, dispenserBlockEntity.y, dispenserBlockEntity.z, dispenseDirection.getOffsetX() + 1 + (dispenseDirection.getOffsetZ() + 1) * 3);
}

public BlockPos getFacingBlockPos() {
return new BlockPos(dispenserBlockEntity.x, dispenserBlockEntity.y, dispenserBlockEntity.z).add(dispenseDirection.getVector());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package net.modificationstation.stationapi.api.item;

import net.modificationstation.stationapi.api.dispenser.ItemDispenseContext;

/**
* Intended to be applied to an Item, which provides a custom dispenser behavior
* @author matthewperiut
* @see ItemDispenseContext
*/
public interface CustomDispenseBehavior {
void dispense(ItemDispenseContext context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.modificationstation.stationapi.impl.dispenser;

import net.mine_diver.unsafeevents.listener.EventListener;
import net.modificationstation.stationapi.api.StationAPI;
import net.modificationstation.stationapi.api.dispenser.DispenseEvent;
import net.modificationstation.stationapi.api.item.CustomDispenseBehavior;
import net.modificationstation.stationapi.api.mod.entrypoint.Entrypoint;
import net.modificationstation.stationapi.api.mod.entrypoint.EventBusPolicy;

@Entrypoint(eventBus = @EventBusPolicy(registerInstance = false))
@EventListener(phase = StationAPI.INTERNAL_PHASE)
public class CustomDispenseBehaviorImpl {
@EventListener
private static void implementCustomDispenseBehaviorInterface(DispenseEvent event) {
if (event.context.itemStack != null) {
if (event.context.itemStack.getItem() instanceof CustomDispenseBehavior behavior) {
behavior.dispense(event.context);
event.cancel();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.modificationstation.stationapi.impl.dispenser;

public class DispenserInfoStorage {
public static int slot;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package net.modificationstation.stationapi.mixin.item;

import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

import java.util.Random;

@Mixin(Entity.class)
public interface EntityAccessor {
@Accessor("random")
Random stationapi_getRandom();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package net.modificationstation.stationapi.mixin.item.dispenser.block;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import com.llamalad7.mixinextras.sugar.Share;
import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import net.minecraft.block.DispenserBlock;
import net.minecraft.block.entity.DispenserBlockEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import net.modificationstation.stationapi.api.StationAPI;
import net.modificationstation.stationapi.api.dispenser.DispenseEvent;
import net.modificationstation.stationapi.api.dispenser.ItemDispenseContext;
import net.modificationstation.stationapi.impl.dispenser.DispenserInfoStorage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.util.Random;

@Mixin(DispenserBlock.class)
class DispenserBlockMixin {
@Inject(
method = "method_1774",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/item/ItemStack;itemId:I",
ordinal = 0
),
cancellable = true
)
private void stationapi_dispatchDispenseEvent(
World world, int x, int y, int z, Random random, CallbackInfo ci,
@Local(index = 11) DispenserBlockEntity dispenserBlockEntity, @Local(index = 12) LocalRef<ItemStack> dispensedStackRef,
@Share("skipSpecial") LocalBooleanRef skipSpecialRef
) {
int slot = DispenserInfoStorage.slot;
ItemDispenseContext context = new ItemDispenseContext(dispensedStackRef.get(), dispenserBlockEntity, slot);

if (StationAPI.EVENT_BUS.post(
DispenseEvent.builder().context(context).build()
).isCanceled()) ci.cancel();

dispensedStackRef.set(context.itemStack);
skipSpecialRef.set(context.skipSpecial);
}

@WrapOperation(
method = "method_1774",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/item/Item;id:I"
)
)
private int stationapi_skipSpecial(
Item instance, Operation<Integer> original,
@Local(index = 12) ItemStack dispensedStackRef,
@Share("skipSpecial") LocalBooleanRef skipSpecialRef
) {
return skipSpecialRef.get() ? dispensedStackRef.itemId + 1 : original.call(instance);
}
}
Loading