Merge branch 'mc1.20.1/feature-dev' of https://github.com/Creators-of-Create/Create-Concealed into mc1.20.1/feature-dev

This commit is contained in:
Kryppers 2025-02-26 17:40:23 +00:00
commit d821f11b32
49 changed files with 1203 additions and 860 deletions

View file

@ -130,6 +130,7 @@ _Now using Flywheel 1.0_
- Fixed vaults and tanks rotated in place not updating their multiblock correctly
- Hose pulley now deletes lilypads and other surface foliage
- Fixed crushing wheels not applying looting to killed entities
- Updated contraption chunkban protections, corrected limits and made them much harder to hit
#### API Changes
@ -138,6 +139,7 @@ _Now using Flywheel 1.0_
- Added `#create:chain_rideable` to mark items as valid for riding a chain with
- Added `#create:invalid_for_track_paving` for items
- Added `#create:sugar_cane_variants` to allow the mechanical saw to work with custom sugarcane variants (#7263)
- Added `#create:not_harvestable` to disallow blocks that the mechanical harvester would otherwise try to harvest
- New API for custom storage block behaviour on contraptions.
For simple cases, create provides the `#create:simple_mounted_storage` and `#create:chest_mounted_storage` block tags.
- Added `#create:non_breakable` to mark blocks that cannot be broken by block-breaking kinetics
@ -149,6 +151,22 @@ _Now using Flywheel 1.0_
- Synced AllPortalTracks with Create Fabric
- Implemented DyeHelper api (#7265)
- Implemented api to add custom block train conductors (#7030)
- Convert Potato Cannon project types into a dynamic registry
- Convert Potato Cannon projectile types into a dynamic registry
- Everything can be done with datapacks now, and there is no need to write a mod unless you need to add new
Render Modes, Entity Hit Actions or Block Hit Actions
- Reworked the AttachedRegistry class into SimpleRegistry and added Provider functionality
- Exposed all custom registries as API
- Exposed a handful of previously internal classes to the API, and gave them some cleanup
- BlockSpoutingBehaviour
- MovementBehaviour
- MovingInteractionBehaviour
- DisplaySource
- DisplayTarget
- ContraptionMovementSetting
- BoilerHeater
- PortalTrackProvider
- BlockMovementChecks
- ContraptionType
- MountedDispenseBehavior
- BlockStressValues
- OpenPipeEffectHandler

View file

@ -0,0 +1,5 @@
{
"values": [
"minecraft:fire"
]
}

View file

@ -268,7 +268,6 @@ import com.simibubi.create.foundation.data.ModelGen;
import com.simibubi.create.foundation.data.SharedProperties;
import com.simibubi.create.foundation.item.ItemDescription;
import com.simibubi.create.foundation.item.UncontainableBlockItem;
import com.simibubi.create.foundation.utility.ColorHandlers;
import com.simibubi.create.foundation.utility.DyeHelper;
import com.simibubi.create.infrastructure.config.CStress;
import com.tterrag.registrate.providers.RegistrateRecipeProvider;
@ -296,6 +295,7 @@ import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.IronBarsBlock;
import net.minecraft.world.level.block.RedStoneWireBlock;
import net.minecraft.world.level.block.RotatedPillarBlock;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockBehaviour;
@ -1259,7 +1259,8 @@ public class AllBlocks {
.transform(pickaxeOnly())
.blockstate(new ControllerRailGenerator()::generate)
.addLayer(() -> RenderType::cutoutMipped)
.color(() -> ColorHandlers::getRedstonePower)
.color(() -> () -> (state, world, pos, layer) -> RedStoneWireBlock
.getColorForPower(pos != null && world != null ? state.getValue(BlockStateProperties.POWER) : 0))
.tag(BlockTags.RAILS)
.item()
.model((c, p) -> p.generated(c, Create.asResource("block/" + c.getName())))

View file

@ -5,7 +5,7 @@ import java.util.Map;
import java.util.function.Supplier;
import com.simibubi.create.api.contraption.ContraptionType;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.content.contraptions.Contraption;
import com.simibubi.create.content.contraptions.bearing.BearingContraption;
import com.simibubi.create.content.contraptions.bearing.ClockworkContraption;
@ -17,34 +17,29 @@ import com.simibubi.create.content.contraptions.piston.PistonContraption;
import com.simibubi.create.content.contraptions.pulley.PulleyContraption;
import com.simibubi.create.content.trains.entity.CarriageContraption;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.RegistryObject;
import net.minecraft.core.Holder.Reference;
import net.minecraft.core.Registry;
public class AllContraptionTypes {
private static final DeferredRegister<ContraptionType> REGISTER = DeferredRegister.create(CreateRegistries.CONTRAPTION_TYPE, Create.ID);
public static final Map<String, ContraptionType> BY_LEGACY_NAME = new HashMap<>();
public static final RegistryObject<ContraptionType> PISTON = register("piston", PistonContraption::new);
public static final RegistryObject<ContraptionType> BEARING = register("bearing", BearingContraption::new);
public static final RegistryObject<ContraptionType> PULLEY = register("pulley", PulleyContraption::new);
public static final RegistryObject<ContraptionType> CLOCKWORK = register("clockwork", ClockworkContraption::new);
public static final RegistryObject<ContraptionType> MOUNTED = register("mounted", MountedContraption::new);
public static final RegistryObject<ContraptionType> STABILIZED = register("stabilized", StabilizedContraption::new);
public static final RegistryObject<ContraptionType> GANTRY = register("gantry", GantryContraption::new);
public static final RegistryObject<ContraptionType> CARRIAGE = register("carriage", CarriageContraption::new);
public static final RegistryObject<ContraptionType> ELEVATOR = register("elevator", ElevatorContraption::new);
public static final Reference<ContraptionType> PISTON = register("piston", PistonContraption::new);
public static final Reference<ContraptionType> BEARING = register("bearing", BearingContraption::new);
public static final Reference<ContraptionType> PULLEY = register("pulley", PulleyContraption::new);
public static final Reference<ContraptionType> CLOCKWORK = register("clockwork", ClockworkContraption::new);
public static final Reference<ContraptionType> MOUNTED = register("mounted", MountedContraption::new);
public static final Reference<ContraptionType> STABILIZED = register("stabilized", StabilizedContraption::new);
public static final Reference<ContraptionType> GANTRY = register("gantry", GantryContraption::new);
public static final Reference<ContraptionType> CARRIAGE = register("carriage", CarriageContraption::new);
public static final Reference<ContraptionType> ELEVATOR = register("elevator", ElevatorContraption::new);
private static RegistryObject<ContraptionType> register(String name, Supplier<? extends Contraption> factory) {
return REGISTER.register(name, () -> {
ContraptionType type = new ContraptionType(factory);
BY_LEGACY_NAME.put(name, type);
return type;
});
private static Reference<ContraptionType> register(String name, Supplier<? extends Contraption> factory) {
ContraptionType type = new ContraptionType(factory);
BY_LEGACY_NAME.put(name, type);
return Registry.registerForHolder(CreateBuiltInRegistries.CONTRAPTION_TYPE, Create.asResource(name), type);
}
public static void register(IEventBus modEventBus) {
REGISTER.register(modEventBus);
public static void init() {
}
}

View file

@ -130,6 +130,7 @@ public class AllPartialModels {
CHAIN_CONVEYOR_SHAFT = block("chain_conveyor/shaft"),
FROGPORT_BODY = block("package_frogport/body"), FROGPORT_HEAD = block("package_frogport/head"),
FROGPORT_HEAD_GOGGLES = block("package_frogport/head_goggles"),
FROGPORT_TONGUE = block("package_frogport/tongue"),
POSTBOX_FLAG = block("package_postbox/flag"),

View file

@ -113,6 +113,7 @@ public class AllTags {
FALLBACK_MOUNTED_STORAGE_BLACKLIST,
ROOTS,
SUGAR_CANE_VARIANTS,
NON_HARVESTABLE,
CORALS,

View file

@ -26,6 +26,7 @@ import com.simibubi.create.content.schematics.ServerSchematicLoader;
import com.simibubi.create.content.trains.GlobalRailwayManager;
import com.simibubi.create.content.trains.bogey.BogeySizes;
import com.simibubi.create.content.trains.track.AllPortalTracks;
import com.simibubi.create.foundation.CreateNBTProcessors;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.block.CopperRegistries;
@ -33,7 +34,6 @@ import com.simibubi.create.foundation.data.CreateRegistrate;
import com.simibubi.create.foundation.item.ItemDescription;
import com.simibubi.create.foundation.item.KineticStats;
import com.simibubi.create.foundation.item.TooltipModifier;
import com.simibubi.create.foundation.utility.CreateNBTProcessors;
import com.simibubi.create.infrastructure.command.ServerLagger;
import com.simibubi.create.infrastructure.config.AllConfigs;
import com.simibubi.create.infrastructure.data.CreateDatagen;
@ -137,12 +137,6 @@ public class Create {
AllConfigs.register(modLoadingContext);
// TODO - Make these use Registry.register and move them into the RegisterEvent
AllArmInteractionPointTypes.register(modEventBus);
AllFanProcessingTypes.register(modEventBus);
AllItemAttributeTypes.register(modEventBus);
AllContraptionTypes.register(modEventBus);
// FIXME: some of these registrations are not thread-safe
BogeySizes.init();
AllBogeyStyles.init();
@ -189,6 +183,10 @@ public class Create {
}
public static void onRegister(final RegisterEvent event) {
AllArmInteractionPointTypes.init();
AllFanProcessingTypes.init();
AllItemAttributeTypes.init();
AllContraptionTypes.init();
AllPotatoProjectileRenderModes.init();
AllPotatoProjectileEntityHitActions.init();
AllPotatoProjectileBlockHitActions.init();

View file

@ -20,9 +20,9 @@ import com.simibubi.create.content.schematics.client.SchematicHandler;
import com.simibubi.create.content.trains.GlobalRailwayManager;
import com.simibubi.create.foundation.ClientResourceReloadListener;
import com.simibubi.create.foundation.blockEntity.behaviour.ValueSettingsClient;
import com.simibubi.create.foundation.model.ModelSwapper;
import com.simibubi.create.foundation.ponder.CreatePonderPlugin;
import com.simibubi.create.foundation.render.AllInstanceTypes;
import com.simibubi.create.foundation.utility.ModelSwapper;
import com.simibubi.create.infrastructure.config.AllConfigs;
import com.simibubi.create.infrastructure.gui.CreateMainMenuScreen;
@ -40,6 +40,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;

View file

@ -4,8 +4,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.Type;
@ -17,15 +15,12 @@ import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileRen
import net.minecraft.core.Holder;
import net.minecraft.core.Holder.Reference;
import net.minecraft.core.HolderSet;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.RegistryCodecs;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.worldgen.BootstapContext;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
@ -55,26 +50,12 @@ public record PotatoCannonProjectileType(HolderSet<Item> items, int reloadTicks,
PotatoProjectileBlockHitAction.CODEC.optionalFieldOf("on_entity_hit").forGetter(p -> p.onBlockHit)
).apply(i, PotatoCannonProjectileType::new));
@Nullable
public static PotatoCannonProjectileType getTypeForItem(Level level, Item item) {
public static Optional<Reference<PotatoCannonProjectileType>> getTypeForItem(RegistryAccess registryAccess, Item item) {
// Cache this if it causes performance issues, but it probably won't
List<PotatoCannonProjectileType> types = level.registryAccess()
.lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE)
return registryAccess.lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE)
.listElements()
.map(Reference::value)
.toList();
for (PotatoCannonProjectileType type : types)
if (type.items.contains(item.builtInRegistryHolder()))
return type;
return null;
}
public static Optional<PotatoCannonProjectileType> getTypeForStack(Level level, ItemStack item) {
if (item.isEmpty())
return Optional.empty();
return Optional.ofNullable(getTypeForItem(level, item.getItem()));
.filter(ref -> ref.value().items.contains(item.builtInRegistryHolder()))
.findFirst();
}
public boolean preEntityHit(ItemStack stack, EntityHitResult ray) {
@ -96,8 +77,6 @@ public record PotatoCannonProjectileType(HolderSet<Item> items, int reloadTicks,
}
public static class Builder {
private ResourceLocation id;
private final List<Holder<Item>> items = new ArrayList<>();
private int reloadTicks = 10;
private int damage = 1;
@ -114,10 +93,6 @@ public record PotatoCannonProjectileType(HolderSet<Item> items, int reloadTicks,
private PotatoProjectileEntityHitAction onEntityHit = null;
private PotatoProjectileBlockHitAction onBlockHit = null;
public Builder(ResourceLocation id) {
this.id = id;
}
public Builder reloadTicks(int reload) {
this.reloadTicks = reload;
return this;
@ -209,8 +184,8 @@ public record PotatoCannonProjectileType(HolderSet<Item> items, int reloadTicks,
return this;
}
public void register(BootstapContext<PotatoCannonProjectileType> ctx) {
PotatoCannonProjectileType type = new PotatoCannonProjectileType(
public PotatoCannonProjectileType build() {
return new PotatoCannonProjectileType(
HolderSet.direct(items),
reloadTicks,
damage,
@ -227,12 +202,6 @@ public record PotatoCannonProjectileType(HolderSet<Item> items, int reloadTicks,
Optional.ofNullable(onEntityHit),
Optional.ofNullable(onBlockHit)
);
ctx.register(ResourceKey.create(CreateRegistries.POTATO_PROJECTILE_TYPE, id), type);
}
public void registerAndAssign(BootstapContext<PotatoCannonProjectileType> ctx, ItemLike... items) {
addItems(items);
register(ctx);
}
}
}

View file

@ -73,7 +73,6 @@ import com.simibubi.create.content.trains.bogey.AbstractBogeyBlock;
import com.simibubi.create.foundation.blockEntity.IMultiBlockEntityContainer;
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.utility.BlockHelper;
import com.simibubi.create.foundation.utility.ICoordinate;
import com.simibubi.create.infrastructure.config.AllConfigs;
import net.createmod.catnip.data.Iterate;
@ -86,6 +85,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.HolderGetter;
import net.minecraft.core.Vec3i;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
@ -1389,7 +1389,7 @@ public abstract class Contraption {
public void expandBoundsAroundAxis(Axis axis) {
Set<BlockPos> blocks = getBlocks().keySet();
int radius = (int) (Math.ceil(Math.sqrt(getRadius(blocks, axis))));
int radius = (int) (Math.ceil(getRadius(blocks, axis)));
int maxX = radius + 2;
int maxY = radius + 2;
@ -1398,13 +1398,13 @@ public abstract class Contraption {
int minY = -radius - 1;
int minZ = -radius - 1;
if (axis == Direction.Axis.X) {
if (axis == Axis.X) {
maxX = (int) bounds.maxX;
minX = (int) bounds.minX;
} else if (axis == Direction.Axis.Y) {
} else if (axis == Axis.Y) {
maxY = (int) bounds.maxY;
minY = (int) bounds.minY;
} else if (axis == Direction.Axis.Z) {
} else if (axis == Axis.Z) {
maxZ = (int) bounds.maxZ;
minZ = (int) bounds.minZ;
}
@ -1489,32 +1489,38 @@ public abstract class Contraption {
});
}
public static float getRadius(Set<BlockPos> blocks, Direction.Axis axis) {
public static double getRadius(Iterable<? extends Vec3i> blocks, Axis axis) {
Axis axisA;
Axis axisB;
switch (axis) {
case X:
return getMaxDistSqr(blocks, BlockPos::getY, BlockPos::getZ);
case Y:
return getMaxDistSqr(blocks, BlockPos::getX, BlockPos::getZ);
case Z:
return getMaxDistSqr(blocks, BlockPos::getX, BlockPos::getY);
case X -> {
axisA = Axis.Y;
axisB = Axis.Z;
}
case Y -> {
axisA = Axis.X;
axisB = Axis.Z;
}
case Z -> {
axisA = Axis.X;
axisB = Axis.Y;
}
default -> throw new IllegalStateException("Unexpected value: " + axis);
}
throw new IllegalStateException("Impossible axis");
}
int maxDistSq = 0;
for (Vec3i vec : blocks) {
int a = vec.get(axisA);
int b = vec.get(axisB);
public static float getMaxDistSqr(Set<BlockPos> blocks, ICoordinate one, ICoordinate other) {
float maxDistSq = -1;
for (BlockPos pos : blocks) {
float a = one.get(pos);
float b = other.get(pos);
float distSq = a * a + b * b;
int distSq = a * a + b * b;
if (distSq > maxDistSq)
maxDistSq = distSq;
}
return maxDistSq;
return Math.sqrt(maxDistSq);
}
public MountedStorageManager getStorage() {

View file

@ -57,12 +57,15 @@ public class HarvesterMovementBehaviour implements MovementBehaviour {
@Override
public void visitNewPosition(MovementContext context, BlockPos pos) {
Level world = context.world;
BlockState stateVisited = world.getBlockState(pos);
boolean notCropButCuttable = false;
if (world.isClientSide)
return;
BlockState stateVisited = world.getBlockState(pos);
if (stateVisited.isAir() || AllBlockTags.NON_HARVESTABLE.matches(stateVisited))
return;
boolean notCropButCuttable = false;
if (!isValidCrop(world, pos, stateVisited)) {
if (isValidOther(world, pos, stateVisited))
notCropButCuttable = true;

View file

@ -1,9 +1,10 @@
package com.simibubi.create.content.equipment.clipboard;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import javax.annotation.Nullable;
import com.simibubi.create.foundation.utility.CreateNBTProcessors;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.CreateNBTProcessors;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
@ -11,9 +12,8 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraftforge.network.NetworkEvent.Context;
import javax.annotation.Nullable;
import net.minecraftforge.network.NetworkEvent.Context;
public class ClipboardEditPacket extends SimplePacketBase {

View file

@ -21,32 +21,34 @@ import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Blocks;
public class AllPotatoProjectileTypes {
public static ResourceKey<PotatoCannonProjectileType> FALLBACK = ResourceKey.create(CreateRegistries.POTATO_PROJECTILE_TYPE, Create.asResource("fallback"));
public static final ResourceKey<PotatoCannonProjectileType> FALLBACK = ResourceKey.create(CreateRegistries.POTATO_PROJECTILE_TYPE, Create.asResource("fallback"));
public static void bootstrap(BootstapContext<PotatoCannonProjectileType> ctx) {
create("fallback")
register(ctx, "fallback", new PotatoCannonProjectileType.Builder()
.damage(0)
.register(ctx);
.build());
create("potato")
register(ctx, "potato", new PotatoCannonProjectileType.Builder()
.damage(5)
.reloadTicks(15)
.velocity(1.25f)
.knockback(1.5f)
.renderTumbling()
.onBlockHit(new PlantCrop(Blocks.POTATOES))
.registerAndAssign(ctx, Items.POTATO);
.addItems(Items.POTATO)
.build());
create("baked_potato")
register(ctx, "baked_potato", new PotatoCannonProjectileType.Builder()
.damage(5)
.reloadTicks(15)
.velocity(1.25f)
.knockback(0.5f)
.renderTumbling()
.preEntityHit(SetOnFire.seconds(3))
.registerAndAssign(ctx, Items.BAKED_POTATO);
.addItems(Items.BAKED_POTATO)
.build());
create("carrot")
register(ctx, "carrot", new PotatoCannonProjectileType.Builder()
.damage(4)
.reloadTicks(12)
.velocity(1.45f)
@ -54,18 +56,20 @@ public class AllPotatoProjectileTypes {
.renderTowardMotion(140, 1)
.soundPitch(1.5f)
.onBlockHit(new PlantCrop(Blocks.CARROTS))
.registerAndAssign(ctx, Items.CARROT);
.addItems(Items.CARROT)
.build());
create("golden_carrot")
register(ctx, "golden_carrot", new PotatoCannonProjectileType.Builder()
.damage(12)
.reloadTicks(15)
.velocity(1.45f)
.knockback(0.5f)
.renderTowardMotion(140, 2)
.soundPitch(1.5f)
.registerAndAssign(ctx, Items.GOLDEN_CARROT);
.addItems(Items.GOLDEN_CARROT)
.build());
create("sweet_berry")
register(ctx, "sweet_berry", new PotatoCannonProjectileType.Builder()
.damage(3)
.reloadTicks(10)
.knockback(0.1f)
@ -73,9 +77,10 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.splitInto(3)
.soundPitch(1.25f)
.registerAndAssign(ctx, Items.SWEET_BERRIES);
.addItems(Items.SWEET_BERRIES)
.build());
create("glow_berry")
register(ctx, "glow_berry", new PotatoCannonProjectileType.Builder()
.damage(2)
.reloadTicks(10)
.knockback(0.05f)
@ -84,9 +89,10 @@ public class AllPotatoProjectileTypes {
.splitInto(2)
.soundPitch(1.2f)
.onEntityHit(new PotionEffect(MobEffects.GLOWING, 1, 200, false))
.registerAndAssign(ctx, Items.GLOW_BERRIES);
.addItems(Items.GLOW_BERRIES)
.build());
create("chocolate_berry")
register(ctx, "chocolate_berry", new PotatoCannonProjectileType.Builder()
.damage(4)
.reloadTicks(10)
.knockback(0.2f)
@ -94,36 +100,40 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.splitInto(3)
.soundPitch(1.25f)
.registerAndAssign(ctx, AllItems.CHOCOLATE_BERRIES.get());
.addItems(AllItems.CHOCOLATE_BERRIES.get())
.build());
create("poison_potato")
register(ctx, "poison_potato", new PotatoCannonProjectileType.Builder()
.damage(5)
.reloadTicks(15)
.knockback(0.05f)
.velocity(1.25f)
.renderTumbling()
.onEntityHit(new PotionEffect(MobEffects.POISON, 1, 160, true))
.registerAndAssign(ctx, Items.POISONOUS_POTATO);
.addItems(Items.POISONOUS_POTATO)
.build());
create("chorus_fruit")
register(ctx, "chorus_fruit", new PotatoCannonProjectileType.Builder()
.damage(3)
.reloadTicks(15)
.velocity(1.20f)
.knockback(0.05f)
.renderTumbling()
.onEntityHit(new ChorusTeleport(20))
.registerAndAssign(ctx, Items.CHORUS_FRUIT);
.addItems(Items.CHORUS_FRUIT)
.build());
create("apple")
register(ctx, "apple", new PotatoCannonProjectileType.Builder()
.damage(5)
.reloadTicks(10)
.velocity(1.45f)
.knockback(0.5f)
.renderTumbling()
.soundPitch(1.1f)
.registerAndAssign(ctx, Items.APPLE);
.addItems(Items.APPLE)
.build());
create("honeyed_apple")
register(ctx, "honeyed_apple", new PotatoCannonProjectileType.Builder()
.damage(6)
.reloadTicks(15)
.velocity(1.35f)
@ -131,9 +141,10 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.soundPitch(1.1f)
.onEntityHit(new PotionEffect(MobEffects.MOVEMENT_SLOWDOWN, 2, 160, true))
.registerAndAssign(ctx, AllItems.HONEYED_APPLE.get());
.addItems(AllItems.HONEYED_APPLE.get())
.build());
create("golden_apple")
register(ctx, "golden_apple", new PotatoCannonProjectileType.Builder()
.damage(1)
.reloadTicks(100)
.velocity(1.45f)
@ -141,9 +152,10 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.soundPitch(1.1f)
.onEntityHit(CureZombieVillager.INSTANCE)
.registerAndAssign(ctx, Items.GOLDEN_APPLE);
.addItems(Items.GOLDEN_APPLE)
.build());
create("enchanted_golden_apple")
register(ctx, "enchanted_golden_apple", new PotatoCannonProjectileType.Builder()
.damage(1)
.reloadTicks(100)
.velocity(1.45f)
@ -151,27 +163,30 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.soundPitch(1.1f)
.onEntityHit(new FoodEffects(Foods.ENCHANTED_GOLDEN_APPLE, false))
.registerAndAssign(ctx, Items.ENCHANTED_GOLDEN_APPLE);
.addItems(Items.ENCHANTED_GOLDEN_APPLE)
.build());
create("beetroot")
register(ctx, "beetroot", new PotatoCannonProjectileType.Builder()
.damage(2)
.reloadTicks(5)
.velocity(1.6f)
.knockback(0.1f)
.renderTowardMotion(140, 2)
.soundPitch(1.6f)
.registerAndAssign(ctx, Items.BEETROOT);
.addItems(Items.BEETROOT)
.build());
create("melon_slice")
register(ctx, "melon_slice", new PotatoCannonProjectileType.Builder()
.damage(3)
.reloadTicks(8)
.knockback(0.1f)
.velocity(1.45f)
.renderTumbling()
.soundPitch(1.5f)
.registerAndAssign(ctx, Items.MELON_SLICE);
.addItems(Items.MELON_SLICE)
.build());
create("glistering_melon")
register(ctx, "glistering_melon", new PotatoCannonProjectileType.Builder()
.damage(5)
.reloadTicks(8)
.knockback(0.1f)
@ -179,9 +194,10 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.soundPitch(1.5f)
.onEntityHit(new PotionEffect(MobEffects.GLOWING, 1, 100, true))
.registerAndAssign(ctx, Items.GLISTERING_MELON_SLICE);
.addItems(Items.GLISTERING_MELON_SLICE)
.build());
create("melon_block")
register(ctx, "melon_block", new PotatoCannonProjectileType.Builder()
.damage(8)
.reloadTicks(20)
.knockback(2.0f)
@ -189,9 +205,10 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.soundPitch(0.9f)
.onBlockHit(new PlaceBlockOnGround(Blocks.MELON))
.registerAndAssign(ctx, Blocks.MELON);
.addItems(Blocks.MELON)
.build());
create("pumpkin_block")
register(ctx, "pumpkin_block", new PotatoCannonProjectileType.Builder()
.damage(6)
.reloadTicks(15)
.knockback(2.0f)
@ -199,9 +216,10 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.soundPitch(0.9f)
.onBlockHit(new PlaceBlockOnGround(Blocks.PUMPKIN))
.registerAndAssign(ctx, Blocks.PUMPKIN);
.addItems(Blocks.PUMPKIN)
.build());
create("pumpkin_pie")
register(ctx, "pumpkin_pie", new PotatoCannonProjectileType.Builder()
.damage(7)
.reloadTicks(15)
.knockback(0.05f)
@ -209,18 +227,20 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.sticky()
.soundPitch(1.1f)
.registerAndAssign(ctx, Items.PUMPKIN_PIE);
.addItems(Items.PUMPKIN_PIE)
.build());
create("cake")
register(ctx, "cake", new PotatoCannonProjectileType.Builder()
.damage(8)
.reloadTicks(15)
.knockback(0.1f)
.velocity(1.1f)
.renderTumbling()
.sticky()
.registerAndAssign(ctx, Items.CAKE);
.addItems(Items.CAKE)
.build());
create("blaze_cake")
register(ctx, "blaze_cake", new PotatoCannonProjectileType.Builder()
.damage(15)
.reloadTicks(20)
.knockback(0.3f)
@ -228,18 +248,20 @@ public class AllPotatoProjectileTypes {
.renderTumbling()
.sticky()
.preEntityHit(SetOnFire.seconds(12))
.registerAndAssign(ctx, AllItems.BLAZE_CAKE.get());
.addItems(AllItems.BLAZE_CAKE.get())
.build());
create("fish")
register(ctx, "fish", new PotatoCannonProjectileType.Builder()
.damage(4)
.knockback(0.6f)
.velocity(1.3f)
.renderTowardMotion(140, 1)
.sticky()
.soundPitch(1.3f)
.registerAndAssign(ctx, Items.COD, Items.COOKED_COD, Items.SALMON, Items.COOKED_SALMON, Items.TROPICAL_FISH);
.addItems(Items.COD, Items.COOKED_COD, Items.SALMON, Items.COOKED_SALMON, Items.TROPICAL_FISH)
.build());
create("pufferfish")
register(ctx, "pufferfish", new PotatoCannonProjectileType.Builder()
.damage(4)
.knockback(0.4f)
.velocity(1.1f)
@ -247,9 +269,10 @@ public class AllPotatoProjectileTypes {
.sticky()
.onEntityHit(new FoodEffects(Foods.PUFFERFISH, false))
.soundPitch(1.1f)
.registerAndAssign(ctx, Items.PUFFERFISH);
.addItems(Items.PUFFERFISH)
.build());
create("suspicious_stew")
register(ctx, "suspicious_stew", new PotatoCannonProjectileType.Builder()
.damage(3)
.reloadTicks(40)
.knockback(0.2f)
@ -257,10 +280,11 @@ public class AllPotatoProjectileTypes {
.renderTowardMotion(140, 1)
.dropStack(Items.BOWL.getDefaultInstance())
.onEntityHit(SuspiciousStew.INSTANCE)
.registerAndAssign(ctx, Items.SUSPICIOUS_STEW);
.addItems(Items.SUSPICIOUS_STEW)
.build());
}
private static PotatoCannonProjectileType.Builder create(String name) {
return new PotatoCannonProjectileType.Builder(Create.asResource(name));
private static void register(BootstapContext<PotatoCannonProjectileType> ctx, String name, PotatoCannonProjectileType type) {
ctx.register(ResourceKey.create(CreateRegistries.POTATO_PROJECTILE_TYPE, Create.asResource(name)), type);
}
}

View file

@ -11,15 +11,14 @@ import com.simibubi.create.AllEnchantments;
import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.CreateClient;
import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.content.equipment.armor.BacktankUtil;
import com.simibubi.create.content.equipment.zapper.ShootableGadgetItemMethods;
import com.simibubi.create.foundation.item.CustomArmPoseItem;
import com.simibubi.create.foundation.item.render.SimpleCustomRenderer;
import com.simibubi.create.foundation.utility.CreateLang;
import com.simibubi.create.foundation.utility.GlobalRegistryAccess;
import com.simibubi.create.infrastructure.config.AllConfigs;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.math.VecHelper;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
@ -28,6 +27,7 @@ import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.Holder;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
@ -51,22 +51,188 @@ import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.extensions.common.IClientItemExtensions;
import net.minecraftforge.server.ServerLifecycleHooks;
public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmPoseItem {
public static ItemStack CLIENT_CURRENT_AMMO = ItemStack.EMPTY;
public static final int MAX_DAMAGE = 100;
public PotatoCannonItem(Properties properties) {
super(properties.defaultDurability(MAX_DAMAGE));
}
@Nullable
public static Ammo getAmmo(Player player, ItemStack heldStack) {
ItemStack ammoStack = player.getProjectile(heldStack);
if (ammoStack.isEmpty()) {
return null;
}
Optional<Holder.Reference<PotatoCannonProjectileType>> optionalType = PotatoCannonProjectileType.getTypeForItem(player.level().registryAccess(), ammoStack.getItem());
if (optionalType.isEmpty()) {
return null;
}
return new Ammo(ammoStack, optionalType.get().get());
}
@Override
public InteractionResult useOn(UseOnContext context) {
return use(context.getLevel(), context.getPlayer(), context.getHand()).getResult();
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
ItemStack heldStack = player.getItemInHand(hand);
if (ShootableGadgetItemMethods.shouldSwap(player, heldStack, hand, s -> s.getItem() instanceof PotatoCannonItem)) {
return InteractionResultHolder.fail(heldStack);
}
Ammo ammo = getAmmo(player, heldStack);
if (ammo == null) {
return InteractionResultHolder.pass(heldStack);
}
ItemStack ammoStack = ammo.stack();
PotatoCannonProjectileType projectileType = ammo.type();
if (level.isClientSide) {
CreateClient.POTATO_CANNON_RENDER_HANDLER.dontAnimateItem(hand);
return InteractionResultHolder.success(heldStack);
}
Vec3 barrelPos = ShootableGadgetItemMethods.getGunBarrelVec(player, hand == InteractionHand.MAIN_HAND,
new Vec3(.75f, -0.15f, 1.5f));
Vec3 correction =
ShootableGadgetItemMethods.getGunBarrelVec(player, hand == InteractionHand.MAIN_HAND, new Vec3(-.05f, 0, 0))
.subtract(player.position()
.add(0, player.getEyeHeight(), 0));
Vec3 lookVec = player.getLookAngle();
Vec3 motion = lookVec.add(correction)
.normalize()
.scale(2)
.scale(projectileType.velocityMultiplier());
float soundPitch = projectileType.soundPitch() + (level.getRandom().nextFloat() - .5f) / 4f;
boolean spray = projectileType.split() > 1;
Vec3 sprayBase = VecHelper.rotate(new Vec3(0, 0.1, 0), 360 * level.getRandom().nextFloat(), Axis.Z);
float sprayChange = 360f / projectileType.split();
ItemStack ammoStackCopy = ammoStack.copy();
for (int i = 0; i < projectileType.split(); i++) {
PotatoProjectileEntity projectile = AllEntityTypes.POTATO_PROJECTILE.create(level);
projectile.setItem(ammoStackCopy);
projectile.setEnchantmentEffectsFromCannon(heldStack);
Vec3 splitMotion = motion;
if (spray) {
float imperfection = 40 * (level.getRandom().nextFloat() - 0.5f);
Vec3 sprayOffset = VecHelper.rotate(sprayBase, i * sprayChange + imperfection, Axis.Z);
splitMotion = splitMotion.add(VecHelper.lookAt(sprayOffset, motion));
}
if (i != 0)
projectile.recoveryChance = 0;
projectile.setPos(barrelPos.x, barrelPos.y, barrelPos.z);
projectile.setDeltaMovement(splitMotion);
projectile.setOwner(player);
level.addFreshEntity(projectile);
}
if (!player.isCreative()) {
ammoStack.shrink(1);
if (ammoStack.isEmpty())
player.getInventory().removeItem(ammoStack);
}
if (!BacktankUtil.canAbsorbDamage(player, maxUses()))
heldStack.hurtAndBreak(1, player, p -> p.broadcastBreakEvent(hand));
ShootableGadgetItemMethods.applyCooldown(player, heldStack, hand, s -> s.getItem() instanceof PotatoCannonItem, projectileType.reloadTicks());
ShootableGadgetItemMethods.sendPackets(player,
b -> new PotatoCannonPacket(barrelPos, lookVec.normalize(), ammoStack, hand, soundPitch, b));
return InteractionResultHolder.success(heldStack);
}
@Override
@OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltip, TooltipFlag flag) {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) {
super.appendHoverText(stack, level, tooltip, flag);
return;
}
Ammo ammo = getAmmo(player, stack);
if (ammo == null) {
super.appendHoverText(stack, level, tooltip, flag);
return;
}
ItemStack ammoStack = ammo.stack();
PotatoCannonProjectileType type = ammo.type();
int power = stack.getEnchantmentLevel(Enchantments.POWER_ARROWS);
int punch = stack.getEnchantmentLevel(Enchantments.PUNCH_ARROWS);
final float additionalDamageMult = 1 + power * .2f;
final float additionalKnockback = punch * .5f;
String _attack = "potato_cannon.ammo.attack_damage";
String _reload = "potato_cannon.ammo.reload_ticks";
String _knockback = "potato_cannon.ammo.knockback";
tooltip.add(CommonComponents.EMPTY);
tooltip.add(Component.translatable(ammoStack.getDescriptionId()).append(Component.literal(":"))
.withStyle(ChatFormatting.GRAY));
MutableComponent spacing = CommonComponents.space();
ChatFormatting green = ChatFormatting.GREEN;
ChatFormatting darkGreen = ChatFormatting.DARK_GREEN;
float damageF = type.damage() * additionalDamageMult;
MutableComponent damage = Component.literal(damageF == Mth.floor(damageF) ? "" + Mth.floor(damageF) : "" + damageF);
MutableComponent reloadTicks = Component.literal("" + type.reloadTicks());
MutableComponent knockback =
Component.literal("" + (type.knockback() + additionalKnockback));
damage = damage.withStyle(additionalDamageMult > 1 ? green : darkGreen);
knockback = knockback.withStyle(additionalKnockback > 0 ? green : darkGreen);
reloadTicks = reloadTicks.withStyle(darkGreen);
tooltip.add(spacing.plainCopy()
.append(CreateLang.translateDirect(_attack, damage)
.withStyle(darkGreen)));
tooltip.add(spacing.plainCopy()
.append(CreateLang.translateDirect(_reload, reloadTicks)
.withStyle(darkGreen)));
tooltip.add(spacing.plainCopy()
.append(CreateLang.translateDirect(_knockback, knockback)
.withStyle(darkGreen)));
super.appendHoverText(stack, level, tooltip, flag);
}
@Override
public boolean canAttackBlock(BlockState state, Level world, BlockPos pos, Player player) {
return false;
}
@Override
public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
return slotChanged || newStack.getItem() != oldStack.getItem();
}
@Override
public Predicate<ItemStack> getAllSupportedProjectiles() {
return stack -> PotatoCannonProjectileType.getTypeForItem(GlobalRegistryAccess.getOrThrow(), stack.getItem())
.isPresent();
}
@Override
public int getDefaultProjectileRange() {
return 15;
}
@Override
public boolean canApplyAtEnchantingTable(ItemStack stack, Enchantment enchantment) {
if (enchantment == Enchantments.POWER_ARROWS)
@ -82,11 +248,6 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
return super.canApplyAtEnchantingTable(stack, enchantment);
}
@Override
public InteractionResult useOn(UseOnContext context) {
return use(context.getLevel(), context.getPlayer(), context.getHand()).getResult();
}
@Override
public boolean isBarVisible(ItemStack stack) {
return BacktankUtil.isBarVisible(stack, maxUses());
@ -102,175 +263,10 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
return BacktankUtil.getBarColor(stack, maxUses());
}
private int maxUses() {
private static int maxUses() {
return AllConfigs.server().equipment.maxPotatoCannonShots.get();
}
public boolean isCannon(ItemStack stack) {
return stack.getItem() instanceof PotatoCannonItem;
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
ItemStack stack = player.getItemInHand(hand);
return findAmmoInInventory(level, player, stack).map(itemStack -> {
if (ShootableGadgetItemMethods.shouldSwap(player, stack, hand, this::isCannon))
return InteractionResultHolder.fail(stack);
if (level.isClientSide) {
CreateClient.POTATO_CANNON_RENDER_HANDLER.dontAnimateItem(hand);
return InteractionResultHolder.success(stack);
}
Vec3 barrelPos = ShootableGadgetItemMethods.getGunBarrelVec(player, hand == InteractionHand.MAIN_HAND,
new Vec3(.75f, -0.15f, 1.5f));
Vec3 correction =
ShootableGadgetItemMethods.getGunBarrelVec(player, hand == InteractionHand.MAIN_HAND, new Vec3(-.05f, 0, 0))
.subtract(player.position()
.add(0, player.getEyeHeight(), 0));
PotatoCannonProjectileType projectileType = PotatoCannonProjectileType.getTypeForStack(level, itemStack)
.orElseGet(() ->
level.registryAccess()
.lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE)
.getOrThrow(AllPotatoProjectileTypes.FALLBACK)
.value()
);
Vec3 lookVec = player.getLookAngle();
Vec3 motion = lookVec.add(correction)
.normalize()
.scale(2)
.scale(projectileType.velocityMultiplier());
float soundPitch = projectileType.soundPitch() + (level.getRandom().nextFloat() - .5f) / 4f;
boolean spray = projectileType.split() > 1;
Vec3 sprayBase = VecHelper.rotate(new Vec3(0, 0.1, 0), 360 * level.getRandom().nextFloat(), Axis.Z);
float sprayChange = 360f / projectileType.split();
for (int i = 0; i < projectileType.split(); i++) {
PotatoProjectileEntity projectile = AllEntityTypes.POTATO_PROJECTILE.create(level);
projectile.setItem(itemStack);
projectile.setEnchantmentEffectsFromCannon(stack);
Vec3 splitMotion = motion;
if (spray) {
float imperfection = 40 * (level.getRandom().nextFloat() - 0.5f);
Vec3 sprayOffset = VecHelper.rotate(sprayBase, i * sprayChange + imperfection, Axis.Z);
splitMotion = splitMotion.add(VecHelper.lookAt(sprayOffset, motion));
}
if (i != 0)
projectile.recoveryChance = 0;
projectile.setPos(barrelPos.x, barrelPos.y, barrelPos.z);
projectile.setDeltaMovement(splitMotion);
projectile.setOwner(player);
level.addFreshEntity(projectile);
}
if (!player.isCreative()) {
itemStack.shrink(1);
if (itemStack.isEmpty())
player.getInventory().removeItem(itemStack);
}
if (!BacktankUtil.canAbsorbDamage(player, maxUses()))
stack.hurtAndBreak(1, player, p -> p.broadcastBreakEvent(hand));
Integer cooldown =
findAmmoInInventory(level, player, stack).flatMap(i -> PotatoCannonProjectileType.getTypeForStack(level, i))
.map(potatoCannonProjectileType -> potatoCannonProjectileType.reloadTicks())
.orElse(10);
ShootableGadgetItemMethods.applyCooldown(player, stack, hand, this::isCannon, cooldown);
ShootableGadgetItemMethods.sendPackets(player,
b -> new PotatoCannonPacket(barrelPos, lookVec.normalize(), itemStack, hand, soundPitch, b));
return InteractionResultHolder.success(stack);
})
.orElse(InteractionResultHolder.pass(stack));
}
@Override
public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
return slotChanged || newStack.getItem() != oldStack.getItem();
}
private Optional<ItemStack> findAmmoInInventory(Level level, Player player, ItemStack held) {
ItemStack findAmmo = player.getProjectile(held);
return PotatoCannonProjectileType.getTypeForStack(level, findAmmo)
.map($ -> findAmmo);
}
@OnlyIn(Dist.CLIENT)
public static Optional<ItemStack> getAmmoforPreview(ItemStack cannon) {
if (AnimationTickHolder.getTicks() % 3 != 0)
return Optional.of(CLIENT_CURRENT_AMMO)
.filter(stack -> !stack.isEmpty());
LocalPlayer player = Minecraft.getInstance().player;
CLIENT_CURRENT_AMMO = ItemStack.EMPTY;
if (player == null)
return Optional.empty();
ItemStack findAmmo = player.getProjectile(cannon);
Optional<ItemStack> found = PotatoCannonProjectileType.getTypeForStack(Minecraft.getInstance().level, findAmmo)
.map($ -> findAmmo);
found.ifPresent(stack -> CLIENT_CURRENT_AMMO = stack);
return found;
}
@Override
@OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltip, TooltipFlag flag) {
int power = stack.getEnchantmentLevel(Enchantments.POWER_ARROWS);
int punch = stack.getEnchantmentLevel(Enchantments.PUNCH_ARROWS);
final float additionalDamageMult = 1 + power * .2f;
final float additionalKnockback = punch * .5f;
getAmmoforPreview(stack).ifPresent(ammo -> {
String _attack = "potato_cannon.ammo.attack_damage";
String _reload = "potato_cannon.ammo.reload_ticks";
String _knockback = "potato_cannon.ammo.knockback";
tooltip.add(CommonComponents.EMPTY);
tooltip.add(Component.translatable(ammo.getDescriptionId()).append(Component.literal(":"))
.withStyle(ChatFormatting.GRAY));
PotatoCannonProjectileType type = PotatoCannonProjectileType.getTypeForStack(Minecraft.getInstance().level, ammo)
.get();
MutableComponent spacing = CommonComponents.space();
ChatFormatting green = ChatFormatting.GREEN;
ChatFormatting darkGreen = ChatFormatting.DARK_GREEN;
float damageF = type.damage() * additionalDamageMult;
MutableComponent damage = Component.literal(damageF == Mth.floor(damageF) ? "" + Mth.floor(damageF) : "" + damageF);
MutableComponent reloadTicks = Component.literal("" + type.reloadTicks());
MutableComponent knockback =
Component.literal("" + (type.knockback() + additionalKnockback));
damage = damage.withStyle(additionalDamageMult > 1 ? green : darkGreen);
knockback = knockback.withStyle(additionalKnockback > 0 ? green : darkGreen);
reloadTicks = reloadTicks.withStyle(darkGreen);
tooltip.add(spacing.plainCopy()
.append(CreateLang.translateDirect(_attack, damage)
.withStyle(darkGreen)));
tooltip.add(spacing.plainCopy()
.append(CreateLang.translateDirect(_reload, reloadTicks)
.withStyle(darkGreen)));
tooltip.add(spacing.plainCopy()
.append(CreateLang.translateDirect(_knockback, knockback)
.withStyle(darkGreen)));
});
super.appendHoverText(stack, level, tooltip, flag);
}
@Override
public Predicate<ItemStack> getAllSupportedProjectiles() {
Level level = ServerLifecycleHooks.getCurrentServer().getLevel(Level.OVERWORLD);
return stack -> PotatoCannonProjectileType.getTypeForStack(level, stack)
.isPresent();
}
@Override
public boolean onEntitySwing(ItemStack stack, LivingEntity entity) {
return true;
@ -290,15 +286,12 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
return null;
}
@Override
public int getDefaultProjectileRange() {
return 15;
}
@Override
@OnlyIn(Dist.CLIENT)
public void initializeClient(Consumer<IClientItemExtensions> consumer) {
consumer.accept(SimpleCustomRenderer.create(this, new PotatoCannonItemRenderer()));
}
public record Ammo(ItemStack stack, PotatoCannonProjectileType type) {
}
}

View file

@ -5,47 +5,69 @@ import com.mojang.math.Axis;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.equipment.potatoCannon.PotatoCannonItem.Ammo;
import com.simibubi.create.foundation.item.render.CustomRenderedItemModel;
import com.simibubi.create.foundation.item.render.CustomRenderedItemModelRenderer;
import com.simibubi.create.foundation.item.render.PartialItemModelRenderer;
import dev.engine_room.flywheel.lib.model.baked.PartialModel;
import dev.engine_room.flywheel.lib.transform.TransformStack;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.IItemDecorator;
public class PotatoCannonItemRenderer extends CustomRenderedItemModelRenderer {
public static final IItemDecorator DECORATOR = (guiGraphics, font, stack, xOffset, yOffset) -> {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) {
return false;
}
Ammo ammo = PotatoCannonItem.getAmmo(player, stack);
if (ammo == null || AllItems.POTATO_CANNON.is(ammo.stack())) {
return false;
}
PoseStack poseStack = guiGraphics.pose();
poseStack.pushPose();
poseStack.translate(xOffset, yOffset + 8, 100);
poseStack.scale(.5f, .5f, .5f);
guiGraphics.renderItem(ammo.stack(), 0, 0);
poseStack.popPose();
return false;
};
protected static final PartialModel COG = PartialModel.of(Create.asResource("item/potato_cannon/cog"));
@Override
protected void render(ItemStack stack, CustomRenderedItemModel model, PartialItemModelRenderer renderer,
ItemDisplayContext transformType, PoseStack ms, MultiBufferSource buffer, int light, int overlay) {
Minecraft mc = Minecraft.getInstance();
ItemRenderer itemRenderer = mc.getItemRenderer();
renderer.render(model.getOriginalModel(), light);
Minecraft mc = Minecraft.getInstance();
LocalPlayer player = mc.player;
boolean mainHand = player.getMainHandItem() == stack;
boolean offHand = player.getOffhandItem() == stack;
boolean leftHanded = player.getMainArm() == HumanoidArm.LEFT;
float offset = .5f / 16;
float worldTime = AnimationTickHolder.getRenderTime() / 10;
float angle = worldTime * -25;
float speed = CreateClient.POTATO_CANNON_RENDER_HANDLER.getAnimation(mainHand ^ leftHanded,
AnimationTickHolder.getPartialTicks());
float angle = AnimationTickHolder.getRenderTime() * -2.5f;
if (player != null) {
boolean inMainHand = player.getMainHandItem() == stack;
boolean inOffHand = player.getOffhandItem() == stack;
if (inMainHand || inOffHand) {
boolean leftHanded = player.getMainArm() == HumanoidArm.LEFT;
float speed = CreateClient.POTATO_CANNON_RENDER_HANDLER.getAnimation(inMainHand ^ leftHanded,
AnimationTickHolder.getPartialTicks());
angle += 360 * Mth.clamp(speed * 5, 0, 1);
}
}
if (mainHand || offHand)
angle += 360 * Mth.clamp(speed * 5, 0, 1);
angle %= 360;
float offset = .5f / 16;
ms.pushPose();
ms.translate(0, offset, 0);
@ -53,20 +75,5 @@ public class PotatoCannonItemRenderer extends CustomRenderedItemModelRenderer {
ms.translate(0, -offset, 0);
renderer.render(COG.get(), light);
ms.popPose();
if (transformType == ItemDisplayContext.GUI) {
PotatoCannonItem.getAmmoforPreview(stack).ifPresent(ammo -> {
if (AllItems.POTATO_CANNON.is(ammo)) return;
PoseStack localMs = new PoseStack();
localMs.translate(-1 / 4f, -1 / 4f, 1);
localMs.scale(.5f, .5f, .5f);
TransformStack.of(localMs)
.rotateYDegrees(-34);
itemRenderer.renderStatic(ammo, ItemDisplayContext.GUI, light, OverlayTexture.NO_OVERLAY, localMs,
buffer, mc.level, 0);
});
}
}
}

View file

@ -1,7 +1,6 @@
package com.simibubi.create.content.equipment.potatoCannon;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.equipment.zapper.ShootableGadgetRenderHandler;
import com.simibubi.create.foundation.particle.AirParticleData;
@ -26,8 +25,7 @@ public class PotatoCannonRenderHandler extends ShootableGadgetRenderHandler {
@Override
protected boolean appliesTo(ItemStack stack) {
return AllItems.POTATO_CANNON.get()
.isCannon(stack);
return stack.getItem() instanceof PotatoCannonItem;
}
public void beforeShoot(float nextPitch, Vec3 location, Vec3 motion, ItemStack stack) {

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.equipment.potatoCannon;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.AllEnchantments;
import com.simibubi.create.AllSoundEvents;
@ -58,28 +59,17 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
protected float additionalKnockback = 0;
protected float recoveryChance = 0;
public PotatoProjectileEntity(EntityType<? extends AbstractHurtingProjectile> type, Level world) {
super(type, world);
}
public ItemStack getItem() {
return stack;
public PotatoProjectileEntity(EntityType<? extends AbstractHurtingProjectile> type, Level level) {
super(type, level);
}
public void setItem(ItemStack stack) {
this.stack = stack;
}
public PotatoCannonProjectileType getProjectileType() {
if (type == null)
type = PotatoCannonProjectileType.getTypeForStack(level(), stack)
.orElseGet(() ->
level().registryAccess()
.lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE)
.getOrThrow(AllPotatoProjectileTypes.FALLBACK)
.value()
);
return type;
type = PotatoCannonProjectileType.getTypeForItem(level().registryAccess(), stack.getItem())
.orElseGet(() -> level().registryAccess()
.registryOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE)
.getHolderOrThrow(AllPotatoProjectileTypes.FALLBACK))
.value();
}
public void setEnchantmentEffectsFromCannon(ItemStack cannon) {
@ -98,9 +88,18 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
recoveryChance = .125f + recovery * .125f;
}
public ItemStack getItem() {
return stack;
}
@Nullable
public PotatoCannonProjectileType getProjectileType() {
return type;
}
@Override
public void readAdditionalSaveData(CompoundTag nbt) {
stack = ItemStack.of(nbt.getCompound("Item"));
setItem(ItemStack.of(nbt.getCompound("Item")));
additionalDamageMult = nbt.getFloat("AdditionalDamage");
additionalKnockback = nbt.getFloat("AdditionalKnockback");
recoveryChance = nbt.getFloat("Recovery");
@ -116,6 +115,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
super.addAdditionalSaveData(nbt);
}
@Nullable
public Entity getStuckEntity() {
if (stuckEntity == null)
return null;
@ -136,27 +136,26 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
if (getStuckEntity() != null)
return stuckRenderer;
return getProjectileType().renderMode();
return type.renderMode();
}
@Override
public void tick() {
PotatoCannonProjectileType projectileType = getProjectileType();
Entity stuckEntity = getStuckEntity();
if (stuckEntity != null) {
if (getY() < stuckEntity.getY() - 0.1) {
pop(position());
kill();
} else {
stuckFallSpeed += 0.007 * projectileType.gravityMultiplier();
stuckFallSpeed += 0.007 * type.gravityMultiplier();
stuckOffset = stuckOffset.add(0, -stuckFallSpeed, 0);
Vec3 pos = stuckEntity.position()
.add(stuckOffset);
setPos(pos.x, pos.y, pos.z);
}
} else {
setDeltaMovement(getDeltaMovement().add(0, -0.05 * projectileType.gravityMultiplier(), 0)
.scale(projectileType.drag()));
setDeltaMovement(getDeltaMovement().add(0, -0.05 * type.gravityMultiplier(), 0)
.scale(type.drag()));
}
super.tick();
@ -186,9 +185,8 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
Vec3 hit = ray.getLocation();
Entity target = ray.getEntity();
PotatoCannonProjectileType projectileType = getProjectileType();
float damage = projectileType.damage() * additionalDamageMult;
float knockback = projectileType.knockback() + additionalKnockback;
float damage = type.damage() * additionalDamageMult;
float knockback = type.knockback() + additionalKnockback;
Entity owner = this.getOwner();
if (!target.isAlive())
@ -211,7 +209,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
if (target instanceof WitherBoss && ((WitherBoss) target).isPowered())
return;
if (projectileType.preEntityHit(stack, ray))
if (type.preEntityHit(stack, ray))
return;
boolean targetIsEnderman = target.getType() == EntityType.ENDERMAN;
@ -229,11 +227,11 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
if (targetIsEnderman)
return;
if (!projectileType.onEntityHit(stack, ray) && onServer) {
if (!type.onEntityHit(stack, ray) && onServer) {
if (random.nextDouble() <= recoveryChance) {
recoverItem();
} else {
spawnAtLocation(projectileType.dropStack());
spawnAtLocation(type.dropStack());
}
}
@ -297,7 +295,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
protected void onHitBlock(BlockHitResult ray) {
Vec3 hit = ray.getLocation();
pop(hit);
if (!getProjectileType().onBlockHit(level(), stack, ray) && !level().isClientSide) {
if (!type.onBlockHit(level(), stack, ray) && !level().isClientSide) {
if (random.nextDouble() <= recoveryChance) {
recoverItem();
} else {

View file

@ -6,19 +6,17 @@ import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.EnchantmentCategory;
public class PotatoRecoveryEnchantment extends Enchantment {
public PotatoRecoveryEnchantment(Rarity p_i46731_1_, EnchantmentCategory p_i46731_2_, EquipmentSlot[] p_i46731_3_) {
super(p_i46731_1_, p_i46731_2_, p_i46731_3_);
public PotatoRecoveryEnchantment(Rarity rarity, EnchantmentCategory category, EquipmentSlot[] slots) {
super(rarity, category, slots);
}
@Override
public int getMaxLevel() {
return 3;
}
@Override
public boolean canApplyAtEnchantingTable(ItemStack stack) {
return stack.getItem() instanceof PotatoCannonItem;
}
}

View file

@ -12,7 +12,7 @@ import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.AllTags.AllFluidTags;
import com.simibubi.create.Create;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.content.kinetics.fan.processing.HauntingRecipe.HauntingWrapper;
import com.simibubi.create.content.kinetics.fan.processing.SplashingRecipe.SplashingWrapper;
import com.simibubi.create.content.processing.burner.BlazeBurnerBlock;
@ -24,6 +24,7 @@ import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import net.createmod.catnip.math.VecHelper;
import net.createmod.catnip.theme.Color;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.DustParticleOptions;
@ -54,14 +55,10 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.RecipeWrapper;
import net.minecraftforge.registries.DeferredRegister;
public class AllFanProcessingTypes {
private static final DeferredRegister<FanProcessingType> REGISTER = DeferredRegister.create(CreateRegistries.FAN_PROCESSING_TYPE, Create.ID);
public static final BlastingType BLASTING = register("blasting", new BlastingType());
public static final HauntingType HAUNTING = register("haunting", new HauntingType());
public static final SmokingType SMOKING = register("smoking", new SmokingType());
@ -80,12 +77,10 @@ public class AllFanProcessingTypes {
}
private static <T extends FanProcessingType> T register(String name, T type) {
REGISTER.register(name, () -> type);
return type;
return Registry.register(CreateBuiltInRegistries.FAN_PROCESSING_TYPE, Create.asResource(name), type);
}
public static void register(IEventBus eventBus) {
REGISTER.register(eventBus);
public static void init() {
}
@Nullable

View file

@ -8,7 +8,7 @@ import org.apache.commons.lang3.mutable.MutableBoolean;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.content.kinetics.base.KineticBlockEntity;
import com.simibubi.create.content.kinetics.belt.BeltBlock;
import com.simibubi.create.content.kinetics.belt.BeltBlockEntity;
@ -36,6 +36,7 @@ import com.simibubi.create.foundation.item.SmartInventory;
import net.createmod.catnip.math.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.core.Vec3i;
import net.minecraft.world.Containers;
import net.minecraft.world.InteractionResultHolder;
@ -57,15 +58,11 @@ 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.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.wrapper.SidedInvWrapper;
import net.minecraftforge.registries.DeferredRegister;
public class AllArmInteractionPointTypes {
private static final DeferredRegister<ArmInteractionPointType> REGISTER = DeferredRegister.create(CreateRegistries.ARM_INTERACTION_POINT_TYPE, Create.ID);
static {
register("basin", new BasinType());
register("belt", new BeltType());
@ -87,11 +84,10 @@ public class AllArmInteractionPointTypes {
}
private static <T extends ArmInteractionPointType> void register(String name, T type) {
REGISTER.register(name, () -> type);
Registry.register(CreateBuiltInRegistries.ARM_INTERACTION_POINT_TYPE, Create.asResource(name), type);
}
public static void register(IEventBus eventBus) {
REGISTER.register(eventBus);
public static void init() {
}
//

View file

@ -26,7 +26,6 @@ import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.recipe.RecipeConditions;
import com.simibubi.create.foundation.recipe.RecipeFinder;
import com.simibubi.create.foundation.utility.AbstractBlockBreakQueue;
import com.simibubi.create.foundation.utility.TreeCutter;
import com.simibubi.create.infrastructure.config.AllConfigs;
import net.createmod.catnip.math.VecHelper;

View file

@ -10,7 +10,6 @@ import com.simibubi.create.content.contraptions.render.ContraptionMatrices;
import com.simibubi.create.content.kinetics.base.BlockBreakingMovementBehaviour;
import com.simibubi.create.foundation.damageTypes.CreateDamageSources;
import com.simibubi.create.foundation.utility.AbstractBlockBreakQueue;
import com.simibubi.create.foundation.utility.TreeCutter;
import com.simibubi.create.foundation.virtualWorld.VirtualRenderWorld;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
@ -25,6 +24,7 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.items.ItemHandlerHelper;

View file

@ -1,4 +1,4 @@
package com.simibubi.create.foundation.utility;
package com.simibubi.create.content.kinetics.saw;
import java.util.ArrayList;
import java.util.Collections;
@ -18,6 +18,7 @@ import com.simibubi.create.AllTags;
import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.compat.dynamictrees.DynamicTree;
import com.simibubi.create.foundation.utility.AbstractBlockBreakQueue;
import net.createmod.catnip.data.Iterate;
import net.minecraft.core.BlockPos;

View file

@ -3,11 +3,9 @@ package com.simibubi.create.content.logistics.item.filter.attribute;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import org.jetbrains.annotations.ApiStatus;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.Create;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.content.kinetics.fan.processing.AllFanProcessingTypes;
import com.simibubi.create.content.logistics.item.filter.attribute.attributes.AddedByAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.attributes.BookAuthorAttribute;
@ -25,6 +23,7 @@ import com.simibubi.create.content.logistics.item.filter.attribute.attributes.as
import com.simibubi.create.content.logistics.item.filter.attribute.attributes.astralsorcery.AstralSorceryPerkGemAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.legacydeserializers.AllItemAttributeLegacyDeserializers;
import net.minecraft.core.Registry;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
@ -38,14 +37,11 @@ import net.minecraft.world.level.block.ComposterBlock;
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.RecipeWrapper;
import net.minecraftforge.registries.DeferredRegister;
// TODO - Documentation
public class AllItemAttributeTypes {
private static final DeferredRegister<ItemAttributeType> REGISTER = DeferredRegister.create(CreateRegistries.ITEM_ATTRIBUTE_TYPE, Create.ID);
private static final RecipeWrapper RECIPE_WRAPPER = new RecipeWrapper(new ItemStackHandler(1));
public static final ItemAttributeType
@ -94,7 +90,6 @@ public class AllItemAttributeTypes {
.isPresent();
}
// TODO - Move away from stream()
private static boolean maxEnchanted(ItemStack s) {
return EnchantmentHelper.getEnchantments(s)
.entrySet()
@ -112,14 +107,10 @@ public class AllItemAttributeTypes {
}
private static ItemAttributeType register(String id, ItemAttributeType type) {
REGISTER.register(id, () -> type);
return type;
return Registry.register(CreateBuiltInRegistries.ITEM_ATTRIBUTE_TYPE, Create.asResource(id), type);
}
@ApiStatus.Internal
public static void register(IEventBus modEventBus) {
REGISTER.register(modEventBus);
public static void init() {
// Register legacy deserializers to maintain backwards compatability
AllItemAttributeLegacyDeserializers.register();
}

View file

@ -3,6 +3,7 @@ package com.simibubi.create.content.logistics.packagePort.frogport;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.api.equipment.goggles.IHaveHoveringInformation;
import com.simibubi.create.content.logistics.box.PackageItem;
import com.simibubi.create.content.logistics.box.PackageStyles;
@ -27,6 +28,8 @@ import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
@ -45,6 +48,7 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
public LerpedFloat animationProgress;
public LerpedFloat anticipationProgress;
public boolean currentlyDepositing;
public boolean goggles;
public boolean sendAnticipate;
@ -66,6 +70,7 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
manualOpenAnimationProgress = LerpedFloat.linear()
.startWithValue(0)
.chase(0, 0.35, Chaser.LINEAR);
goggles = false;
}
@Override
@ -127,7 +132,7 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
}
if (anticipationProgress.getValue() == 1)
anticipationProgress.updateChaseTarget(0);
anticipationProgress.startWithValue(0);
manualOpenAnimationProgress.updateChaseTarget(openTracker.openCount > 0 ? 1 : 0);
boolean wasOpen = manualOpenAnimationProgress.getValue() > 0;
@ -311,6 +316,8 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
}
if (failedLastExport)
NBTHelper.putMarker(tag, "FailedLastExport");
if (goggles)
NBTHelper.putMarker(tag, "Goggles");
}
@Override
@ -318,6 +325,7 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
super.read(tag, clientPacket);
passiveYaw = tag.getFloat("PlacedYaw");
failedLastExport = tag.getBoolean("FailedLastExport");
goggles = tag.getBoolean("Goggles");
if (!clientPacket)
animatedPackage = null;
if (tag.contains("AnimatedPackage")) {
@ -350,5 +358,23 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
if (level.isClientSide())
sounds.open(level, worldPosition);
}
@Override
public InteractionResult use(Player player) {
if (player == null)
return InteractionResult.PASS;
ItemStack mainHandItem = player.getMainHandItem();
if (!goggles && AllItems.GOGGLES.isIn(mainHandItem)) {
goggles = true;
if (!level.isClientSide()) {
notifyUpdate();
level.playSound(null, worldPosition, SoundEvents.ARMOR_EQUIP_GOLD, SoundSource.BLOCKS, 0.5f, 1.0f);
}
return InteractionResult.SUCCESS;
}
return super.use(player);
}
}

View file

@ -101,7 +101,7 @@ public class FrogportRenderer extends SmartBlockEntityRenderer<FrogportBlockEnti
.overlay(overlay)
.renderInto(ms, buffer.getBuffer(RenderType.cutoutMipped()));
SuperByteBuffer head = CachedBuffers.partial(AllPartialModels.FROGPORT_HEAD, blockEntity.getBlockState());
SuperByteBuffer head = CachedBuffers.partial(blockEntity.goggles ? AllPartialModels.FROGPORT_HEAD_GOGGLES : AllPartialModels.FROGPORT_HEAD, blockEntity.getBlockState());
head.center()
.rotateYDegrees(yaw)

View file

@ -23,7 +23,7 @@ import net.minecraftforge.registries.ForgeRegistries;
public class FrogportVisual extends AbstractBlockEntityVisual<FrogportBlockEntity> implements SimpleDynamicVisual {
private final TransformedInstance body;
private final TransformedInstance head;
private TransformedInstance head;
private final TransformedInstance tongue;
private final TransformedInstance rig;
private final TransformedInstance box;
@ -33,6 +33,7 @@ public class FrogportVisual extends AbstractBlockEntityVisual<FrogportBlockEntit
private float lastHeadPitch = Float.NaN;
private float lastTonguePitch = Float.NaN;
private float lastTongueLength = Float.NaN;
private boolean lastGoggles = false;
public FrogportVisual(VisualizationContext ctx, FrogportBlockEntity blockEntity, float partialTick) {
super(ctx, blockEntity, partialTick);
@ -69,6 +70,8 @@ public class FrogportVisual extends AbstractBlockEntityVisual<FrogportBlockEntit
}
private void animate(float partialTicks) {
updateGoggles();
float yaw = blockEntity.getYaw();
float headPitch = 80;
@ -172,6 +175,28 @@ public class FrogportVisual extends AbstractBlockEntityVisual<FrogportBlockEntit
}
}
public void updateGoggles() {
if (blockEntity.goggles && !lastGoggles) {
head.delete();
head = instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.FROGPORT_HEAD_GOGGLES))
.createInstance();
lastHeadPitch = -1;
updateLight(0);
lastGoggles = true;
}
if (!blockEntity.goggles && lastGoggles) {
head.delete();
head = instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.FROGPORT_HEAD))
.createInstance();
lastHeadPitch = -1;
updateLight(0);
lastGoggles = false;
}
}
private void renderPackage(Vec3 diff, float scale, float itemDistance) {
if (blockEntity.animatedPackage == null || scale < 0.45) {
rig.handle()

View file

@ -18,9 +18,11 @@ public abstract class PercentOrProgressBarDisplaySource extends NumericSingleLin
@Override
protected MutableComponent provideLine(DisplayLinkContext context, DisplayTargetStats stats) {
Float currentLevel = getProgress(context);
if (currentLevel == null)
Float rawProgress = this.getProgress(context);
if (rawProgress == null)
return EMPTY_LINE;
// clamp just in case - #7371
float currentLevel = Mth.clamp(rawProgress, 0, 1);
if (!progressBarActive(context))
return formatNumeric(context, currentLevel);

View file

@ -1,4 +1,4 @@
package com.simibubi.create.foundation.utility;
package com.simibubi.create.foundation;
import java.util.List;
@ -12,6 +12,7 @@ import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.registries.ForgeRegistries;
public class CreateNBTProcessors {
@ -29,7 +30,7 @@ public class CreateNBTProcessors {
if (!data.contains("Book", Tag.TAG_COMPOUND))
return data;
CompoundTag book = data.getCompound("Book");
// Writable books can't have click events, so they're safe to keep
ResourceLocation writableBookResource = ForgeRegistries.ITEMS.getKey(Items.WRITABLE_BOOK);
if (writableBookResource != null && book.getString("id").equals(writableBookResource.toString()))

View file

@ -2,6 +2,7 @@ package com.simibubi.create.foundation.events;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllPackets;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
@ -27,6 +28,7 @@ import com.simibubi.create.content.equipment.clipboard.ClipboardValueSettingsHan
import com.simibubi.create.content.equipment.extendoGrip.ExtendoGripRenderHandler;
import com.simibubi.create.content.equipment.goggles.GoggleOverlayRenderer;
import com.simibubi.create.content.equipment.hats.CreateHatArmorLayer;
import com.simibubi.create.content.equipment.potatoCannon.PotatoCannonItemRenderer;
import com.simibubi.create.content.equipment.toolbox.ToolboxHandlerClient;
import com.simibubi.create.content.equipment.zapper.ZapperItem;
import com.simibubi.create.content.equipment.zapper.terrainzapper.WorldshaperRenderHandler;
@ -71,11 +73,11 @@ import com.simibubi.create.foundation.utility.ServerSpeedProvider;
import com.simibubi.create.foundation.utility.TickBasedCache;
import com.simibubi.create.infrastructure.config.AllConfigs;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.config.ui.BaseConfigScreen;
import net.createmod.catnip.levelWrappers.WrappedClientLevel;
import net.createmod.catnip.render.DefaultSuperRenderTypeBuffer;
import net.createmod.catnip.render.SuperRenderTypeBuffer;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.levelWrappers.WrappedClientLevel;
import net.createmod.ponder.foundation.PonderTooltipHandler;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
@ -90,12 +92,14 @@ import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.ConfigScreenHandler;
import net.minecraftforge.client.event.ClientPlayerNetworkEvent;
import net.minecraftforge.client.event.EntityRenderersEvent;
import net.minecraftforge.client.event.RegisterClientReloadListenersEvent;
import net.minecraftforge.client.event.RegisterGuiOverlaysEvent;
import net.minecraftforge.client.event.RegisterItemDecorationsEvent;
import net.minecraftforge.client.event.RenderLevelStageEvent;
import net.minecraftforge.client.event.RenderLevelStageEvent.Stage;
import net.minecraftforge.client.event.ViewportEvent;
@ -370,6 +374,11 @@ public class ClientEvents {
event.registerAbove(VanillaGuiOverlay.HOTBAR.id(), "toolbox", ToolboxHandlerClient.OVERLAY);
}
@SubscribeEvent
public static void registerItemDecorations(RegisterItemDecorationsEvent event) {
event.register(AllItems.POTATO_CANNON, PotatoCannonItemRenderer.DECORATOR);
}
@SubscribeEvent
public static void onLoadComplete(FMLLoadCompleteEvent event) {
ModContainer createContainer = ModList.get()

View file

@ -1,4 +1,4 @@
package com.simibubi.create.foundation.utility;
package com.simibubi.create.foundation.model;
import java.util.ArrayList;
import java.util.List;
@ -17,6 +17,7 @@ import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.client.event.ModelEvent;
import net.minecraftforge.eventbus.api.IEventBus;

View file

@ -1,26 +0,0 @@
package com.simibubi.create.foundation.utility;
import net.minecraft.client.color.block.BlockColor;
import net.minecraft.client.color.item.ItemColor;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.world.level.GrassColor;
import net.minecraft.world.level.block.RedStoneWireBlock;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
public class ColorHandlers {
public static BlockColor getGrassyBlock() {
return (state, world, pos, layer) -> pos != null && world != null ? BiomeColors.getAverageGrassColor(world, pos)
: GrassColor.get(0.5D, 1.0D);
}
public static ItemColor getGrassyItem() {
return (stack, layer) -> GrassColor.get(0.5D, 1.0D);
}
public static BlockColor getRedstonePower() {
return (state, world, pos, layer) -> RedStoneWireBlock
.getColorForPower(pos != null && world != null ? state.getValue(BlockStateProperties.POWER) : 0);
}
}

View file

@ -0,0 +1,51 @@
package com.simibubi.create.foundation.utility;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.core.RegistryAccess;
import net.minecraft.server.MinecraftServer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.server.ServerLifecycleHooks;
public final class GlobalRegistryAccess {
private static Supplier<@Nullable RegistryAccess> supplier;
static {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> supplier = () -> {
ClientPacketListener packetListener = Minecraft.getInstance().getConnection();
if (packetListener == null) {
return null;
}
return packetListener.registryAccess();
});
if (supplier == null) {
supplier = () -> {
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server == null) {
return null;
}
return server.registryAccess();
};
}
}
@Nullable
public static RegistryAccess get() {
return supplier.get();
}
public static RegistryAccess getOrThrow() {
RegistryAccess registryAccess = get();
if (registryAccess == null) {
throw new IllegalStateException("Could not get RegistryAccess");
}
return registryAccess;
}
}

View file

@ -1,8 +0,0 @@
package com.simibubi.create.foundation.utility;
import net.minecraft.core.BlockPos;
@FunctionalInterface
public interface ICoordinate {
float get(BlockPos from);
}

View file

@ -1,4 +1,4 @@
package com.simibubi.create.foundation.utility;
package com.simibubi.create.infrastructure;
import static com.simibubi.create.AllBlocks.ADJUSTABLE_CHAIN_GEARSHIFT;
import static com.simibubi.create.AllBlocks.ANDESITE_ENCASED_SHAFT;
@ -65,6 +65,7 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

View file

@ -25,10 +25,10 @@ public class CreateContraptionTypeTagsProvider extends TagsProvider<ContraptionT
@Override
protected void addTags(Provider pProvider) {
tag(AllContraptionTypeTags.OPENS_CONTROLS.tag).add(
TagEntry.element(AllContraptionTypes.CARRIAGE.getId())
TagEntry.element(AllContraptionTypes.CARRIAGE.key().location())
);
tag(AllContraptionTypeTags.REQUIRES_VEHICLE_FOR_RENDER.tag).add(
TagEntry.element(AllContraptionTypes.MOUNTED.getId())
TagEntry.element(AllContraptionTypes.MOUNTED.key().location())
);
// VALIDATE

View file

@ -127,6 +127,9 @@ public class CreateRegistrateTags {
prov.tag(AllBlockTags.SUGAR_CANE_VARIANTS.tag)
.add(Blocks.SUGAR_CANE);
prov.tag(AllBlockTags.NON_HARVESTABLE.tag)
.add(Blocks.FIRE);
prov.tag(AllBlockTags.CORALS.tag)
.add(Blocks.DEAD_TUBE_CORAL, Blocks.DEAD_BRAIN_CORAL, Blocks.DEAD_BUBBLE_CORAL, Blocks.DEAD_FIRE_CORAL,
Blocks.DEAD_HORN_CORAL, Blocks.TUBE_CORAL, Blocks.BRAIN_CORAL, Blocks.BUBBLE_CORAL,

View file

@ -19,11 +19,13 @@ import net.minecraft.gametest.framework.GameTest;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.projectile.Arrow;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.CropBlock;
import net.minecraft.world.level.block.LeverBlock;
import net.minecraft.world.level.block.RedstoneLampBlock;
import net.minecraftforge.fluids.FluidStack;
@GameTestGroup(path = "contraptions")
@ -228,4 +230,48 @@ public class TestContraptions {
// public static void trainObserver(CreateGameTestHelper helper) {
// helper.fail("NYI");
// }
@GameTest(template = "dispensers_dont_fight")
public static void dispensersDontFight(CreateGameTestHelper helper) {
helper.pullLever(2, 3, 1);
BlockPos bottom = new BlockPos(6, 4, 1);
BlockPos top = new BlockPos(6, 6, 1);
BlockPos dispenser = new BlockPos(3, 4, 1);
helper.succeedWhen(() -> {
helper.assertEntitiesPresent(EntityType.ARROW, bottom, 3, 0);
helper.assertEntityNotPresent(EntityType.ARROW, top);
helper.assertBlockPresent(Blocks.DISPENSER, dispenser);
helper.assertContainerContains(dispenser, new ItemStack(Items.ARROW, 2));
});
}
@GameTest(template = "dispensers_refill")
public static void dispensersRefill(CreateGameTestHelper helper) {
BlockPos lever = new BlockPos(2, 3, 1);
helper.pullLever(lever);
BlockPos barrel = lever.above();
BlockPos dispenser = barrel.east();
helper.succeedWhen(() -> {
helper.assertBlockPresent(Blocks.DISPENSER, dispenser);
helper.assertContainerContains(dispenser, new ItemStack(Items.SPECTRAL_ARROW, 2));
helper.assertContainerEmpty(barrel);
});
}
@GameTest(template = "vaults_protect_fuel")
public static void vaultsProtectFuel(CreateGameTestHelper helper) {
BlockPos lever = new BlockPos(2, 2, 1);
helper.pullLever(lever);
BlockPos barrelLamp = new BlockPos(1, 3, 3);
BlockPos vaultLamp = barrelLamp.east(2);
helper.runAtTickTime(10, () -> helper.pullLever(lever));
helper.succeedWhen(() -> {
helper.assertBlockProperty(barrelLamp, RedstoneLampBlock.LIT, false);
helper.assertBlockProperty(vaultLamp, RedstoneLampBlock.LIT, true);
});
}
}

View file

@ -4,6 +4,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.mutable.MutableBoolean;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.content.fluids.hosePulley.HosePulleyFluidHandler;
import com.simibubi.create.content.fluids.pipes.valve.FluidValveBlock;
@ -18,15 +20,22 @@ import net.minecraft.gametest.framework.GameTest;
import net.minecraft.gametest.framework.GameTestAssertException;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.monster.Zombie;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.alchemy.PotionUtils;
import net.minecraft.world.item.alchemy.Potions;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.FarmBlock;
import net.minecraft.world.level.block.LeverBlock;
import net.minecraft.world.level.block.RedstoneLampBlock;
import net.minecraft.world.level.material.Fluids;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidType;
import net.minecraftforge.fluids.capability.IFluidHandler;
@ -299,4 +308,59 @@ public class TestFluids {
}
});
}
@GameTest(template = "open_pipes")
public static void openPipes(CreateGameTestHelper helper) {
BlockPos effects = new BlockPos(2, 4, 2);
BlockPos removers = new BlockPos(3, 5, 2);
BlockPos firstSeat = new BlockPos(4, 2, 1);
BlockPos secondSeat = firstSeat.south(2);
Zombie firstZombie = helper.spawn(EntityType.ZOMBIE, firstSeat);
Zombie secondZombie = helper.spawn(EntityType.ZOMBIE, secondSeat);
helper.pullLever(effects);
MutableBoolean stage1 = new MutableBoolean(true);
helper.succeedWhen(() -> {
if (stage1.booleanValue()) {
helper.assertTrue(firstZombie.isOnFire(), "not ignited");
helper.assertFalse(secondZombie.getActiveEffects().isEmpty(), "no effects");
// success, stage 2 time
stage1.setFalse();
helper.pullLever(effects);
helper.pullLever(removers);
helper.fail("switching stages");
} else {
helper.assertFalse(firstZombie.isOnFire(), "not extinguished");
helper.assertTrue(secondZombie.getActiveEffects().isEmpty(), "has effects");
// all done
}
});
}
@GameTest(template = "spouting", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
public static void spouting(CreateGameTestHelper helper) {
BlockPos farmland = new BlockPos(3, 2, 3);
BlockPos depot = new BlockPos(5, 2, 1);
helper.pullLever(2, 3, 2);
ItemStack waterBottle = PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.WATER);
helper.succeedWhen(() -> {
// lava
helper.assertBlockPresent(Blocks.LAVA_CAULDRON, 3, 2, 1);
// water
helper.assertBlockProperty(farmland, FarmBlock.MOISTURE, 7);
helper.assertBlockPresent(Blocks.MUD, farmland.east(1));
helper.assertBlockPresent(Blocks.MUD, farmland.east(2));
helper.assertBlockPresent(Blocks.MUD, farmland.east(3));
helper.assertBlockPresent(Blocks.WATER_CAULDRON, farmland.east(4));
helper.assertContainerContains(depot, Items.WATER_BUCKET);
helper.assertContainerContains(depot.east(1), waterBottle);
helper.assertContainerContains(depot.east(2), Items.GRASS_BLOCK);
});
}
}

View file

@ -31,6 +31,7 @@ import net.minecraft.world.item.enchantment.EnchantmentInstance;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.RedstoneLampBlock;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.registries.ForgeRegistries;
@ -390,4 +391,22 @@ public class TestItems {
helper.assertNixiePower(halfPearlNixie, 8);
});
}
@GameTest(template = "fan_processing", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
public static void fanProcessing(CreateGameTestHelper helper) {
// why does the redstone explode
BlockPos.betweenClosed(new BlockPos(2, 7, 3), new BlockPos(11, 7, 3)).forEach(
pos -> helper.setBlock(pos, Blocks.REDSTONE_WIRE)
);
helper.pullLever(1, 7, 3);
List<BlockPos> lamps = List.of(
new BlockPos(1, 2, 1), new BlockPos(5, 2, 1), new BlockPos(7, 2, 1),
new BlockPos(9, 2, 1), new BlockPos(11, 2, 1)
);
helper.succeedWhen(() -> {
for (BlockPos lamp : lamps) {
helper.assertBlockProperty(lamp, RedstoneLampBlock.LIT, true);
}
});
}
}

View file

@ -0,0 +1,130 @@
{
"credit": "Made with Blockbench",
"textures": {
"0": "create:block/port2",
"1": "create:block/port",
"2": "create:block/froggles",
"particle": "create:block/port2"
},
"render_type": "minecraft:cutout",
"elements": [
{
"from": [2, 9, 2],
"to": [14, 14, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 9, 11]},
"faces": {
"north": {"uv": [0, 0, 6, 2.5], "texture": "#1"},
"east": {"uv": [12, 0, 6, 2.5], "texture": "#1"},
"south": {"uv": [6, 10, 12, 12.5], "texture": "#1"},
"west": {"uv": [6, 0, 12, 2.5], "texture": "#1"},
"up": {"uv": [0, 10, 6, 16], "rotation": 180, "texture": "#1"}
}
},
{
"from": [14, 9, 2],
"to": [2, 14, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [20, 10, 11]},
"faces": {
"north": {"uv": [0, 0, 6, 2.5], "texture": "#1"},
"east": {"uv": [12, 0, 6, 2.5], "texture": "#1"},
"south": {"uv": [6, 10, 12, 12.5], "texture": "#1"},
"west": {"uv": [6, 0, 12, 2.5], "texture": "#1"},
"up": {"uv": [0, 10, 6, 16], "rotation": 180, "texture": "#1"}
}
},
{
"from": [2, 10, 2],
"to": [14, 10, 11],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 10, 11]},
"faces": {
"down": {"uv": [0, 0, 6, 4.5], "rotation": 180, "texture": "#0"}
}
},
{
"from": [1.8, 11, 1.8],
"to": [5, 14.2, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [2, 11, 2]},
"faces": {
"north": {"uv": [4.5, 0, 6, 1.5], "texture": "#1"},
"east": {"uv": [8, 5.5, 9.5, 7], "texture": "#1"},
"south": {"uv": [8, 5.5, 9.5, 7], "texture": "#1"},
"west": {"uv": [6, 0, 7.5, 1.5], "texture": "#1"},
"up": {"uv": [4.5, 14.5, 6, 16], "rotation": 180, "texture": "#1"},
"down": {"uv": [8, 5.5, 9.5, 7], "texture": "#1"}
}
},
{
"from": [11, 11, 1.8],
"to": [14.2, 14.2, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [11, 11, 2]},
"faces": {
"north": {"uv": [6, 0, 4.5, 1.5], "texture": "#1"},
"east": {"uv": [7.5, 0, 6, 1.5], "texture": "#1"},
"south": {"uv": [8, 5.5, 9.5, 7], "texture": "#1"},
"west": {"uv": [8, 5.5, 9.5, 7], "texture": "#1"},
"up": {"uv": [4.5, 14.5, 6, 16], "rotation": 270, "texture": "#1"},
"down": {"uv": [8, 5.5, 9.5, 7], "texture": "#1"}
}
},
{
"name": "lense ext",
"from": [1.6, 10, 1.6],
"to": [14.4, 14.4, 6],
"rotation": {"angle": 0, "axis": "y", "origin": [2, 10, 2]},
"faces": {
"north": {"uv": [0, 12, 12, 16], "texture": "#2"},
"east": {"uv": [16, 12, 12, 16], "texture": "#2"},
"west": {"uv": [12, 12, 16, 16], "texture": "#2"},
"up": {"uv": [12, 12, 0, 8], "texture": "#2"}
}
},
{
"name": "lense int",
"from": [14.4, 10, 1.6],
"to": [1.6, 14.4, 6],
"rotation": {"angle": 0, "axis": "y", "origin": [15, 10, 2]},
"faces": {
"north": {"uv": [0, 12, 12, 16], "texture": "#2"},
"east": {"uv": [16, 12, 12, 16], "texture": "#2"},
"west": {"uv": [12, 12, 16, 16], "texture": "#2"},
"up": {"uv": [12, 12, 0, 8], "texture": "#2"}
}
},
{
"name": "strap ext",
"from": [1.6, 10, 6],
"to": [14.4, 14.4, 14.4],
"rotation": {"angle": 0, "axis": "y", "origin": [2, 10, 6]},
"faces": {
"east": {"uv": [8, 0, 0, 4], "texture": "#2"},
"south": {"uv": [0, 4, 12, 8], "texture": "#2"},
"west": {"uv": [0, 0, 8, 4], "texture": "#2"}
}
},
{
"name": "strap int",
"from": [14.4, 10, 6],
"to": [1.6, 14.4, 14.4],
"rotation": {"angle": 0, "axis": "y", "origin": [15, 10, 6]},
"faces": {
"east": {"uv": [8, 0, 0, 4], "texture": "#2"},
"south": {"uv": [12, 4, 0, 8], "texture": "#2"},
"west": {"uv": [0, 0, 8, 4], "texture": "#2"}
}
}
],
"groups": [
{
"name": "port_head",
"origin": [8, 8, 8],
"color": 0,
"children": [0, 1, 2, 3, 4]
},
{
"name": "froggles",
"origin": [8, 8, 8],
"color": 0,
"children": [5, 6, 7, 8]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B