mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-03-04 06:44:40 +01:00
Merge branch 'mc1.20.1/feature-dev' into mc1.21.1/dev
This commit is contained in:
commit
994f0afd3e
40 changed files with 794 additions and 698 deletions
13
build.gradle
13
build.gradle
|
@ -98,7 +98,6 @@ neoForge {
|
|||
data {
|
||||
data()
|
||||
|
||||
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
||||
programArguments.addAll('--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath())
|
||||
}
|
||||
|
||||
|
@ -125,7 +124,8 @@ repositories {
|
|||
maven { url = "https://maven.saps.dev/releases" } // FTB Mods
|
||||
maven { url = "https://maven.architectury.dev" } // Arch API
|
||||
maven { url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven" } // NeoForge config api port, needed by ponder
|
||||
maven { url = "https://jm.gserv.me/repository/maven-public" // JourneyMap
|
||||
maven {
|
||||
url = "https://jm.gserv.me/repository/maven-public" // JourneyMap
|
||||
content {
|
||||
includeGroup "info.journeymap"
|
||||
includeGroup "mysticdrew"
|
||||
|
@ -167,7 +167,8 @@ dependencies {
|
|||
if (cc_tweaked_enable.toBoolean()) {
|
||||
compileOnly("cc.tweaked:cc-tweaked-${cc_tweaked_minecraft_version}-core-api:${cc_tweaked_version}")
|
||||
compileOnly("cc.tweaked:cc-tweaked-${cc_tweaked_minecraft_version}-forge-api:${cc_tweaked_version}")
|
||||
runtimeOnly("cc.tweaked:cc-tweaked-${cc_tweaked_minecraft_version}-forge:${cc_tweaked_version}")
|
||||
// TODO - Uncomment when cc tweaked supports the api refactors
|
||||
//runtimeOnly("cc.tweaked:cc-tweaked-${cc_tweaked_minecraft_version}-forge:${cc_tweaked_version}")
|
||||
}
|
||||
|
||||
runtimeOnly("dev.architectury:architectury-neoforge:13.0.8")
|
||||
|
@ -221,7 +222,7 @@ jar {
|
|||
|
||||
manifest.attributes([
|
||||
"MixinConfigs": "create.mixins.json",
|
||||
"Git-Hash": gitHash
|
||||
"Git-Hash" : gitHash
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -294,7 +295,7 @@ String calculateGitHash() {
|
|||
commandLine("git", "rev-parse", "HEAD")
|
||||
}
|
||||
return output.standardOutput.asText.get().trim()
|
||||
} catch(Throwable ignored) {
|
||||
} catch (Throwable ignored) {
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +309,7 @@ boolean hasUnstaged() {
|
|||
if (!result.isEmpty())
|
||||
println("Found stageable results:\n ${result}\n")
|
||||
return !result.isEmpty()
|
||||
} catch(Throwable ignored) {
|
||||
} catch (Throwable ignored) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// 1.20.1 2025-02-20T18:50:30.47170729 Registrate Provider for create [Recipes, Advancements, Loot Tables, Tags (blocks), Tags (items), Tags (fluids), Tags (entity_types), Blockstates, Item models, Lang (en_us/en_ud)]
|
||||
// 1.20.1 2025-02-21T09:54:58.646012686 Registrate Provider for create [Recipes, Advancements, Loot Tables, Tags (blocks), Tags (items), Tags (fluids), Tags (entity_types), Blockstates, Item models, Lang (en_us/en_ud)]
|
||||
60bbdf92d2ac9824ea6144955c74043a6005f79d assets/create/blockstates/acacia_window.json
|
||||
6a67703c2697d81b7dc83e9d72a66f9c9ff08383 assets/create/blockstates/acacia_window_pane.json
|
||||
c3ae87b62e81d8e9476eccd793bb1548d74c66a1 assets/create/blockstates/adjustable_chain_gearshift.json
|
||||
|
@ -4728,6 +4728,7 @@ f43cac8216e2a9347e48cf93a43de95dd810ca20 data/create/tags/items/contraption_cont
|
|||
d371dfd35e49a7bef19f59c03e7f4ae20992f03d data/create/tags/items/create_ingots.json
|
||||
910d0f5ccbc4c84b224eca1f1588b1695f41447b data/create/tags/items/crushed_raw_materials.json
|
||||
0fa526e7e742573b603ad26b09526cf724efa1dc data/create/tags/items/deployable_drink.json
|
||||
35133e95f1c8fdd7a1c21afcc231fc0bffefb9a8 data/create/tags/items/dispense_behavior_wrap_blacklist.json
|
||||
ebd7b09daf2f64c0c04d821696b0e145683d8693 data/create/tags/items/dyed_table_cloths.json
|
||||
d2bb65d893d71d2d9871f81f430c233a93adb4bb data/create/tags/items/invalid_for_track_paving.json
|
||||
1cebeb92bd514b75d54ac6d5708047801f0501c5 data/create/tags/items/modded_stripped_logs.json
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
// 1.20.1 2025-02-20T19:36:44.18737762 Create's Mounted Item Storage Type Tags
|
||||
c65f95f356db09e468847e5799a2cdd8e1417cac data/create/tags/create/mounted_item_storage_type/fuel_blacklist.json
|
||||
fdadceec842a4cd12dd95f7e271645a52829ec6e data/create/tags/create/mounted_item_storage_type/internal.json
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"values": [
|
||||
"create:vault"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"values": [
|
||||
"create:dispenser"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"values": []
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
package com.simibubi.create;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.simibubi.create.api.contraption.dispenser.DefaultMountedDispenseBehavior;
|
||||
import com.simibubi.create.api.contraption.dispenser.MountedDispenseBehavior;
|
||||
import com.simibubi.create.api.contraption.dispenser.MountedProjectileDispenseBehavior;
|
||||
import com.simibubi.create.api.contraption.dispenser.OptionalMountedDispenseBehavior;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.item.PrimedTnt;
|
||||
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.entity.projectile.SmallFireball;
|
||||
import net.minecraft.world.entity.projectile.ThrownPotion;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.SpawnEggItem;
|
||||
import net.minecraft.world.item.alchemy.PotionContents;
|
||||
import net.minecraft.world.item.alchemy.Potions;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.BeehiveBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.BucketPickup;
|
||||
import net.minecraft.world.level.block.LevelEvent;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class AllMountedDispenseItemBehaviors {
|
||||
private static final MountedDispenseBehavior SPAWN_EGG = new DefaultMountedDispenseBehavior() {
|
||||
@Override
|
||||
protected ItemStack execute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
if (!(stack.getItem() instanceof SpawnEggItem egg))
|
||||
return super.execute(stack, context, pos, facing);
|
||||
|
||||
if (context.world instanceof ServerLevel serverLevel) {
|
||||
EntityType<?> type = egg.getType(stack);
|
||||
BlockPos offset = BlockPos.containing(facing.x + .7, facing.y + .7, facing.z + .7);
|
||||
Entity entity = type.spawn(serverLevel, stack, null, pos.offset(offset), MobSpawnType.DISPENSER, facing.y < .5, false);
|
||||
if (entity != null) {
|
||||
entity.setDeltaMovement(context.motion.scale(2));
|
||||
}
|
||||
}
|
||||
|
||||
stack.shrink(1);
|
||||
return stack;
|
||||
}
|
||||
};
|
||||
private static final MountedDispenseBehavior TNT = new DefaultMountedDispenseBehavior() {
|
||||
@Override
|
||||
protected ItemStack execute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
double x = pos.getX() + facing.x * .7 + .5;
|
||||
double y = pos.getY() + facing.y * .7 + .5;
|
||||
double z = pos.getZ() + facing.z * .7 + .5;
|
||||
PrimedTnt tnt = new PrimedTnt(context.world, x, y, z, null);
|
||||
tnt.push(context.motion.x, context.motion.y, context.motion.z);
|
||||
context.world.addFreshEntity(tnt);
|
||||
context.world.playSound(null, tnt.getX(), tnt.getY(), tnt.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1, 1);
|
||||
stack.shrink(1);
|
||||
return stack;
|
||||
}
|
||||
};
|
||||
private static final MountedDispenseBehavior FIREWORK = new DefaultMountedDispenseBehavior() {
|
||||
@Override
|
||||
protected ItemStack execute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
double x = pos.getX() + facing.x * .7 + .5;
|
||||
double y = pos.getY() + facing.y * .7 + .5;
|
||||
double z = pos.getZ() + facing.z * .7 + .5;
|
||||
FireworkRocketEntity firework = new FireworkRocketEntity(context.world, stack, x, y, z, true);
|
||||
firework.shoot(facing.x, facing.y, facing.z, 0.5F, 1.0F);
|
||||
context.world.addFreshEntity(firework);
|
||||
stack.shrink(1);
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(LevelAccessor level, BlockPos pos) {
|
||||
level.levelEvent(LevelEvent.SOUND_FIREWORK_SHOOT, pos, 0);
|
||||
}
|
||||
};
|
||||
private static final MountedDispenseBehavior FIRE_CHARGE = new DefaultMountedDispenseBehavior() {
|
||||
@Override
|
||||
protected ItemStack execute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
RandomSource random = context.world.random;
|
||||
double x = pos.getX() + facing.x * .7 + .5;
|
||||
double y = pos.getY() + facing.y * .7 + .5;
|
||||
double z = pos.getZ() + facing.z * .7 + .5;
|
||||
SmallFireball fireball = new SmallFireball(
|
||||
context.world,
|
||||
x, y, z,
|
||||
new Vec3(random.nextGaussian() * 0.05 + facing.x + context.motion.x,
|
||||
random.nextGaussian() * 0.05 + facing.y + context.motion.y,
|
||||
random.nextGaussian() * 0.05 + facing.z + context.motion.z).normalize()
|
||||
);
|
||||
fireball.setItem(stack); // copies the stack
|
||||
context.world.addFreshEntity(fireball);
|
||||
stack.shrink(1);
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(LevelAccessor level, BlockPos pos) {
|
||||
level.levelEvent(LevelEvent.SOUND_BLAZE_FIREBALL, pos, 0);
|
||||
}
|
||||
};
|
||||
private static final MountedDispenseBehavior BUCKET = new DefaultMountedDispenseBehavior() {
|
||||
@Override
|
||||
protected ItemStack execute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
BlockPos interactionPos = pos.relative(MountedDispenseBehavior.getClosestFacingDirection(facing));
|
||||
BlockState state = context.world.getBlockState(interactionPos);
|
||||
if (!(state.getBlock() instanceof BucketPickup bucketPickup)) {
|
||||
return super.execute(stack, context, pos, facing);
|
||||
}
|
||||
|
||||
ItemStack bucket = bucketPickup.pickupBlock(null, context.world, interactionPos, state);
|
||||
MountedDispenseBehavior.placeItemInInventory(bucket, context, pos);
|
||||
stack.shrink(1);
|
||||
return stack;
|
||||
}
|
||||
};
|
||||
private static final MountedDispenseBehavior POTIONS = new MountedProjectileDispenseBehavior() {
|
||||
@Override
|
||||
protected Projectile getProjectile(Level level, double x, double y, double z, ItemStack stack, Direction facing) {
|
||||
ThrownPotion potion = new ThrownPotion(level, x, y, z);
|
||||
potion.setItem(stack); // copies item
|
||||
return potion;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getUncertainty() {
|
||||
return super.getUncertainty() * 0.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getPower() {
|
||||
return super.getPower() * 1.25f;
|
||||
}
|
||||
};
|
||||
private static final MountedDispenseBehavior BOTTLE = new OptionalMountedDispenseBehavior() {
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemStack doExecute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
BlockPos interactionPos = pos.relative(MountedDispenseBehavior.getClosestFacingDirection(facing));
|
||||
BlockState state = context.world.getBlockState(interactionPos);
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (block instanceof BeehiveBlock hive && state.is(BlockTags.BEEHIVES) && state.getValue(BeehiveBlock.HONEY_LEVEL) >= 5) {
|
||||
hive.releaseBeesAndResetHoneyLevel(context.world, state, interactionPos, null, BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED);
|
||||
MountedDispenseBehavior.placeItemInInventory(new ItemStack(Items.HONEY_BOTTLE), context, pos);
|
||||
stack.shrink(1);
|
||||
return stack;
|
||||
} else if (context.world.getFluidState(interactionPos).is(FluidTags.WATER)) {
|
||||
ItemStack waterBottle = PotionContents.createItemStack(Items.POTION, Potions.WATER);
|
||||
MountedDispenseBehavior.placeItemInInventory(waterBottle, context, pos);
|
||||
stack.shrink(1);
|
||||
return stack;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static void registerDefaults() {
|
||||
for (SpawnEggItem egg : SpawnEggItem.eggs()) {
|
||||
MountedDispenseBehavior.REGISTRY.register(egg, SPAWN_EGG);
|
||||
}
|
||||
|
||||
MountedDispenseBehavior.REGISTRY.register(Items.TNT, TNT);
|
||||
MountedDispenseBehavior.REGISTRY.register(Items.FIREWORK_ROCKET, FIREWORK);
|
||||
MountedDispenseBehavior.REGISTRY.register(Items.FIRE_CHARGE, FIRE_CHARGE);
|
||||
MountedDispenseBehavior.REGISTRY.register(Items.BUCKET, BUCKET);
|
||||
MountedDispenseBehavior.REGISTRY.register(Items.GLASS_BOTTLE, BOTTLE);
|
||||
|
||||
// potions can't be automatically converted since they use a weird wrapper thing
|
||||
MountedDispenseBehavior.REGISTRY.register(Items.SPLASH_POTION, POTIONS);
|
||||
MountedDispenseBehavior.REGISTRY.register(Items.LINGERING_POTION, POTIONS);
|
||||
}
|
||||
}
|
|
@ -13,8 +13,6 @@ public class AllMovementBehaviours {
|
|||
MovementBehaviour.REGISTRY.register(Blocks.BELL, new BellMovementBehaviour());
|
||||
MovementBehaviour.REGISTRY.register(Blocks.CAMPFIRE, new CampfireMovementBehaviour());
|
||||
MovementBehaviour.REGISTRY.register(Blocks.SOUL_CAMPFIRE, new CampfireMovementBehaviour());
|
||||
|
||||
DispenserMovementBehaviour.gatherMovedDispenseItemBehaviours();
|
||||
MovementBehaviour.REGISTRY.register(Blocks.DISPENSER, new DispenserMovementBehaviour());
|
||||
MovementBehaviour.REGISTRY.register(Blocks.DROPPER, new DropperMovementBehaviour());
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import static com.simibubi.create.AllTags.NameSpace.QUARK;
|
|||
import static com.simibubi.create.AllTags.NameSpace.TIC;
|
||||
|
||||
import com.simibubi.create.api.contraption.ContraptionType;
|
||||
import com.simibubi.create.api.contraption.storage.item.MountedItemStorage;
|
||||
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
|
||||
import com.simibubi.create.api.registry.CreateRegistries;
|
||||
|
||||
import net.createmod.catnip.lang.Lang;
|
||||
|
@ -191,6 +193,7 @@ public class AllTags {
|
|||
TRACKS,
|
||||
UPRIGHT_ON_BELT,
|
||||
VALVE_HANDLES,
|
||||
DISPENSE_BEHAVIOR_WRAP_BLACKLIST,
|
||||
|
||||
PLATES(COMMON),
|
||||
OBSIDIAN_DUST(COMMON, "dusts/obsidian"),
|
||||
|
@ -415,6 +418,31 @@ public class AllTags {
|
|||
}
|
||||
}
|
||||
|
||||
public enum AllMountedItemStorageTypeTags {
|
||||
INTERNAL,
|
||||
FUEL_BLACKLIST;
|
||||
|
||||
public final TagKey<MountedItemStorageType<?>> tag;
|
||||
public final boolean alwaysDatagen;
|
||||
|
||||
AllMountedItemStorageTypeTags() {
|
||||
ResourceLocation tagId = Create.asResource(Lang.asId(this.name()));
|
||||
this.tag = TagKey.create(CreateRegistries.MOUNTED_ITEM_STORAGE_TYPE, tagId);
|
||||
this.alwaysDatagen = true;
|
||||
}
|
||||
|
||||
public boolean matches(MountedItemStorage storage) {
|
||||
return this.matches(storage.type);
|
||||
}
|
||||
|
||||
public boolean matches(MountedItemStorageType<?> type) {
|
||||
return type.is(this.tag);
|
||||
}
|
||||
|
||||
private static void init() {
|
||||
}
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
AllBlockTags.init();
|
||||
AllItemTags.init();
|
||||
|
@ -422,5 +450,6 @@ public class AllTags {
|
|||
AllEntityTags.init();
|
||||
AllRecipeSerializerTags.init();
|
||||
AllContraptionTypeTags.init();
|
||||
AllMountedItemStorageTypeTags.init();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,6 +177,7 @@ public class Create {
|
|||
AllInteractionBehaviours.registerDefaults();
|
||||
AllContraptionMovementSettings.registerDefaults();
|
||||
AllOpenPipeEffectHandlers.registerDefaults();
|
||||
AllMountedDispenseItemBehaviors.registerDefaults();
|
||||
// --
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package com.simibubi.create.api.contraption.dispenser;
|
||||
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.LevelEvent;
|
||||
import net.minecraft.world.level.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
/**
|
||||
* A parallel to {@link DefaultDispenseItemBehavior}, providing a common, default, extendable dispense implementation.
|
||||
*/
|
||||
public class DefaultMountedDispenseBehavior implements MountedDispenseBehavior {
|
||||
/**
|
||||
* A reusable instance of the default behavior.
|
||||
*/
|
||||
public static final MountedDispenseBehavior INSTANCE = new DefaultMountedDispenseBehavior();
|
||||
|
||||
@Override
|
||||
public ItemStack dispense(ItemStack stack, MovementContext context, BlockPos pos) {
|
||||
Vec3 normal = MountedDispenseBehavior.getDispenserNormal(context);
|
||||
|
||||
Direction closestToFacing = MountedDispenseBehavior.getClosestFacingDirection(normal);
|
||||
Container inventory = HopperBlockEntity.getContainerAt(context.world, pos.relative(closestToFacing));
|
||||
if (inventory == null) {
|
||||
ItemStack remainder = this.execute(stack, context, pos, normal);
|
||||
this.playSound(context.world, pos);
|
||||
this.playAnimation(context.world, pos, closestToFacing);
|
||||
return remainder;
|
||||
} else {
|
||||
ItemStack toInsert = stack.copyWithCount(1);
|
||||
ItemStack remainder = HopperBlockEntity.addItem(null, inventory, toInsert, closestToFacing.getOpposite());
|
||||
if (remainder.isEmpty()) {
|
||||
stack.shrink(1);
|
||||
}
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispense the given item. Sounds and particles are already handled.
|
||||
* @return the remaining items after dispensing one
|
||||
*/
|
||||
protected ItemStack execute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
ItemStack toDispense = stack.split(1);
|
||||
spawnItem(context.world, toDispense, 6, facing, pos, context);
|
||||
return stack;
|
||||
}
|
||||
|
||||
protected void playSound(LevelAccessor level, BlockPos pos) {
|
||||
level.levelEvent(LevelEvent.SOUND_DISPENSER_DISPENSE, pos, 0);
|
||||
}
|
||||
|
||||
protected void playAnimation(LevelAccessor level, BlockPos pos, Vec3 facing) {
|
||||
this.playAnimation(level, pos, MountedDispenseBehavior.getClosestFacingDirection(facing));
|
||||
}
|
||||
|
||||
protected void playAnimation(LevelAccessor level, BlockPos pos, Direction direction) {
|
||||
level.levelEvent(LevelEvent.PARTICLES_SHOOT_SMOKE, pos, direction.get3DDataValue());
|
||||
}
|
||||
|
||||
public static void spawnItem(Level level, ItemStack stack, int speed, Vec3 facing, BlockPos pos, MovementContext context) {
|
||||
double x = pos.getX() + facing.x + .5;
|
||||
double y = pos.getY() + facing.y + .5;
|
||||
double z = pos.getZ() + facing.z + .5;
|
||||
if (MountedDispenseBehavior.getClosestFacingDirection(facing).getAxis() == Direction.Axis.Y) {
|
||||
y = y - 0.125;
|
||||
} else {
|
||||
y = y - 0.15625;
|
||||
}
|
||||
|
||||
ItemEntity entity = new ItemEntity(level, x, y, z, stack);
|
||||
double d3 = level.random.nextDouble() * 0.1 + 0.2;
|
||||
entity.setDeltaMovement(
|
||||
level.random.nextGaussian() * 0.0075 * speed + facing.x() * d3 + context.motion.x,
|
||||
level.random.nextGaussian() * 0.0075 * speed + facing.y() * d3 + context.motion.y,
|
||||
level.random.nextGaussian() * 0.0075 * speed + facing.z() * d3 + context.motion.z
|
||||
);
|
||||
level.addFreshEntity(entity);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package com.simibubi.create.api.contraption.dispenser;
|
||||
|
||||
import com.simibubi.create.api.registry.SimpleRegistry;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.impl.contraption.dispenser.DispenserBehaviorConverter;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.dispenser.DispenseItemBehavior;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import net.neoforged.neoforge.items.ItemHandlerHelper;
|
||||
import net.neoforged.neoforge.items.wrapper.CombinedInvWrapper;
|
||||
|
||||
/**
|
||||
* A parallel to {@link DispenseItemBehavior}, for use by mounted dispensers.
|
||||
* Create will attempt to wrap existing {@link DispenseItemBehavior}s, but this interface can be used to provide better or fixed behavior.
|
||||
* @see DefaultMountedDispenseBehavior
|
||||
* @see MountedProjectileDispenseBehavior
|
||||
* @see OptionalMountedDispenseBehavior
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MountedDispenseBehavior {
|
||||
SimpleRegistry<Item, MountedDispenseBehavior> REGISTRY = Util.make(() -> {
|
||||
SimpleRegistry<Item, MountedDispenseBehavior> registry = SimpleRegistry.create();
|
||||
registry.registerProvider(DispenserBehaviorConverter.INSTANCE);
|
||||
return registry;
|
||||
});
|
||||
|
||||
/**
|
||||
* Dispense the given stack into the world.
|
||||
* @param stack the stack to dispense. Safe to modify, behaviors are given a copy
|
||||
* @param context the MovementContext of the dispenser
|
||||
* @param pos the BlockPos being visited by the dispenser
|
||||
* @return the remaining stack after dispensing one item
|
||||
*/
|
||||
ItemStack dispense(ItemStack stack, MovementContext context, BlockPos pos);
|
||||
|
||||
// utilities for implementations
|
||||
|
||||
static Vec3 getDispenserNormal(MovementContext ctx) {
|
||||
Direction facing = ctx.state.getValue(DispenserBlock.FACING);
|
||||
Vec3 normal = Vec3.atLowerCornerOf(facing.getNormal());
|
||||
return ctx.rotation.apply(normal).normalize();
|
||||
}
|
||||
|
||||
static Direction getClosestFacingDirection(Vec3 facing) {
|
||||
return Direction.getNearest(facing.x, facing.y, facing.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to place an item back into the inventory. This is used in the case of item overflow, such as a stack
|
||||
* of buckets becoming two separate stacks when one is filled with water.
|
||||
* <p>
|
||||
* First tries to insert directly into the dispenser inventory. If that fails, it then tries the contraption's
|
||||
* whole inventory. If that still fails, the stack is dispensed into the world with the default behavior.
|
||||
* @param stack the stack to store in the inventory
|
||||
* @param context the MovementContext given to the behavior
|
||||
* @param pos the position given to the behavior
|
||||
*/
|
||||
static void placeItemInInventory(ItemStack stack, MovementContext context, BlockPos pos) {
|
||||
ItemStack toInsert = stack.copy();
|
||||
// try inserting into own inventory first
|
||||
ItemStack remainder = ItemHandlerHelper.insertItem(context.getItemStorage(), toInsert, false);
|
||||
if (!remainder.isEmpty()) {
|
||||
// next, try the whole contraption inventory
|
||||
CombinedInvWrapper contraption = context.contraption.getStorage().getAllItems();
|
||||
ItemStack newRemainder = ItemHandlerHelper.insertItem(contraption, remainder, false);
|
||||
if (!newRemainder.isEmpty()) {
|
||||
// if there's *still* something left, dispense into world
|
||||
DefaultMountedDispenseBehavior.INSTANCE.dispense(remainder, context, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.simibubi.create.api.contraption.dispenser;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.foundation.mixin.accessor.ProjectileDispenseBehaviorAccessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.dispenser.ProjectileDispenseBehavior;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.LevelEvent;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
/**
|
||||
* A parallel to {@link ProjectileDispenseBehavior}, providing a base implementation for projectile-shooting behaviors.
|
||||
*/
|
||||
public abstract class MountedProjectileDispenseBehavior extends DefaultMountedDispenseBehavior {
|
||||
@Override
|
||||
protected ItemStack execute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
double x = pos.getX() + facing.x * .7 + .5;
|
||||
double y = pos.getY() + facing.y * .7 + .5;
|
||||
double z = pos.getZ() + facing.z * .7 + .5;
|
||||
Projectile projectile = this.getProjectile(context.world, x, y, z, stack.copy(), MountedDispenseBehavior.getClosestFacingDirection(facing));
|
||||
if (projectile == null)
|
||||
return stack;
|
||||
|
||||
Vec3 motion = facing.scale(this.getPower()).add(context.motion);
|
||||
projectile.shoot(motion.x, motion.y, motion.z, (float) motion.length(), this.getUncertainty());
|
||||
context.world.addFreshEntity(projectile);
|
||||
stack.shrink(1);
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(LevelAccessor level, BlockPos pos) {
|
||||
level.levelEvent(LevelEvent.SOUND_DISPENSER_PROJECTILE_LAUNCH, pos, 0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract Projectile getProjectile(Level level, double x, double y, double z, ItemStack stack, Direction facing);
|
||||
|
||||
protected float getUncertainty() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
protected float getPower() {
|
||||
return 1.1f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a mounted behavior wrapper from a vanilla projectile dispense behavior.
|
||||
*/
|
||||
public static MountedDispenseBehavior of(ProjectileDispenseBehavior vanillaBehaviour) {
|
||||
ProjectileDispenseBehaviorAccessor accessor = (ProjectileDispenseBehaviorAccessor) vanillaBehaviour;
|
||||
return new MountedProjectileDispenseBehavior() {
|
||||
@Override
|
||||
protected Projectile getProjectile(Level level, double x, double y, double z, ItemStack stack, Direction facing) {
|
||||
return accessor.create$getProjectileItem().asProjectile(level, new Vec3(x, y, z), stack, facing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getUncertainty() {
|
||||
return accessor.create$getDispenseConfig().uncertainty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getPower() {
|
||||
return accessor.create$getDispenseConfig().power();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.simibubi.create.api.contraption.dispenser;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.LevelEvent;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
/**
|
||||
* A mounted dispenser behavior that might fail, playing the empty sound if it does.
|
||||
*/
|
||||
public class OptionalMountedDispenseBehavior extends DefaultMountedDispenseBehavior {
|
||||
private boolean success;
|
||||
|
||||
@Override
|
||||
protected final ItemStack execute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
ItemStack remainder = this.doExecute(stack, context, pos, facing);
|
||||
this.success = remainder != null;
|
||||
return remainder == null ? stack : remainder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(LevelAccessor level, BlockPos pos) {
|
||||
if (this.success) {
|
||||
super.playSound(level, pos);
|
||||
} else {
|
||||
level.levelEvent(LevelEvent.SOUND_DISPENSER_FAIL, pos, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispense the given item.
|
||||
* @return the remaining items after dispensing one, or null if it failed
|
||||
*/
|
||||
@Nullable
|
||||
protected ItemStack doExecute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
return super.execute(stack, context, pos, facing);
|
||||
}
|
||||
}
|
|
@ -8,11 +8,8 @@ import java.util.function.Predicate;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.simibubi.create.api.behaviour.movement.MovementBehaviour;
|
||||
import com.simibubi.create.api.contraption.storage.item.menu.MountedStorageMenus;
|
||||
import com.simibubi.create.content.contraptions.Contraption;
|
||||
import com.simibubi.create.content.contraptions.MountedStorageManager;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.foundation.utility.CreateLang;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
@ -59,26 +56,6 @@ public abstract class MountedItemStorage implements IItemHandlerModifiable {
|
|||
*/
|
||||
public abstract void unmount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be);
|
||||
|
||||
/**
|
||||
* Internal mounted storages are not exposed to the larger contraption inventory.
|
||||
* They are only for internal use, such as access from a {@link MovementBehaviour}.
|
||||
* Internal storages are still accessible through {@link MovementContext#getItemStorage()}
|
||||
* as well as {@link MountedStorageManager#getAllItemStorages()}.
|
||||
* A storage being internal implies that it does not provide fuel either.
|
||||
* This is only called once on assembly.
|
||||
*/
|
||||
public boolean isInternal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contraptions may search storage for fuel, such as for powering furnace minecarts
|
||||
* and trains. Return false if this storage should
|
||||
*/
|
||||
public boolean providesFuel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a player clicking on this mounted storage. This is always called on the server.
|
||||
* The default implementation will try to open a generic GUI for standard inventories.
|
||||
|
|
|
@ -14,6 +14,8 @@ import com.tterrag.registrate.util.nullness.NonNullUnaryOperator;
|
|||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
@ -28,9 +30,15 @@ public abstract class MountedItemStorageType<T extends MountedItemStorage> {
|
|||
});
|
||||
|
||||
public final MapCodec<? extends T> codec;
|
||||
public final Holder<MountedItemStorageType<?>> holder;
|
||||
|
||||
protected MountedItemStorageType(MapCodec<? extends T> codec) {
|
||||
this.codec = codec;
|
||||
this.holder = CreateBuiltInRegistries.MOUNTED_ITEM_STORAGE_TYPE.createIntrusiveHolder(this);
|
||||
}
|
||||
|
||||
public final boolean is(TagKey<MountedItemStorageType<?>> tag) {
|
||||
return this.holder.is(tag);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
package com.simibubi.create.api.registry;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.simibubi.create.api.behaviour.display.DisplaySource;
|
||||
import com.simibubi.create.api.behaviour.display.DisplayTarget;
|
||||
|
@ -19,10 +15,8 @@ import com.simibubi.create.content.logistics.item.filter.attribute.ItemAttribute
|
|||
import com.simibubi.create.content.logistics.packagePort.PackagePortTargetType;
|
||||
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.RegistrationInfo;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.WritableRegistry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
|
@ -37,8 +31,6 @@ import net.neoforged.neoforge.registries.NewRegistryEvent;
|
|||
*/
|
||||
@EventBusSubscriber(bus = Bus.MOD)
|
||||
public class CreateBuiltInRegistries {
|
||||
private static final WritableRegistry<WritableRegistry<?>> ROOT_REGISTRY = getRootRegistry();
|
||||
|
||||
public static final Set<Registry<?>> REGISTRIES = new HashSet<>();
|
||||
|
||||
public static final Registry<ArmInteractionPointType> ARM_INTERACTION_POINT_TYPE = simple(CreateRegistries.ARM_INTERACTION_POINT_TYPE);
|
||||
|
@ -46,47 +38,24 @@ public class CreateBuiltInRegistries {
|
|||
public static final Registry<ItemAttributeType> ITEM_ATTRIBUTE_TYPE = simple(CreateRegistries.ITEM_ATTRIBUTE_TYPE);
|
||||
public static final Registry<DisplaySource> DISPLAY_SOURCE = simple(CreateRegistries.DISPLAY_SOURCE);
|
||||
public static final Registry<DisplayTarget> DISPLAY_TARGET = simple(CreateRegistries.DISPLAY_TARGET);
|
||||
public static final Registry<MountedItemStorageType<?>> MOUNTED_ITEM_STORAGE_TYPE = simple(CreateRegistries.MOUNTED_ITEM_STORAGE_TYPE);
|
||||
public static final Registry<MountedItemStorageType<?>> MOUNTED_ITEM_STORAGE_TYPE = withIntrusiveHolders(CreateRegistries.MOUNTED_ITEM_STORAGE_TYPE);
|
||||
public static final Registry<MountedFluidStorageType<?>> MOUNTED_FLUID_STORAGE_TYPE = simple(CreateRegistries.MOUNTED_FLUID_STORAGE_TYPE);
|
||||
public static final Registry<ContraptionType> CONTRAPTION_TYPE = withIntrusiveHolders(CreateRegistries.CONTRAPTION_TYPE);
|
||||
public static final Registry<PackagePortTargetType> PACKAGE_PORT_TARGET_TYPE = simple(CreateRegistries.PACKAGE_PORT_TARGET_TYPE);
|
||||
|
||||
private static <T> Registry<T> simple(ResourceKey<Registry<T>> key) {
|
||||
return register(key, new MappedRegistry<>(key, Lifecycle.stable(), false));
|
||||
return register(new MappedRegistry<>(key, Lifecycle.stable(), false));
|
||||
}
|
||||
|
||||
private static <T> Registry<T> withIntrusiveHolders(ResourceKey<Registry<T>> key) {
|
||||
return register(key, new MappedRegistry<>(key, Lifecycle.stable(), true));
|
||||
return register(new MappedRegistry<>(key, Lifecycle.stable(), true));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> Registry<T> register(ResourceKey<Registry<T>> key, WritableRegistry<T> registry) {
|
||||
ROOT_REGISTRY.register(
|
||||
(ResourceKey<WritableRegistry<?>>) (Object) key, registry, new RegistrationInfo(Optional.empty(), Lifecycle.stable())
|
||||
);
|
||||
private static <T> Registry<T> register(WritableRegistry<T> registry) {
|
||||
REGISTRIES.add(registry);
|
||||
return registry;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static WritableRegistry<WritableRegistry<?>> getRootRegistry() {
|
||||
// an accessor can't be used here because BuiltInRegistries is loaded too early during datagen.
|
||||
try {
|
||||
Field field = BuiltInRegistries.class.getDeclaredField("WRITABLE_REGISTRY");
|
||||
field.setAccessible(true);
|
||||
return (WritableRegistry<WritableRegistry<?>>) field.get(null);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new RuntimeException("Create: Failed to get root registry", e);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Remove
|
||||
@ApiStatus.Internal
|
||||
public static void init() {
|
||||
// make sure the class is loaded.
|
||||
// this method is called at the tail of BuiltInRegistries, injected by a coremod. See it for details.
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onNewRegistryEvent(NewRegistryEvent event) {
|
||||
for (Registry<?> registry : REGISTRIES)
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.simibubi.create.api.registry.registrate;
|
|||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -22,21 +23,21 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
|||
import net.minecraft.world.level.material.Fluid;
|
||||
|
||||
public class SimpleBuilder<R, T extends R, P> extends AbstractBuilder<R, T, P, SimpleBuilder<R, T, P>> {
|
||||
private final T value;
|
||||
private final Supplier<T> value;
|
||||
|
||||
private SimpleRegistryAccess<Block, R> byBlock;
|
||||
private SimpleRegistryAccess<BlockEntityType<?>, R> byBlockEntity;
|
||||
private SimpleRegistryAccess<EntityType<?>, R> byEntity;
|
||||
private SimpleRegistryAccess<Fluid, R> byFluid;
|
||||
|
||||
public SimpleBuilder(AbstractRegistrate<?> owner, P parent, String name, BuilderCallback callback, ResourceKey<Registry<R>> registryKey, T value) {
|
||||
public SimpleBuilder(AbstractRegistrate<?> owner, P parent, String name, BuilderCallback callback, ResourceKey<Registry<R>> registryKey, Supplier<T> value) {
|
||||
super(owner, parent, name, callback, registryKey);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T createEntry() {
|
||||
return this.value;
|
||||
return this.value.get();
|
||||
}
|
||||
|
||||
// for setup
|
||||
|
@ -91,49 +92,49 @@ public class SimpleBuilder<R, T extends R, P> extends AbstractBuilder<R, T, P, S
|
|||
|
||||
public SimpleBuilder<R, T, P> associate(Block block) {
|
||||
assertPresent(this.byBlock, "Block");
|
||||
this.byBlock.adder.accept(block, this.value);
|
||||
this.onRegister(value -> this.byBlock.adder.accept(block, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimpleBuilder<R, T, P> associateBlockTag(TagKey<Block> tag) {
|
||||
assertPresent(this.byBlock, "Block");
|
||||
this.byBlock.tagAdder.accept(tag, this.value);
|
||||
this.onRegister(value -> this.byBlock.tagAdder.accept(tag, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimpleBuilder<R, T, P> associate(BlockEntityType<?> type) {
|
||||
assertPresent(this.byBlockEntity, "BlockEntityType");
|
||||
this.byBlockEntity.adder.accept(type, this.value);
|
||||
this.onRegister(value -> this.byBlockEntity.adder.accept(type, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimpleBuilder<R, T, P> associateBeTag(TagKey<BlockEntityType<?>> tag) {
|
||||
assertPresent(this.byBlockEntity, "BlockEntityType");
|
||||
this.byBlockEntity.tagAdder.accept(tag, this.value);
|
||||
this.onRegister(value -> this.byBlockEntity.tagAdder.accept(tag, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimpleBuilder<R, T, P> associate(EntityType<?> type) {
|
||||
assertPresent(this.byEntity, "EntityType");
|
||||
this.byEntity.adder.accept(type, this.value);
|
||||
this.onRegister(value -> this.byEntity.adder.accept(type, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimpleBuilder<R, T, P> associateEntityTag(TagKey<EntityType<?>> tag) {
|
||||
assertPresent(this.byEntity, "EntityType");
|
||||
this.byEntity.tagAdder.accept(tag, this.value);
|
||||
this.onRegister(value -> this.byEntity.tagAdder.accept(tag, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimpleBuilder<R, T, P> associate(Fluid fluid) {
|
||||
assertPresent(this.byFluid, "Fluid");
|
||||
this.byFluid.adder.accept(fluid, this.value);
|
||||
this.onRegister(value -> this.byFluid.adder.accept(fluid, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimpleBuilder<R, T, P> associateFluidTag(TagKey<Fluid> tag) {
|
||||
assertPresent(this.byFluid, "Fluid");
|
||||
this.byFluid.tagAdder.accept(tag, this.value);
|
||||
this.onRegister(value -> this.byFluid.tagAdder.accept(tag, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.simibubi.create.AllTags.AllMountedItemStorageTypeTags;
|
||||
|
||||
import net.createmod.catnip.codecs.CatnipCodecUtils;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -96,16 +98,12 @@ public class MountedStorageManager {
|
|||
|
||||
this.allItemStorages = ImmutableMap.copyOf(this.itemsBuilder);
|
||||
|
||||
this.items = new MountedItemStorageWrapper(subMap(
|
||||
this.allItemStorages, storage -> !storage.isInternal()
|
||||
));
|
||||
this.items = new MountedItemStorageWrapper(subMap(this.allItemStorages, this::isExposed));
|
||||
|
||||
this.allItems = this.items;
|
||||
this.itemsBuilder = null;
|
||||
|
||||
ImmutableMap<BlockPos, MountedItemStorage> fuelMap = subMap(
|
||||
this.allItemStorages, storage -> !storage.isInternal() && storage.providesFuel()
|
||||
);
|
||||
ImmutableMap<BlockPos, MountedItemStorage> fuelMap = subMap(this.allItemStorages, this::canUseForFuel);
|
||||
this.fuelItems = fuelMap.isEmpty() ? null : new MountedItemStorageWrapper(fuelMap);
|
||||
|
||||
ImmutableMap<BlockPos, MountedFluidStorage> fluids = ImmutableMap.copyOf(this.fluidsBuilder);
|
||||
|
@ -118,6 +116,14 @@ public class MountedStorageManager {
|
|||
this.syncedFluidsBuilder = null;
|
||||
}
|
||||
|
||||
private boolean isExposed(MountedItemStorage storage) {
|
||||
return !AllMountedItemStorageTypeTags.INTERNAL.matches(storage);
|
||||
}
|
||||
|
||||
private boolean canUseForFuel(MountedItemStorage storage) {
|
||||
return this.isExposed(storage) && !AllMountedItemStorageTypeTags.FUEL_BLACKLIST.matches(storage);
|
||||
}
|
||||
|
||||
private boolean isInitialized() {
|
||||
return this.itemsBuilder == null;
|
||||
}
|
||||
|
@ -364,10 +370,7 @@ public class MountedStorageManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets a map of all MountedItemStorages in the contraption, irrelevant of them
|
||||
* being internal or providing fuel.
|
||||
* @see MountedItemStorage#isInternal()
|
||||
* @see MountedItemStorage#providesFuel()
|
||||
* Gets a map of all MountedItemStorages in the contraption, irrelevant of them being internal or providing fuel.
|
||||
*/
|
||||
public ImmutableMap<BlockPos, MountedItemStorage> getAllItemStorages() {
|
||||
this.assertInitialized();
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions.behaviour.dispenser;
|
||||
|
||||
public class DispenseItemLocation {
|
||||
private final boolean internal;
|
||||
private final int slot;
|
||||
|
||||
public static final DispenseItemLocation NONE = new DispenseItemLocation(false, -1);
|
||||
|
||||
public DispenseItemLocation(boolean internal, int slot) {
|
||||
this.internal = internal;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
public boolean isInternal() {
|
||||
return internal;
|
||||
}
|
||||
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return slot < 0;
|
||||
}
|
||||
}
|
|
@ -1,114 +1,16 @@
|
|||
package com.simibubi.create.content.contraptions.behaviour.dispenser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.simibubi.create.api.contraption.dispenser.DefaultMountedDispenseBehavior;
|
||||
import com.simibubi.create.api.contraption.dispenser.MountedDispenseBehavior;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.foundation.mixin.accessor.DispenserBlockAccessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.dispenser.BlockSource;
|
||||
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
|
||||
import net.minecraft.core.dispenser.DispenseItemBehavior;
|
||||
import net.minecraft.core.dispenser.ProjectileDispenseBehavior;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
import net.minecraft.world.level.block.entity.DispenserBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class DispenserMovementBehaviour extends DropperMovementBehaviour {
|
||||
private static final Map<Item, IMovedDispenseItemBehaviour> movedDispenseItemBehaviors = new HashMap<>();
|
||||
private static final Set<Item> blacklist = new HashSet<>();
|
||||
|
||||
private static boolean spawnEggsRegistered = false;
|
||||
|
||||
public static void gatherMovedDispenseItemBehaviours() {
|
||||
IMovedDispenseItemBehaviour.init();
|
||||
}
|
||||
|
||||
public static void registerMovedDispenseItemBehaviour(Item item,
|
||||
IMovedDispenseItemBehaviour movedDispenseItemBehaviour) {
|
||||
movedDispenseItemBehaviors.put(item, movedDispenseItemBehaviour);
|
||||
}
|
||||
|
||||
public static DispenseItemBehavior getDispenseMethod(Level level, ItemStack itemstack) {
|
||||
return ((DispenserBlockAccessor) Blocks.DISPENSER).create$callGetDispenseMethod(level, itemstack);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IMovedDispenseItemBehaviour getDispenseBehavior(MovementContext context, BlockPos pos, ItemStack stack) {
|
||||
if (!spawnEggsRegistered) {
|
||||
spawnEggsRegistered = true;
|
||||
IMovedDispenseItemBehaviour.initSpawnEggs();
|
||||
}
|
||||
|
||||
Item item = stack.getItem();
|
||||
// return registered/cached behavior if present
|
||||
if (movedDispenseItemBehaviors.containsKey(item)) {
|
||||
return movedDispenseItemBehaviors.get(item);
|
||||
}
|
||||
|
||||
// if there isn't one, try to create one from a vanilla behavior
|
||||
if (blacklist.contains(item)) {
|
||||
// unless it's been blacklisted, which means a behavior was created already and errored
|
||||
return MovedDefaultDispenseItemBehaviour.INSTANCE;
|
||||
}
|
||||
|
||||
DispenseItemBehavior behavior = getDispenseMethod(context.world, stack);
|
||||
// no behavior or default, use the moved default
|
||||
if (behavior == null || behavior.getClass() == DefaultDispenseItemBehavior.class)
|
||||
return MovedDefaultDispenseItemBehaviour.INSTANCE;
|
||||
|
||||
// projectile-specific behaviors are pretty straightforward to convert
|
||||
if (behavior instanceof ProjectileDispenseBehavior projectile) {
|
||||
IMovedDispenseItemBehaviour movedBehaviour = MovedProjectileDispenserBehaviour.of(projectile);
|
||||
// cache it for later
|
||||
registerMovedDispenseItemBehaviour(item, movedBehaviour);
|
||||
return movedBehaviour;
|
||||
}
|
||||
|
||||
MinecraftServer server = context.world.getServer();
|
||||
ServerLevel serverLevel = server != null ? server.getLevel(context.world.dimension()) : null;
|
||||
|
||||
DispenserBlockEntity blockEntity = null;
|
||||
if (context.world.getBlockEntity(pos) instanceof DispenserBlockEntity dispenserBlockEntity)
|
||||
blockEntity = dispenserBlockEntity;
|
||||
|
||||
// other behaviors are more convoluted due to BlockSource providing a BlockEntity.
|
||||
Vec3 normal = getRotatedFacingNormal(context);
|
||||
Direction nearestFacing = Direction.getNearest(normal.x, normal.y, normal.z);
|
||||
BlockSource source = new BlockSource(serverLevel, pos, context.state, blockEntity);
|
||||
IMovedDispenseItemBehaviour movedBehavior = new FallbackMovedDispenseBehavior(item, behavior, source);
|
||||
registerMovedDispenseItemBehaviour(item, movedBehavior);
|
||||
return movedBehavior;
|
||||
}
|
||||
|
||||
private static Vec3 getRotatedFacingNormal(MovementContext ctx) {
|
||||
Direction facing = ctx.state.getValue(DispenserBlock.FACING);
|
||||
Vec3 normal = Vec3.atLowerCornerOf(facing.getNormal());
|
||||
return ctx.rotation.apply(normal);
|
||||
}
|
||||
|
||||
private record FallbackMovedDispenseBehavior(Item item, DispenseItemBehavior wrapped, BlockSource source) implements IMovedDispenseItemBehaviour {
|
||||
@Override
|
||||
public ItemStack dispense(ItemStack stack, MovementContext context, BlockPos pos) {
|
||||
ItemStack backup = stack.copy();
|
||||
try {
|
||||
return this.wrapped.dispense(this.source, stack);
|
||||
} catch (NullPointerException ignored) {
|
||||
// error due to lack of a BlockEntity. Un-register self to avoid continuing to fail
|
||||
movedDispenseItemBehaviors.remove(this.item);
|
||||
blacklist.add(this.item);
|
||||
return backup;
|
||||
}
|
||||
}
|
||||
protected MountedDispenseBehavior getDispenseBehavior(MovementContext context, BlockPos pos, ItemStack stack) {
|
||||
MountedDispenseBehavior behavior = MountedDispenseBehavior.REGISTRY.get(stack.getItem());
|
||||
return behavior != null ? behavior : DefaultMountedDispenseBehavior.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import java.util.function.Predicate;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.simibubi.create.api.behaviour.movement.MovementBehaviour;
|
||||
import com.simibubi.create.api.contraption.dispenser.DefaultMountedDispenseBehavior;
|
||||
import com.simibubi.create.api.contraption.dispenser.MountedDispenseBehavior;
|
||||
import com.simibubi.create.api.contraption.storage.item.MountedItemStorage;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.foundation.item.ItemHelper;
|
||||
|
@ -38,13 +40,13 @@ public class DropperMovementBehaviour implements MovementBehaviour {
|
|||
|
||||
// copy because dispense behaviors will modify it directly
|
||||
ItemStack stack = storage.getStackInSlot(slot).copy();
|
||||
IMovedDispenseItemBehaviour behavior = getDispenseBehavior(context, pos, stack);
|
||||
MountedDispenseBehavior behavior = getDispenseBehavior(context, pos, stack);
|
||||
ItemStack remainder = behavior.dispense(stack, context, pos);
|
||||
storage.setStackInSlot(slot, remainder);
|
||||
}
|
||||
|
||||
protected IMovedDispenseItemBehaviour getDispenseBehavior(MovementContext context, BlockPos pos, ItemStack stack) {
|
||||
return MovedDefaultDispenseItemBehaviour.INSTANCE;
|
||||
protected MountedDispenseBehavior getDispenseBehavior(MovementContext context, BlockPos pos, ItemStack stack) {
|
||||
return DefaultMountedDispenseBehavior.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,8 +61,8 @@ public class DropperMovementBehaviour implements MovementBehaviour {
|
|||
|
||||
if (stack.getCount() == 1 && stack.getMaxStackSize() != 1) {
|
||||
stack = tryTopOff(stack, contraptionInventory);
|
||||
if (stack == null) {
|
||||
continue;
|
||||
if (stack != null) {
|
||||
storage.setStackInSlot(i, stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions.behaviour.dispenser;
|
||||
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.item.PrimedTnt;
|
||||
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.entity.projectile.SmallFireball;
|
||||
import net.minecraft.world.entity.projectile.ThrownPotion;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.SpawnEggItem;
|
||||
import net.minecraft.world.item.alchemy.PotionContents;
|
||||
import net.minecraft.world.item.alchemy.Potions;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.BeehiveBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.BucketPickup;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public interface IMovedDispenseItemBehaviour {
|
||||
|
||||
static void initSpawnEggs() {
|
||||
final IMovedDispenseItemBehaviour spawnEggDispenseBehaviour = new MovedDefaultDispenseItemBehaviour() {
|
||||
@Override
|
||||
protected ItemStack dispenseStack(ItemStack itemStack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
if (!(itemStack.getItem() instanceof SpawnEggItem))
|
||||
return super.dispenseStack(itemStack, context, pos, facing);
|
||||
if (context.world instanceof ServerLevel) {
|
||||
EntityType<?> entityType = ((SpawnEggItem) itemStack.getItem()).getType(itemStack);
|
||||
Entity spawnedEntity = entityType.spawn((ServerLevel) context.world, itemStack, null,
|
||||
pos.offset(BlockPos.containing(facing.x + .7, facing.y + .7, facing.z + .7)), MobSpawnType.DISPENSER, facing.y < .5,
|
||||
false);
|
||||
if (spawnedEntity != null)
|
||||
spawnedEntity.setDeltaMovement(context.motion.scale(2));
|
||||
}
|
||||
itemStack.shrink(1);
|
||||
return itemStack;
|
||||
}
|
||||
};
|
||||
|
||||
for (SpawnEggItem spawneggitem : SpawnEggItem.eggs())
|
||||
DispenserMovementBehaviour.registerMovedDispenseItemBehaviour(spawneggitem, spawnEggDispenseBehaviour);
|
||||
}
|
||||
|
||||
static void init() {
|
||||
MovedProjectileDispenserBehaviour movedPotionDispenseItemBehaviour = new MovedProjectileDispenserBehaviour() {
|
||||
@Override
|
||||
protected Projectile getProjectileEntity(Level world, double x, double y, double z, ItemStack itemStack, Direction facing) {
|
||||
return Util.make(new ThrownPotion(world, x, y, z), (thrownPotion) -> thrownPotion.setItem(itemStack));
|
||||
}
|
||||
|
||||
protected float getProjectileInaccuracy() {
|
||||
return super.getProjectileInaccuracy() * 0.5F;
|
||||
}
|
||||
|
||||
protected float getProjectileVelocity() {
|
||||
return super.getProjectileVelocity() * .5F;
|
||||
}
|
||||
};
|
||||
|
||||
DispenserMovementBehaviour.registerMovedDispenseItemBehaviour(Items.SPLASH_POTION,
|
||||
movedPotionDispenseItemBehaviour);
|
||||
DispenserMovementBehaviour.registerMovedDispenseItemBehaviour(Items.LINGERING_POTION,
|
||||
movedPotionDispenseItemBehaviour);
|
||||
|
||||
DispenserMovementBehaviour.registerMovedDispenseItemBehaviour(Items.TNT,
|
||||
new MovedDefaultDispenseItemBehaviour() {
|
||||
@Override
|
||||
protected ItemStack dispenseStack(ItemStack itemStack, MovementContext context, BlockPos pos,
|
||||
Vec3 facing) {
|
||||
double x = pos.getX() + facing.x * .7 + .5;
|
||||
double y = pos.getY() + facing.y * .7 + .5;
|
||||
double z = pos.getZ() + facing.z * .7 + .5;
|
||||
PrimedTnt tntentity = new PrimedTnt(context.world, x, y, z, null);
|
||||
tntentity.push(context.motion.x, context.motion.y, context.motion.z);
|
||||
context.world.addFreshEntity(tntentity);
|
||||
context.world.playSound(null, tntentity.getX(), tntentity.getY(), tntentity.getZ(),
|
||||
SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
itemStack.shrink(1);
|
||||
return itemStack;
|
||||
}
|
||||
});
|
||||
|
||||
DispenserMovementBehaviour.registerMovedDispenseItemBehaviour(Items.FIREWORK_ROCKET,
|
||||
new MovedDefaultDispenseItemBehaviour() {
|
||||
@Override
|
||||
protected ItemStack dispenseStack(ItemStack itemStack, MovementContext context, BlockPos pos,
|
||||
Vec3 facing) {
|
||||
double x = pos.getX() + facing.x * .7 + .5;
|
||||
double y = pos.getY() + facing.y * .7 + .5;
|
||||
double z = pos.getZ() + facing.z * .7 + .5;
|
||||
FireworkRocketEntity fireworkrocketentity =
|
||||
new FireworkRocketEntity(context.world, itemStack, x, y, z, true);
|
||||
fireworkrocketentity.shoot(facing.x, facing.y, facing.z, 0.5F, 1.0F);
|
||||
context.world.addFreshEntity(fireworkrocketentity);
|
||||
itemStack.shrink(1);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playDispenseSound(LevelAccessor world, BlockPos pos) {
|
||||
world.levelEvent(1004, pos, 0);
|
||||
}
|
||||
});
|
||||
|
||||
DispenserMovementBehaviour.registerMovedDispenseItemBehaviour(Items.FIRE_CHARGE,
|
||||
new MovedDefaultDispenseItemBehaviour() {
|
||||
@Override
|
||||
protected void playDispenseSound(LevelAccessor world, BlockPos pos) {
|
||||
world.levelEvent(1018, pos, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemStack dispenseStack(ItemStack itemStack, MovementContext context, BlockPos pos,
|
||||
Vec3 facing) {
|
||||
RandomSource random = context.world.random;
|
||||
double x = pos.getX() + facing.x * .7 + .5;
|
||||
double y = pos.getY() + facing.y * .7 + .5;
|
||||
double z = pos.getZ() + facing.z * .7 + .5;
|
||||
context.world.addFreshEntity(Util.make(
|
||||
new SmallFireball(context.world, x, y, z,
|
||||
new Vec3(random.nextGaussian() * 0.05D + facing.x + context.motion.x,
|
||||
random.nextGaussian() * 0.05D + facing.y + context.motion.y,
|
||||
random.nextGaussian() * 0.05D + facing.z + context.motion.z).normalize()),
|
||||
(smallFireball) -> smallFireball.setItem(itemStack)));
|
||||
itemStack.shrink(1);
|
||||
return itemStack;
|
||||
}
|
||||
});
|
||||
|
||||
DispenserMovementBehaviour.registerMovedDispenseItemBehaviour(Items.GLASS_BOTTLE,
|
||||
new MovedOptionalDispenseBehaviour() {
|
||||
@Override
|
||||
protected ItemStack dispenseStack(ItemStack itemStack, MovementContext context, BlockPos pos,
|
||||
Vec3 facing) {
|
||||
this.successful = false;
|
||||
BlockPos interactAt = pos.relative(getClosestFacingDirection(facing));
|
||||
BlockState state = context.world.getBlockState(interactAt);
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (state.is(BlockTags.BEEHIVES) && state.getValue(BeehiveBlock.HONEY_LEVEL) >= 5) {
|
||||
((BeehiveBlock) block).releaseBeesAndResetHoneyLevel(context.world, state, interactAt, null,
|
||||
BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED);
|
||||
this.successful = true;
|
||||
return placeItemInInventory(itemStack, new ItemStack(Items.HONEY_BOTTLE), context, pos, facing);
|
||||
} else if (context.world.getFluidState(interactAt)
|
||||
.is(FluidTags.WATER)) {
|
||||
this.successful = true;
|
||||
return placeItemInInventory(itemStack,
|
||||
PotionContents.createItemStack(Items.POTION, Potions.WATER), context, pos, facing);
|
||||
} else {
|
||||
return super.dispenseStack(itemStack, context, pos, facing);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
DispenserMovementBehaviour.registerMovedDispenseItemBehaviour(Items.BUCKET,
|
||||
new MovedDefaultDispenseItemBehaviour() {
|
||||
@Override
|
||||
protected ItemStack dispenseStack(ItemStack itemStack, MovementContext context, BlockPos pos,
|
||||
Vec3 facing) {
|
||||
BlockPos interactAt = pos.relative(getClosestFacingDirection(facing));
|
||||
BlockState state = context.world.getBlockState(interactAt);
|
||||
Block block = state.getBlock();
|
||||
if (block instanceof BucketPickup) {
|
||||
ItemStack bucket = ((BucketPickup) block).pickupBlock(null, context.world, interactAt, state);
|
||||
return placeItemInInventory(itemStack, bucket, context, pos, facing);
|
||||
}
|
||||
return super.dispenseStack(itemStack, context, pos, facing);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ItemStack dispense(ItemStack itemStack, MovementContext context, BlockPos pos);
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions.behaviour.dispenser;
|
||||
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
import net.minecraft.world.level.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import net.neoforged.neoforge.items.ItemHandlerHelper;
|
||||
import net.neoforged.neoforge.items.wrapper.CombinedInvWrapper;
|
||||
|
||||
public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBehaviour {
|
||||
public static final MovedDefaultDispenseItemBehaviour INSTANCE = new MovedDefaultDispenseItemBehaviour();
|
||||
|
||||
public static void doDispense(Level p_82486_0_, ItemStack p_82486_1_, int p_82486_2_, Vec3 facing,
|
||||
BlockPos p_82486_4_, MovementContext context) {
|
||||
double d0 = p_82486_4_.getX() + facing.x + .5;
|
||||
double d1 = p_82486_4_.getY() + facing.y + .5;
|
||||
double d2 = p_82486_4_.getZ() + facing.z + .5;
|
||||
if (Direction.getNearest(facing.x, facing.y, facing.z)
|
||||
.getAxis() == Direction.Axis.Y) {
|
||||
d1 = d1 - 0.125D;
|
||||
} else {
|
||||
d1 = d1 - 0.15625D;
|
||||
}
|
||||
|
||||
ItemEntity itementity = new ItemEntity(p_82486_0_, d0, d1, d2, p_82486_1_);
|
||||
double d3 = p_82486_0_.random.nextDouble() * 0.1D + 0.2D;
|
||||
itementity.setDeltaMovement(
|
||||
p_82486_0_.random.nextGaussian() * (double) 0.0075F * (double) p_82486_2_ + facing.x() * d3
|
||||
+ context.motion.x,
|
||||
p_82486_0_.random.nextGaussian() * (double) 0.0075F * (double) p_82486_2_ + facing.y() * d3
|
||||
+ context.motion.y,
|
||||
p_82486_0_.random.nextGaussian() * (double) 0.0075F * (double) p_82486_2_ + facing.z() * d3
|
||||
+ context.motion.z);
|
||||
p_82486_0_.addFreshEntity(itementity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack dispense(ItemStack itemStack, MovementContext context, BlockPos pos) {
|
||||
Vec3 facingVec = Vec3.atLowerCornerOf(context.state.getValue(DispenserBlock.FACING)
|
||||
.getNormal());
|
||||
facingVec = context.rotation.apply(facingVec);
|
||||
facingVec.normalize();
|
||||
|
||||
Direction closestToFacing = getClosestFacingDirection(facingVec);
|
||||
Container inventory = HopperBlockEntity.getContainerAt(context.world, pos.relative(closestToFacing));
|
||||
if (inventory == null) {
|
||||
this.playDispenseSound(context.world, pos);
|
||||
this.spawnDispenseParticles(context.world, pos, closestToFacing);
|
||||
return this.dispenseStack(itemStack, context, pos, facingVec);
|
||||
} else {
|
||||
if (HopperBlockEntity.addItem(null, inventory, itemStack.copy()
|
||||
.split(1), closestToFacing.getOpposite())
|
||||
.isEmpty())
|
||||
itemStack.shrink(1);
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispense the specified stack, play the dispense sound and spawn particles.
|
||||
*/
|
||||
protected ItemStack dispenseStack(ItemStack itemStack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
ItemStack itemstack = itemStack.split(1);
|
||||
doDispense(context.world, itemstack, 6, facing, pos, context);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the dispense sound from the specified block.
|
||||
*/
|
||||
protected void playDispenseSound(LevelAccessor world, BlockPos pos) {
|
||||
world.levelEvent(1000, pos, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Order clients to display dispense particles from the specified block and
|
||||
* facing.
|
||||
*/
|
||||
protected void spawnDispenseParticles(LevelAccessor world, BlockPos pos, Vec3 facing) {
|
||||
spawnDispenseParticles(world, pos, getClosestFacingDirection(facing));
|
||||
}
|
||||
|
||||
protected void spawnDispenseParticles(LevelAccessor world, BlockPos pos, Direction direction) {
|
||||
world.levelEvent(2000, pos, direction.get3DDataValue());
|
||||
}
|
||||
|
||||
protected Direction getClosestFacingDirection(Vec3 exactFacing) {
|
||||
return Direction.getNearest(exactFacing.x, exactFacing.y, exactFacing.z);
|
||||
}
|
||||
|
||||
protected ItemStack placeItemInInventory(ItemStack consumedFrom, ItemStack output, MovementContext context,
|
||||
BlockPos pos, Vec3 facing) {
|
||||
consumedFrom.shrink(1);
|
||||
|
||||
ItemStack toInsert = output.copy();
|
||||
// try inserting into own inventory first
|
||||
ItemStack remainder = ItemHandlerHelper.insertItem(context.getItemStorage(), toInsert, false);
|
||||
if (!remainder.isEmpty()) {
|
||||
// next, try the whole contraption inventory
|
||||
// note that this contains the dispenser inventory. That's fine.
|
||||
CombinedInvWrapper contraption = context.contraption.getStorage().getAllItems();
|
||||
ItemStack newRemainder = ItemHandlerHelper.insertItem(contraption, remainder, false);
|
||||
if (!newRemainder.isEmpty()) {
|
||||
// if there's *still* something left, dispense into world
|
||||
INSTANCE.dispenseStack(remainder, context, pos, facing);
|
||||
}
|
||||
}
|
||||
|
||||
return consumedFrom;
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions.behaviour.dispenser;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
public class MovedOptionalDispenseBehaviour extends MovedDefaultDispenseItemBehaviour {
|
||||
protected boolean successful = true;
|
||||
|
||||
@Override
|
||||
protected void playDispenseSound(LevelAccessor world, BlockPos pos) {
|
||||
world.levelEvent(this.successful ? 1000 : 1001, pos, 0);
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions.behaviour.dispenser;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.foundation.mixin.accessor.ProjectileDispenseBehaviorAccessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.dispenser.ProjectileDispenseBehavior;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public abstract class MovedProjectileDispenserBehaviour extends MovedDefaultDispenseItemBehaviour {
|
||||
|
||||
@Override
|
||||
protected ItemStack dispenseStack(ItemStack itemStack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
double x = pos.getX() + facing.x * .7 + .5;
|
||||
double y = pos.getY() + facing.y * .7 + .5;
|
||||
double z = pos.getZ() + facing.z * .7 + .5;
|
||||
Projectile projectile = this.getProjectileEntity(context.world, x, y, z, itemStack.copy(), getClosestFacingDirection(facing));
|
||||
if (projectile == null)
|
||||
return itemStack;
|
||||
Vec3 effectiveMovementVec = facing.scale(getProjectileVelocity()).add(context.motion);
|
||||
projectile.shoot(effectiveMovementVec.x, effectiveMovementVec.y, effectiveMovementVec.z, (float) effectiveMovementVec.length(), this.getProjectileInaccuracy());
|
||||
context.world.addFreshEntity(projectile);
|
||||
itemStack.shrink(1);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playDispenseSound(LevelAccessor world, BlockPos pos) {
|
||||
world.levelEvent(1002, pos, 0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract Projectile getProjectileEntity(Level world, double x, double y, double z, ItemStack itemStack, Direction facing);
|
||||
|
||||
protected float getProjectileInaccuracy() {
|
||||
return 6.0F;
|
||||
}
|
||||
|
||||
protected float getProjectileVelocity() {
|
||||
return 1.1F;
|
||||
}
|
||||
|
||||
public static MovedProjectileDispenserBehaviour of(ProjectileDispenseBehavior vanillaBehaviour) {
|
||||
ProjectileDispenseBehaviorAccessor accessor = (ProjectileDispenseBehaviorAccessor) vanillaBehaviour;
|
||||
return new MovedProjectileDispenserBehaviour() {
|
||||
@Override
|
||||
protected Projectile getProjectileEntity(Level world, double x, double y, double z, ItemStack itemStack, Direction facing) {
|
||||
return accessor.create$getProjectileItem().asProjectile(world, new SimplePos(x, y, z), itemStack, facing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getProjectileInaccuracy() {
|
||||
return accessor.create$getDispenseConfig().uncertainty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getProjectileVelocity() {
|
||||
return accessor.create$getDispenseConfig().power();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions.behaviour.dispenser;
|
||||
|
||||
import net.minecraft.core.Position;
|
||||
|
||||
public class SimplePos implements Position {
|
||||
private final double x;
|
||||
private final double y;
|
||||
private final double z;
|
||||
|
||||
public SimplePos(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double x() {
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double y() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double z() {
|
||||
return z;
|
||||
}
|
||||
}
|
|
@ -31,11 +31,6 @@ public class DispenserMountedStorage extends SimpleMountedStorage {
|
|||
this(AllMountedStorageTypes.DISPENSER.get(), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected MenuProvider createMenuProvider(Component name, IItemHandlerModifiable handler,
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.lang.invoke.VarHandle;
|
|||
import java.util.Map;
|
||||
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.foundation.mixin.accessor.ItemModelGeneratorsAccessor;
|
||||
import com.tterrag.registrate.providers.DataGenContext;
|
||||
import com.tterrag.registrate.providers.RegistrateItemModelProvider;
|
||||
|
||||
|
@ -33,25 +34,25 @@ public class TrimmableArmorModelGenerator {
|
|||
public static <T extends ArmorItem> void generate(DataGenContext<Item, T> c, RegistrateItemModelProvider p) {
|
||||
T item = c.get();
|
||||
ItemModelBuilder builder = p.generated(c);
|
||||
for (ItemModelGenerators.TrimModelData data : ItemModelGenerators.GENERATED_TRIM_MODELS) {
|
||||
for (ItemModelGenerators.TrimModelData data : ItemModelGeneratorsAccessor.create$getGENERATED_TRIM_MODELS()) {
|
||||
ResourceLocation modelLoc = ModelLocationUtils.getModelLocation(item);
|
||||
ResourceLocation textureLoc = TextureMapping.getItemTexture(item);
|
||||
String trimId = data.name(item.getMaterial());
|
||||
ResourceLocation trimModelLoc = modelLoc.withSuffix("_" + trimId + "_trim");
|
||||
ResourceLocation trimLoc =
|
||||
ResourceLocation.withDefaultNamespace("trims/items/" + item.getType().getName() + "_trim_" + trimId);
|
||||
ResourceLocation.withDefaultNamespace("trims/items/" + item.getType().getName() + "_trim_" + trimId);
|
||||
String parent = "item/generated";
|
||||
if (item.getMaterial() == AllArmorMaterials.CARDBOARD) {
|
||||
trimLoc = Create.asResource("trims/items/card_" + item.getType().getName() + "_trim_" + trimId);
|
||||
}
|
||||
ItemModelBuilder itemModel = p.withExistingParent(trimModelLoc.getPath(), parent)
|
||||
.texture("layer0", textureLoc);
|
||||
.texture("layer0", textureLoc);
|
||||
Map<String, String> textures = (Map<String, String>) TEXTURES_HANDLE.get(itemModel);
|
||||
textures.put("layer1", trimLoc.toString());
|
||||
builder.override()
|
||||
.predicate(ItemModelGenerators.TRIM_TYPE_PREDICATE_ID, data.itemModelIndex())
|
||||
.model(itemModel)
|
||||
.end();
|
||||
.predicate(ItemModelGenerators.TRIM_TYPE_PREDICATE_ID, data.itemModelIndex())
|
||||
.model(itemModel)
|
||||
.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,11 +46,6 @@ public class ItemVaultMountedStorage extends WrapperMountedItemStorage<ItemStack
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean providesFuel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ItemVaultMountedStorage fromVault(ItemVaultBlockEntity vault) {
|
||||
// Vault inventories have a world-affecting onContentsChanged, copy to a safe one
|
||||
return new ItemVaultMountedStorage(copyToItemStackHandler(vault.getInventoryOfBlock()));
|
||||
|
|
|
@ -156,25 +156,25 @@ public class CreateRegistrate extends AbstractRegistrate<CreateRegistrate> {
|
|||
|
||||
public <T extends MountedItemStorageType<?>> SimpleBuilder<MountedItemStorageType<?>, T, CreateRegistrate> mountedItemStorage(String name, Supplier<T> supplier) {
|
||||
return this.entry(name, callback -> new SimpleBuilder<>(
|
||||
this, this, name, callback, CreateRegistries.MOUNTED_ITEM_STORAGE_TYPE, supplier.get()
|
||||
this, this, name, callback, CreateRegistries.MOUNTED_ITEM_STORAGE_TYPE, supplier
|
||||
).byBlock(MountedItemStorageType.REGISTRY));
|
||||
}
|
||||
|
||||
public <T extends MountedFluidStorageType<?>> SimpleBuilder<MountedFluidStorageType<?>, T, CreateRegistrate> mountedFluidStorage(String name, Supplier<T> supplier) {
|
||||
return this.entry(name, callback -> new SimpleBuilder<>(
|
||||
this, this, name, callback, CreateRegistries.MOUNTED_FLUID_STORAGE_TYPE, supplier.get()
|
||||
this, this, name, callback, CreateRegistries.MOUNTED_FLUID_STORAGE_TYPE, supplier
|
||||
).byBlock(MountedFluidStorageType.REGISTRY));
|
||||
}
|
||||
|
||||
public <T extends DisplaySource> SimpleBuilder<DisplaySource, T, CreateRegistrate> displaySource(String name, Supplier<T> supplier) {
|
||||
return this.entry(name, callback -> new SimpleBuilder<>(
|
||||
this, this, name, callback, CreateRegistries.DISPLAY_SOURCE, supplier.get()
|
||||
this, this, name, callback, CreateRegistries.DISPLAY_SOURCE, supplier
|
||||
).byBlock(DisplaySource.BY_BLOCK).byBlockEntity(DisplaySource.BY_BLOCK_ENTITY));
|
||||
}
|
||||
|
||||
public <T extends DisplayTarget> SimpleBuilder<DisplayTarget, T, CreateRegistrate> displayTarget(String name, Supplier<T> supplier) {
|
||||
return this.entry(name, callback -> new SimpleBuilder<>(
|
||||
this, this, name, callback, CreateRegistries.DISPLAY_TARGET, supplier.get()
|
||||
this, this, name, callback, CreateRegistries.DISPLAY_TARGET, supplier
|
||||
).byBlock(DisplayTarget.BY_BLOCK).byBlockEntity(DisplayTarget.BY_BLOCK_ENTITY));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package com.simibubi.create.foundation.mixin.accessor;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.core.WritableRegistry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
|
||||
@Mixin(BuiltInRegistries.class)
|
||||
public interface BuiltInRegistriesAccessor {
|
||||
@Accessor("WRITABLE_REGISTRY")
|
||||
static WritableRegistry<WritableRegistry<?>> create$getWRITABLE_REGISTRY() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.simibubi.create.foundation.mixin.accessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.data.models.ItemModelGenerators;
|
||||
import net.minecraft.data.models.ItemModelGenerators.TrimModelData;
|
||||
|
||||
@Mixin(ItemModelGenerators.class)
|
||||
public interface ItemModelGeneratorsAccessor {
|
||||
@Accessor("GENERATED_TRIM_MODELS")
|
||||
static List<TrimModelData> create$getGENERATED_TRIM_MODELS() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package com.simibubi.create.impl.contraption.dispenser;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.simibubi.create.AllTags.AllItemTags;
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.api.contraption.dispenser.DefaultMountedDispenseBehavior;
|
||||
import com.simibubi.create.api.contraption.dispenser.MountedDispenseBehavior;
|
||||
import com.simibubi.create.api.contraption.dispenser.MountedProjectileDispenseBehavior;
|
||||
import com.simibubi.create.api.registry.SimpleRegistry;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.foundation.mixin.accessor.DispenserBlockAccessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.dispenser.BlockSource;
|
||||
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
|
||||
import net.minecraft.core.dispenser.DispenseItemBehavior;
|
||||
import net.minecraft.core.dispenser.ProjectileDispenseBehavior;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
import net.neoforged.neoforge.event.TagsUpdatedEvent;
|
||||
import net.neoforged.neoforge.server.ServerLifecycleHooks;
|
||||
|
||||
public enum DispenserBehaviorConverter implements SimpleRegistry.Provider<Item, MountedDispenseBehavior> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MountedDispenseBehavior get(Item item) {
|
||||
DispenseItemBehavior vanilla = getDispenseMethod(new ItemStack(item));
|
||||
if (vanilla == null)
|
||||
return null;
|
||||
|
||||
// when the default, return null. The default will be used anyway, avoid caching it for no reason.
|
||||
if (vanilla.getClass() == DefaultDispenseItemBehavior.class)
|
||||
return null;
|
||||
|
||||
// if the item is explicitly blocked from having its behavior wrapped, ignore it
|
||||
if (AllItemTags.DISPENSE_BEHAVIOR_WRAP_BLACKLIST.matches(item))
|
||||
return null;
|
||||
|
||||
if (vanilla instanceof ProjectileDispenseBehavior projectile) {
|
||||
return MountedProjectileDispenseBehavior.of(projectile);
|
||||
}
|
||||
|
||||
// other behaviors are more dangerous due to BlockSource providing a BlockEntity, which contraptions can't do.
|
||||
// wrap in a fallback that will watch for errors.
|
||||
return new FallbackBehavior(item, vanilla);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegister(Runnable invalidate) {
|
||||
// invalidate if the blacklist tag might've changed
|
||||
NeoForge.EVENT_BUS.addListener((TagsUpdatedEvent event) -> {
|
||||
if (event.shouldUpdateStaticData()) {
|
||||
invalidate.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static DispenseItemBehavior getDispenseMethod(ItemStack stack) {
|
||||
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||
if (server == null)
|
||||
return null;
|
||||
|
||||
return ((DispenserBlockAccessor) Blocks.DISPENSER).create$callGetDispenseMethod(server.getLevel(Level.OVERWORLD), stack);
|
||||
}
|
||||
|
||||
private static final class FallbackBehavior extends DefaultMountedDispenseBehavior {
|
||||
private final Item item;
|
||||
private final DispenseItemBehavior wrapped;
|
||||
private boolean hasErrored;
|
||||
|
||||
private FallbackBehavior(Item item, DispenseItemBehavior wrapped) {
|
||||
this.item = item;
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemStack execute(ItemStack stack, MovementContext context, BlockPos pos, Vec3 facing) {
|
||||
if (this.hasErrored)
|
||||
return stack;
|
||||
|
||||
MinecraftServer server = context.world.getServer();
|
||||
ServerLevel serverLevel = server != null ? server.getLevel(context.world.dimension()) : null;
|
||||
|
||||
Direction nearestFacing = MountedDispenseBehavior.getClosestFacingDirection(facing);
|
||||
BlockState state = context.state;
|
||||
if (state.hasProperty(BlockStateProperties.FACING))
|
||||
state = state.setValue(BlockStateProperties.FACING, nearestFacing);
|
||||
|
||||
BlockSource source = new BlockSource(serverLevel, pos, state, null);
|
||||
|
||||
try {
|
||||
// use a copy in case of implosion after modifying it
|
||||
return this.wrapped.dispense(source, stack.copy());
|
||||
} catch (NullPointerException e) {
|
||||
// likely due to the lack of a BlockEntity
|
||||
ResourceLocation itemId = BuiltInRegistries.ITEM.getKey(this.item);
|
||||
String message = "Error dispensing item '" + itemId + "' from contraption, not doing that anymore";
|
||||
Create.LOGGER.error(message, e);
|
||||
this.hasErrored = true;
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ public class CreateDatagen {
|
|||
|
||||
generator.addProvider(event.includeServer(), new CreateRecipeSerializerTagsProvider(output, lookupProvider, existingFileHelper));
|
||||
generator.addProvider(event.includeServer(), new CreateContraptionTypeTagsProvider(output, lookupProvider, existingFileHelper));
|
||||
generator.addProvider(event.includeServer(), new CreateMountedItemStorageTypeTagsProvider(output, lookupProvider, existingFileHelper));
|
||||
generator.addProvider(event.includeServer(), new DamageTypeTagGen(output, lookupProvider, existingFileHelper));
|
||||
generator.addProvider(event.includeServer(), new AllAdvancements(output, lookupProvider));
|
||||
generator.addProvider(event.includeServer(), new StandardRecipeGen(output, lookupProvider));
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package com.simibubi.create.infrastructure.data;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.simibubi.create.AllMountedStorageTypes;
|
||||
import com.simibubi.create.AllTags.AllMountedItemStorageTypeTags;
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
|
||||
import com.simibubi.create.api.registry.CreateRegistries;
|
||||
|
||||
import net.minecraft.core.HolderLookup.Provider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.tags.TagsProvider;
|
||||
import net.minecraft.tags.TagEntry;
|
||||
|
||||
import net.neoforged.neoforge.common.data.ExistingFileHelper;
|
||||
|
||||
public class CreateMountedItemStorageTypeTagsProvider extends TagsProvider<MountedItemStorageType<?>> {
|
||||
public CreateMountedItemStorageTypeTagsProvider(PackOutput output, CompletableFuture<Provider> lookupProvider, @Nullable ExistingFileHelper existingFileHelper) {
|
||||
super(output, CreateRegistries.MOUNTED_ITEM_STORAGE_TYPE, lookupProvider, Create.ID, existingFileHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addTags(Provider pProvider) {
|
||||
tag(AllMountedItemStorageTypeTags.INTERNAL.tag).add(
|
||||
TagEntry.element(AllMountedStorageTypes.DISPENSER.getId())
|
||||
);
|
||||
tag(AllMountedItemStorageTypeTags.FUEL_BLACKLIST.tag).add(
|
||||
TagEntry.element(AllMountedStorageTypes.VAULT.getId())
|
||||
);
|
||||
|
||||
// VALIDATE
|
||||
|
||||
for (AllMountedItemStorageTypeTags tag : AllMountedItemStorageTypeTags.values()) {
|
||||
if (tag.alwaysDatagen) {
|
||||
getOrCreateRawBuilder(tag.tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Create's Mounted Item Storage Type Tags";
|
||||
}
|
||||
}
|
|
@ -53,6 +53,3 @@ protected-f net.minecraft.world.entity.decoration.HangingEntity recalculateBound
|
|||
public net.minecraft.world.item.alchemy.PotionBrewing$Mix
|
||||
|
||||
public net.minecraft.data.models.ItemModelGenerators$TrimModelData
|
||||
|
||||
# Using a static accessor here doesn't work, no idea why but will have to debug it later
|
||||
public net.minecraft.data.models.ItemModelGenerators f_265952_ # GENERATED_TRIM_MODELS
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"better_registry": "coremods/better_registry.js"
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
var ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI')
|
||||
|
||||
// this is terrible, but Forge has forced our hands.
|
||||
// for some reason, Forge loads half the game with Bootstrap.bootStrap *before* loading mods during datagen.
|
||||
// this is not the case in other entrypoints.
|
||||
// this makes mixins to some important places, like BuiltInRegistries, impossible since mixin isn't
|
||||
// initialized when the class is loaded.
|
||||
function initializeCoreMod() {
|
||||
return {
|
||||
"registrycoremod": {
|
||||
'target': {
|
||||
'type': 'METHOD',
|
||||
'class': 'net.minecraft.core.registries.BuiltInRegistries',
|
||||
'methodName': '<clinit>',
|
||||
'methodDesc': '()V'
|
||||
},
|
||||
'transformer': function (method) {
|
||||
var CreateBuiltInRegistries = "com/simibubi/create/api/registry/CreateBuiltInRegistries";
|
||||
|
||||
var insn = ASMAPI.buildMethodCall(CreateBuiltInRegistries, "init", "()V", ASMAPI.MethodType.STATIC)
|
||||
|
||||
method.instructions.insertBefore(method.instructions.getLast(), insn)
|
||||
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,11 +25,13 @@
|
|||
"accessor.AbstractRegistrateAccessor",
|
||||
"accessor.BlockBehaviourAccessor",
|
||||
"accessor.BlockLootSubProviderAccessor",
|
||||
"accessor.BuiltInRegistriesAccessor",
|
||||
"accessor.DispenserBlockAccessor",
|
||||
"accessor.FallingBlockEntityAccessor",
|
||||
"accessor.FlowingFluidAccessor",
|
||||
"accessor.FluidInteractionRegistryAccessor",
|
||||
"accessor.GameTestHelperAccessor",
|
||||
"accessor.ItemModelGeneratorsAccessor",
|
||||
"accessor.ItemStackHandlerAccessor",
|
||||
"accessor.LivingEntityAccessor",
|
||||
"accessor.MappedRegistryAccessor",
|
||||
|
|
Loading…
Add table
Reference in a new issue