Merge branch 'mc1.20.1/feature-dev' into mc1.21.1/dev

This commit is contained in:
IThundxr 2025-02-26 16:33:38 -05:00
commit ccf3f17434
Failed to generate hash of commit
79 changed files with 3875 additions and 3439 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

@ -1,4 +1,4 @@
// 1.21.1 2025-02-23T21:05:27.700364514 Create's Generated Registry Entries
// 1.20.1 2025-02-24T12:59:18.4372587 Create's Generated Registry Entries
abb9dd2c98388abb430343d4ac02d460e5edc086 data/create/create/potato_projectile/type/apple.json
1a20cb4e64dc07918a28fbccc73102bca3560f16 data/create/create/potato_projectile/type/baked_potato.json
8450576952f7723d22250da6f554dd0fda4e9fce data/create/create/potato_projectile/type/beetroot.json
@ -7,7 +7,7 @@ abb9dd2c98388abb430343d4ac02d460e5edc086 data/create/create/potato_projectile/ty
22e0f5befe5835c7a9a6448e17e73e6fb7d0fb9c data/create/create/potato_projectile/type/carrot.json
af17d72549ee9421d2643a215ffc2291874fff93 data/create/create/potato_projectile/type/chocolate_berry.json
8cb4811601d46db5fdc0291edce3ec2f3a64e902 data/create/create/potato_projectile/type/chorus_fruit.json
69138506c1b02aa4e2456cc1912ddb70f8a1e42a data/create/create/potato_projectile/type/enchanted_golden_apple.json
9dd1298993a4657d69a9d2ac4de3ad7879d9aa8d data/create/create/potato_projectile/type/enchanted_golden_apple.json
e3ab3c4bd70ad742d37091ae05774aadffb1d1d5 data/create/create/potato_projectile/type/fallback.json
98bf0ce51484c5a9709fc69d046d0c70d987cfdd data/create/create/potato_projectile/type/fish.json
afe4e7cc87110d6f045f6672fb312a09290ca04a data/create/create/potato_projectile/type/glistering_melon.json
@ -19,10 +19,10 @@ c6234751ccad5111857b0ae9e297e42f529ad32a data/create/create/potato_projectile/ty
352402ba2fff3f23af178d15d61b0ba0a151097a data/create/create/potato_projectile/type/melon_slice.json
cbfd3028ad878bddbdaeb0eec0cd5a1c50d4c901 data/create/create/potato_projectile/type/poison_potato.json
b58e779803d29d4b36ecf79fde4e918577c71748 data/create/create/potato_projectile/type/potato.json
3ba8dbd239452eb2531d8f13332a37bc1ffa3616 data/create/create/potato_projectile/type/pufferfish.json
d15070ca23129e7d4025d86cb4be5701a466e593 data/create/create/potato_projectile/type/pufferfish.json
0c099b5f1bd7ba3832ea2d29e8c1c22c83ed1c3d data/create/create/potato_projectile/type/pumpkin_block.json
7d58a0671c55e40cdb728c60152665c89209fb91 data/create/create/potato_projectile/type/pumpkin_pie.json
30bf0b35aeff136251d3bfe2af04fd89ccf953de data/create/create/potato_projectile/type/suspicious_stew.json
0e73e50b02e9afe5f170729935ae5921d22d7091 data/create/create/potato_projectile/type/suspicious_stew.json
b4a7c10bd9cdcfe67df2a5766e5ec2e35a3f9b24 data/create/create/potato_projectile/type/sweet_berry.json
030ede1044384c4117ac1e491bf5c78bbd2842f5 data/create/damage_type/crush.json
92b0416950ffeb3ba68811e587177c2f8811c2c5 data/create/damage_type/cuckoo_surprise.json
@ -33,11 +33,9 @@ afdc8574e8e915dac5693f368e9c1260714ad111 data/create/damage_type/mechanical_roll
43cfce97a9326a1a7ce95f91a052586610b9686c data/create/damage_type/mechanical_saw.json
58ab7a8eed0841f05b4b1373ba159e3e6503b7c3 data/create/damage_type/potato_cannon.json
4dae2c0fc9fe6749649ef06dd3b7baaa9bd5e807 data/create/damage_type/run_over.json
eefc270acd718dc889a9a15e350cee5b9c673dfa data/create/enchantment/capacity.json
d7ba49f29711546a65861c8140004d97fa09f16b data/create/enchantment/potato_recovery.json
95c3416ba53bcedc4e8aae5d2e9fffb10e4f7d10 data/create/neoforge/biome_modifier/striated_ores_nether.json
422b87075cee548e3d215822f546a06c89aaf7f4 data/create/neoforge/biome_modifier/striated_ores_overworld.json
3bc0da630715b1fc7d796e1592dd15e0f36951a5 data/create/neoforge/biome_modifier/zinc_ore.json
f650826db97d0562230ae0bd965ccbad4c696eff data/create/forge/biome_modifier/striated_ores_nether.json
d401f270f1ba0e5d6940aeb8f72ebb3a02dd055e data/create/forge/biome_modifier/striated_ores_overworld.json
ed82e4fd3dba36cf2fd81a196cc6670ae0c6da9f data/create/forge/biome_modifier/zinc_ore.json
ed83634a4af759ef80cfd1101f5715d38268620f data/create/worldgen/configured_feature/striated_ores_nether.json
39576de9dc1368287a96a8627bbdef34954242e0 data/create/worldgen/configured_feature/striated_ores_overworld.json
fbf6999fb922a0770cfbea3a8bddc11ba8b1552b data/create/worldgen/configured_feature/zinc_ore.json

View file

@ -1072,6 +1072,7 @@
"create.display_source.nixie_tube": "sǝqn⟘ ǝıxıN ʎdoƆ",
"create.display_source.observed_train_name": ɯɐN uıɐɹ⟘ pǝʇɔǝʇǝᗡ",
"create.display_source.player_deaths": "sɥʇɐǝᗡ ɹǝʎɐןԀ",
"create.display_source.read_package_address": "ssǝɹppⱯ ǝbɐʞɔɐԀ pɐǝᴚ",
"create.display_source.redstone_power": "ɹǝʍoԀ ǝuoʇspǝᴚ",
"create.display_source.redstone_power.display": "ʇɐɯɹoℲ ʎɐןdsıᗡ",
"create.display_source.redstone_power.number": "ɹǝqɯnN",

View file

@ -1072,6 +1072,7 @@
"create.display_source.nixie_tube": "Copy Nixie Tubes",
"create.display_source.observed_train_name": "Detected Train Name",
"create.display_source.player_deaths": "Player Deaths",
"create.display_source.read_package_address": "Read Package Address",
"create.display_source.redstone_power": "Redstone Power",
"create.display_source.redstone_power.display": "Display Format",
"create.display_source.redstone_power.number": "Number",

View file

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

View file

@ -270,7 +270,6 @@ 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.mixin.accessor.BlockLootSubProviderAccessor;
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;
@ -299,6 +298,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;
@ -1265,7 +1265,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())))
@ -1801,6 +1802,7 @@ public class AllBlocks {
.transform(displaySource(AllDisplaySources.LIST_ITEMS))
.transform(displaySource(AllDisplaySources.COUNT_FLUIDS))
.transform(displaySource(AllDisplaySources.LIST_FLUIDS))
.transform(displaySource(AllDisplaySources.READ_PACKAGE_ADDRESS))
.lang("Smart Observer")
.item()
.transform(customItemModel("_", "block"))

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,35 +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.minecraft.core.Holder;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredRegister;
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 Holder<ContraptionType> PISTON = register("piston", PistonContraption::new);
public static final Holder<ContraptionType> BEARING = register("bearing", BearingContraption::new);
public static final Holder<ContraptionType> PULLEY = register("pulley", PulleyContraption::new);
public static final Holder<ContraptionType> CLOCKWORK = register("clockwork", ClockworkContraption::new);
public static final Holder<ContraptionType> MOUNTED = register("mounted", MountedContraption::new);
public static final Holder<ContraptionType> STABILIZED = register("stabilized", StabilizedContraption::new);
public static final Holder<ContraptionType> GANTRY = register("gantry", GantryContraption::new);
public static final Holder<ContraptionType> CARRIAGE = register("carriage", CarriageContraption::new);
public static final Holder<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 Holder<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

@ -28,6 +28,7 @@ import com.simibubi.create.content.redstone.displayLink.source.KineticSpeedDispl
import com.simibubi.create.content.redstone.displayLink.source.KineticStressDisplaySource;
import com.simibubi.create.content.redstone.displayLink.source.NixieTubeDisplaySource;
import com.simibubi.create.content.redstone.displayLink.source.ObservedTrainNameSource;
import com.simibubi.create.content.redstone.displayLink.source.PackageAddressDisplaySource;
import com.simibubi.create.content.redstone.displayLink.source.RedstonePowerDisplaySource;
import com.simibubi.create.content.redstone.displayLink.source.ScoreboardDisplaySource;
import com.simibubi.create.content.redstone.displayLink.source.StationSummaryDisplaySource;
@ -83,6 +84,7 @@ public class AllDisplaySources {
public static final RegistryEntry<DisplaySource, ItemListDisplaySource> LIST_ITEMS = simple("list_items", ItemListDisplaySource::new);
public static final RegistryEntry<DisplaySource, FluidAmountDisplaySource> COUNT_FLUIDS = simple("count_fluids", FluidAmountDisplaySource::new);
public static final RegistryEntry<DisplaySource, FluidListDisplaySource> LIST_FLUIDS = simple("list_fluids", FluidListDisplaySource::new);
public static final RegistryEntry<DisplaySource, PackageAddressDisplaySource> READ_PACKAGE_ADDRESS = simple("read_package_address", PackageAddressDisplaySource::new);
public static final RegistryEntry<DisplaySource, ComputerDisplaySource> COMPUTER = REGISTRATE.displaySource("computer", ComputerDisplaySource::new)
.onRegisterAfter(Registries.BLOCK_ENTITY_TYPE, source -> {

View file

@ -408,6 +408,7 @@ public class AllItems {
public static final ItemEntry<PotatoCannonItem> POTATO_CANNON =
REGISTRATE.item("potato_cannon", PotatoCannonItem::new)
.properties(p -> p.durability(100))
.model(AssetLookup.itemModelWithPartials())
.tag(Tags.Items.ENCHANTABLES)
.register();

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

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

View file

@ -2,6 +2,8 @@ package com.simibubi.create;
import java.util.Random;
import com.simibubi.create.foundation.recipe.AllIngredients;
import org.slf4j.Logger;
import com.google.gson.Gson;
@ -28,14 +30,13 @@ 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.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.recipe.AllIngredients;
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,10 +138,6 @@ public class Create {
AllConfigs.register(modLoadingContext, modContainer);
// TODO - Make these use Registry.register and move them into the RegisterEvent
AllArmInteractionPointTypes.register(modEventBus);
AllFanProcessingTypes.register(modEventBus);
AllItemAttributeTypes.register(modEventBus);
AllContraptionTypes.register(modEventBus);
AllPackagePortTargetTypes.register(modEventBus);
// FIXME: some of these registrations are not thread-safe
@ -183,6 +180,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

@ -21,9 +21,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;
@ -43,7 +43,6 @@ import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.neoforge.common.NeoForge;
@ -67,12 +66,12 @@ public class CreateClient {
public static final ClientResourceReloadListener RESOURCE_RELOAD_LISTENER = new ClientResourceReloadListener();
public CreateClient(IEventBus modEventBus) {
public CreateClient(net.neoforged.bus.api.IEventBus modEventBus) {
onCtorClient(modEventBus);
}
public static void onCtorClient(IEventBus modEventBus) {
IEventBus neoEventBus = NeoForge.EVENT_BUS;
public static void onCtorClient(net.neoforged.bus.api.IEventBus modEventBus) {
net.neoforged.bus.api.IEventBus neoEventBus = NeoForge.EVENT_BUS;
modEventBus.addListener(CreateClient::clientInit);
modEventBus.addListener(AllParticleTypes::registerFactories);

View file

@ -4,26 +4,23 @@ 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;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.Billboard;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileRenderModes.Billboard;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileRenderModes.TowardMotion;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileRenderModes.Tumble;
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.BootstrapContext;
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;
@ -53,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) {
@ -94,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;
@ -112,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;
@ -172,17 +149,17 @@ public record PotatoCannonProjectileType(HolderSet<Item> items, int reloadTicks,
}
public Builder renderBillboard() {
renderMode(PotatoProjectileRenderMode.Billboard.INSTANCE);
renderMode(Billboard.INSTANCE);
return this;
}
public Builder renderTumbling() {
renderMode(PotatoProjectileRenderMode.Tumble.INSTANCE);
renderMode(Tumble.INSTANCE);
return this;
}
public Builder renderTowardMotion(int spriteAngle, float spin) {
renderMode(new PotatoProjectileRenderMode.TowardMotion(spriteAngle, spin));
renderMode(new TowardMotion(spriteAngle, spin));
return this;
}
@ -207,8 +184,8 @@ public record PotatoCannonProjectileType(HolderSet<Item> items, int reloadTicks,
return this;
}
public void register(BootstrapContext<PotatoCannonProjectileType> ctx) {
PotatoCannonProjectileType type = new PotatoCannonProjectileType(
public PotatoCannonProjectileType build() {
return new PotatoCannonProjectileType(
HolderSet.direct(items),
reloadTicks,
damage,
@ -225,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(BootstrapContext<PotatoCannonProjectileType> ctx, ItemLike... items) {
addItems(items);
register(ctx);
}
}
}

View file

@ -4,23 +4,12 @@ import java.util.function.Function;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.foundation.mixin.accessor.FallingBlockEntityAccessor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.BlockHitResult;
import net.neoforged.neoforge.common.SpecialPlantable;
public interface PotatoProjectileBlockHitAction {
Codec<PotatoProjectileBlockHitAction> CODEC = CreateBuiltInRegistries.POTATO_PROJECTILE_BLOCK_HIT_ACTION.byNameCodec()
.dispatch(PotatoProjectileBlockHitAction::codec, Function.identity());
@ -28,89 +17,4 @@ public interface PotatoProjectileBlockHitAction {
boolean execute(LevelAccessor level, ItemStack projectile, BlockHitResult ray);
MapCodec<? extends PotatoProjectileBlockHitAction> codec();
record PlantCrop(Holder<Block> cropBlock) implements PotatoProjectileBlockHitAction {
public static final MapCodec<PlantCrop> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
BuiltInRegistries.BLOCK.holderByNameCodec().fieldOf("block").forGetter(PlantCrop::cropBlock)
).apply(instance, PlantCrop::new));
public PlantCrop(Block cropBlock) {
this(cropBlock.builtInRegistryHolder());
}
@Override
public boolean execute(LevelAccessor level, ItemStack projectile, BlockHitResult ray) {
if (level.isClientSide())
return true;
BlockPos hitPos = ray.getBlockPos();
if (level instanceof Level l && !l.isLoaded(hitPos))
return true;
Direction face = ray.getDirection();
if (face != Direction.UP)
return false;
BlockPos placePos = hitPos.relative(face);
if (!level.getBlockState(placePos)
.canBeReplaced())
return false;
if (!(cropBlock.value() instanceof SpecialPlantable specialPlantable))
return false;
if (specialPlantable.canPlacePlantAtPosition(projectile, level, placePos, null))
specialPlantable.spawnPlantAtPosition(projectile, level, placePos, null);
return true;
}
@Override
public MapCodec<? extends PotatoProjectileBlockHitAction> codec() {
return CODEC;
}
}
record PlaceBlockOnGround(Holder<Block> block) implements PotatoProjectileBlockHitAction {
public static final MapCodec<PlaceBlockOnGround> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
BuiltInRegistries.BLOCK.holderByNameCodec().fieldOf("block").forGetter(PlaceBlockOnGround::block)
).apply(instance, PlaceBlockOnGround::new));
public PlaceBlockOnGround(Block block) {
this(block.builtInRegistryHolder());
}
@Override
public boolean execute(LevelAccessor levelAccessor, ItemStack projectile, BlockHitResult ray) {
if (levelAccessor.isClientSide())
return true;
BlockPos hitPos = ray.getBlockPos();
if (levelAccessor instanceof Level l && !l.isLoaded(hitPos))
return true;
Direction face = ray.getDirection();
BlockPos placePos = hitPos.relative(face);
if (!levelAccessor.getBlockState(placePos)
.canBeReplaced())
return false;
if (face == Direction.UP) {
levelAccessor.setBlock(placePos, block.value()
.defaultBlockState(), 3);
} else if (levelAccessor instanceof Level level) {
double y = ray.getLocation().y - 0.5;
if (!level.isEmptyBlock(placePos.above()))
y = Math.min(y, placePos.getY());
if (!level.isEmptyBlock(placePos.below()))
y = Math.max(y, placePos.getY());
FallingBlockEntity falling = FallingBlockEntityAccessor.create$callInit(level, placePos.getX() + 0.5, y,
placePos.getZ() + 0.5, block.value().defaultBlockState());
falling.time = 1;
level.addFreshEntity(falling);
}
return true;
}
@Override
public MapCodec<? extends PotatoProjectileBlockHitAction> codec() {
return CODEC;
}
}
}

View file

@ -1,47 +1,13 @@
package com.simibubi.create.api.equipment.potatoCannon;
import java.util.UUID;
import java.util.function.Function;
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.foundation.codec.CreateCodecs;
import net.createmod.catnip.data.WorldAttached;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.animal.Fox;
import net.minecraft.world.entity.monster.ZombieVillager;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.food.FoodProperties.PossibleEffect;
import net.minecraft.world.food.Foods;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.SuspiciousStewEffects;
import net.minecraft.world.item.component.SuspiciousStewEffects.Entry;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.common.util.FakePlayer;
import net.neoforged.neoforge.event.EventHooks;
import net.neoforged.neoforge.event.entity.EntityTeleportEvent;
public interface PotatoProjectileEntityHitAction {
Codec<PotatoProjectileEntityHitAction> CODEC = CreateBuiltInRegistries.POTATO_PROJECTILE_ENTITY_HIT_ACTION.byNameCodec()
@ -59,194 +25,4 @@ public interface PotatoProjectileEntityHitAction {
boolean execute(ItemStack projectile, EntityHitResult ray, Type type);
MapCodec<? extends PotatoProjectileEntityHitAction> codec();
record SetOnFire(int ticks) implements PotatoProjectileEntityHitAction {
public static final MapCodec<SetOnFire> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ExtraCodecs.POSITIVE_INT.fieldOf("ticks").forGetter(SetOnFire::ticks)
).apply(instance, SetOnFire::new));
public static SetOnFire seconds(int seconds) {
return new SetOnFire(seconds * 20);
}
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
ray.getEntity()
.setRemainingFireTicks(ticks);
return false;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
record PotionEffect(Holder<MobEffect> effect, int level, int ticks,
boolean recoverable) implements PotatoProjectileEntityHitAction {
public static final MapCodec<PotionEffect> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
BuiltInRegistries.MOB_EFFECT.holderByNameCodec().fieldOf("effect").forGetter(PotionEffect::effect),
ExtraCodecs.POSITIVE_INT.fieldOf("level").forGetter(PotionEffect::level),
ExtraCodecs.POSITIVE_INT.fieldOf("ticks").forGetter(PotionEffect::ticks),
Codec.BOOL.fieldOf("recoverable").forGetter(PotionEffect::recoverable)
).apply(instance, PotionEffect::new));
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
if (entity.level().isClientSide)
return true;
if (entity instanceof LivingEntity)
applyEffect((LivingEntity) entity, new MobEffectInstance(effect, ticks, level - 1));
return !recoverable;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
record FoodEffects(FoodProperties foodProperty, boolean recoverable) implements PotatoProjectileEntityHitAction {
public static final MapCodec<FoodEffects> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
FoodProperties.DIRECT_CODEC.fieldOf("food_property").forGetter(FoodEffects::foodProperty),
Codec.BOOL.fieldOf("recoverable").forGetter(FoodEffects::recoverable)
).apply(instance, FoodEffects::new));
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
if (entity.level().isClientSide)
return true;
if (entity instanceof LivingEntity livingEntity) {
for (PossibleEffect possibleEffect : foodProperty.effects()) {
if (livingEntity.getRandom().nextFloat() < possibleEffect.probability())
applyEffect(livingEntity, new MobEffectInstance(possibleEffect.effect()));
}
}
return !recoverable;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
record ChorusTeleport(double teleportDiameter) implements PotatoProjectileEntityHitAction {
public static final MapCodec<ChorusTeleport> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
CreateCodecs.POSITIVE_DOUBLE.fieldOf("teleport_diameter").forGetter(ChorusTeleport::teleportDiameter)
).apply(instance, ChorusTeleport::new));
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
Level level = entity.getCommandSenderWorld();
if (level.isClientSide)
return true;
if (!(entity instanceof LivingEntity livingEntity))
return false;
double entityX = livingEntity.getX();
double entityY = livingEntity.getY();
double entityZ = livingEntity.getZ();
for (int teleportTry = 0; teleportTry < 16; ++teleportTry) {
double teleportX = entityX + (livingEntity.getRandom()
.nextDouble() - 0.5D) * teleportDiameter;
double teleportY = Mth.clamp(entityY + (livingEntity.getRandom()
.nextInt((int) teleportDiameter) - (int) (teleportDiameter / 2)), 0.0D, level.getHeight() - 1);
double teleportZ = entityZ + (livingEntity.getRandom()
.nextDouble() - 0.5D) * teleportDiameter;
EntityTeleportEvent.ChorusFruit event =
EventHooks.onChorusFruitTeleport(livingEntity, teleportX, teleportY, teleportZ);
if (event.isCanceled())
return false;
if (livingEntity.randomTeleport(event.getTargetX(), event.getTargetY(), event.getTargetZ(), true)) {
if (livingEntity.isPassenger())
livingEntity.stopRiding();
SoundEvent soundevent =
livingEntity instanceof Fox ? SoundEvents.FOX_TELEPORT : SoundEvents.CHORUS_FRUIT_TELEPORT;
level.playSound(null, entityX, entityY, entityZ, soundevent, SoundSource.PLAYERS, 1.0F, 1.0F);
livingEntity.playSound(soundevent, 1.0F, 1.0F);
livingEntity.setDeltaMovement(Vec3.ZERO);
return true;
}
}
return false;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
enum CureZombieVillager implements PotatoProjectileEntityHitAction {
INSTANCE;
private static final FoodEffects EFFECT = new FoodEffects(Foods.GOLDEN_APPLE, false);
private static final GameProfile ZOMBIE_CONVERTER_NAME =
new GameProfile(UUID.fromString("be12d3dc-27d3-4992-8c97-66be53fd49c5"), "Converter");
private static final WorldAttached<FakePlayer> ZOMBIE_CONVERTERS =
new WorldAttached<>(w -> new FakePlayer((ServerLevel) w, ZOMBIE_CONVERTER_NAME));
public static final MapCodec<CureZombieVillager> CODEC = MapCodec.unit(INSTANCE);
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
Level world = entity.level();
if (!(entity instanceof ZombieVillager zombieVillager) || !zombieVillager.hasEffect(MobEffects.WEAKNESS))
return EFFECT.execute(projectile, ray, type);
if (world.isClientSide)
return false;
FakePlayer dummy = ZOMBIE_CONVERTERS.get(world);
dummy.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.GOLDEN_APPLE, 1));
zombieVillager.mobInteract(dummy, InteractionHand.MAIN_HAND);
return true;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
enum SuspiciousStew implements PotatoProjectileEntityHitAction {
INSTANCE;
public static final MapCodec<SuspiciousStew> CODEC = MapCodec.unit(INSTANCE);
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
if (ray.getEntity() instanceof LivingEntity livingEntity) {
SuspiciousStewEffects effects = projectile.getOrDefault(DataComponents.SUSPICIOUS_STEW_EFFECTS, SuspiciousStewEffects.EMPTY);
for (Entry effect : effects.effects())
livingEntity.addEffect(effect.createEffectInstance());
}
return false;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
private static void applyEffect(LivingEntity entity, MobEffectInstance effect) {
if (effect.getEffect().value().isInstantenous()) {
effect.getEffect().value()
.applyInstantenousEffect(null, null, entity, effect.getDuration(), 1.0);
} else {
entity.addEffect(effect);
}
}
}

View file

@ -1,23 +1,13 @@
package com.simibubi.create.api.equipment.potatoCannon;
import static com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.entityRandom;
import java.util.function.Function;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.content.equipment.potatoCannon.PotatoProjectileEntity;
import dev.engine_room.flywheel.lib.transform.TransformStack;
import net.createmod.catnip.math.AngleHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
@ -29,96 +19,4 @@ public interface PotatoProjectileRenderMode {
void transform(PoseStack ms, PotatoProjectileEntity entity, float pt);
MapCodec<? extends PotatoProjectileRenderMode> codec();
enum Billboard implements PotatoProjectileRenderMode {
INSTANCE;
public static final MapCodec<Billboard> CODEC = MapCodec.unit(INSTANCE);
@Override
@OnlyIn(Dist.CLIENT)
public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) {
Minecraft mc = Minecraft.getInstance();
Vec3 p1 = mc.getCameraEntity()
.getEyePosition(pt);
Vec3 diff = entity.getBoundingBox()
.getCenter()
.subtract(p1);
TransformStack.of(ms)
.rotateYDegrees(AngleHelper.deg(Mth.atan2(diff.x, diff.z)) + 180)
.rotateXDegrees(AngleHelper.deg(Mth.atan2(diff.y, Mth.sqrt((float) (diff.x * diff.x + diff.z * diff.z)))));
}
@Override
public MapCodec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
enum Tumble implements PotatoProjectileRenderMode {
INSTANCE;
public static final MapCodec<Tumble> CODEC = MapCodec.unit(INSTANCE);
@Override
@OnlyIn(Dist.CLIENT)
public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) {
Billboard.INSTANCE.transform(ms, entity, pt);
TransformStack.of(ms)
.rotateZDegrees((entity.tickCount + pt) * 2 * entityRandom(entity, 16))
.rotateXDegrees((entity.tickCount + pt) * entityRandom(entity, 32));
}
@Override
public MapCodec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
record TowardMotion(int spriteAngleOffset, float spin) implements PotatoProjectileRenderMode {
public static final MapCodec<TowardMotion> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
Codec.INT.fieldOf("sprite_angle_offset").forGetter(i -> i.spriteAngleOffset),
Codec.FLOAT.fieldOf("spin").forGetter(i -> i.spin)
).apply(instance, TowardMotion::new));
@Override
@OnlyIn(Dist.CLIENT)
public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) {
Vec3 diff = entity.getDeltaMovement();
TransformStack.of(ms)
.rotateYDegrees(AngleHelper.deg(Mth.atan2(diff.x, diff.z)))
.rotateXDegrees(270
+ AngleHelper.deg(Mth.atan2(diff.y, -Mth.sqrt((float) (diff.x * diff.x + diff.z * diff.z)))));
TransformStack.of(ms)
.rotateYDegrees((entity.tickCount + pt) * 20 * spin + entityRandom(entity, 360))
.rotateZDegrees(-spriteAngleOffset);
}
@Override
public MapCodec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
record StuckToEntity(Vec3 offset) implements PotatoProjectileRenderMode {
public static final MapCodec<StuckToEntity> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
Vec3.CODEC.fieldOf("offset").forGetter(i -> i.offset)
).apply(instance, StuckToEntity::new));
@Override
@OnlyIn(Dist.CLIENT)
public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) {
TransformStack.of(ms).rotateYDegrees(AngleHelper.deg(Mth.atan2(offset.x, offset.z)));
}
@Override
public MapCodec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
static int entityRandom(Entity entity, int maxValue) {
return (System.identityHashCode(entity) * 31) % maxValue;
}
}

View file

@ -1,8 +1,8 @@
package com.simibubi.create.api.registry;
import org.jetbrains.annotations.ApiStatus.Internal;
import com.mojang.serialization.MapCodec;
import com.simibubi.create.content.logistics.packagePort.PackagePortTargetType;
import com.simibubi.create.Create;
import com.simibubi.create.api.behaviour.display.DisplaySource;
import com.simibubi.create.api.behaviour.display.DisplayTarget;
@ -16,22 +16,15 @@ import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode
import com.simibubi.create.content.kinetics.fan.processing.FanProcessingType;
import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPointType;
import com.simibubi.create.content.logistics.item.filter.attribute.ItemAttributeType;
import com.simibubi.create.content.logistics.packagePort.PackagePortTargetType;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.common.EventBusSubscriber.Bus;
import net.neoforged.neoforge.registries.DataPackRegistryEvent;
/**
* Keys for registries added by Create.
*
* @see CreateBuiltInRegistries
*/
@EventBusSubscriber(bus = Bus.MOD)
public class CreateRegistries {
public static final ResourceKey<Registry<ArmInteractionPointType>> ARM_INTERACTION_POINT_TYPE = key("arm_interaction_point_type");
public static final ResourceKey<Registry<FanProcessingType>> FAN_PROCESSING_TYPE = key("fan_processing_type");
@ -50,14 +43,4 @@ public class CreateRegistries {
private static <T> ResourceKey<Registry<T>> key(String name) {
return ResourceKey.createRegistryKey(Create.asResource(name));
}
@Internal
@SubscribeEvent
public static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) {
event.dataPackRegistry(
POTATO_PROJECTILE_TYPE,
PotatoCannonProjectileType.CODEC,
PotatoCannonProjectileType.CODEC
);
}
}

View file

@ -22,6 +22,10 @@ import java.util.function.Function;
import javax.annotation.Nullable;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.Vec3i;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
@ -73,7 +77,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,7 +89,6 @@ 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.HolderLookup;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
@ -1403,7 +1405,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;
@ -1412,13 +1414,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;
}
@ -1503,32 +1505,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,13 +1,25 @@
package com.simibubi.create.content.equipment.potatoCannon;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.Create;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlaceBlockOnGround;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlantCrop;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.foundation.mixin.accessor.FallingBlockEntityAccessor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.BlockHitResult;
import net.neoforged.neoforge.common.SpecialPlantable;
public class AllPotatoProjectileBlockHitActions {
public static void init() {
@ -18,4 +30,89 @@ public class AllPotatoProjectileBlockHitActions {
private static void register(String name, MapCodec<? extends PotatoProjectileBlockHitAction> codec) {
Registry.register(CreateBuiltInRegistries.POTATO_PROJECTILE_BLOCK_HIT_ACTION, Create.asResource(name), codec);
}
public record PlantCrop(Holder<Block> cropBlock) implements PotatoProjectileBlockHitAction {
public static final MapCodec<PlantCrop> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
BuiltInRegistries.BLOCK.holderByNameCodec().fieldOf("block").forGetter(PlantCrop::cropBlock)
).apply(instance, PlantCrop::new));
public PlantCrop(Block cropBlock) {
this(cropBlock.builtInRegistryHolder());
}
@Override
public boolean execute(LevelAccessor level, ItemStack projectile, BlockHitResult ray) {
if (level.isClientSide())
return true;
BlockPos hitPos = ray.getBlockPos();
if (level instanceof Level l && !l.isLoaded(hitPos))
return true;
Direction face = ray.getDirection();
if (face != Direction.UP)
return false;
BlockPos placePos = hitPos.relative(face);
if (!level.getBlockState(placePos)
.canBeReplaced())
return false;
if (!(cropBlock.value() instanceof SpecialPlantable specialPlantable))
return false;
if (specialPlantable.canPlacePlantAtPosition(projectile, level, placePos, null))
specialPlantable.spawnPlantAtPosition(projectile, level, placePos, null);
return true;
}
@Override
public MapCodec<? extends PotatoProjectileBlockHitAction> codec() {
return CODEC;
}
}
public record PlaceBlockOnGround(Holder<Block> block) implements PotatoProjectileBlockHitAction {
public static final MapCodec<PlaceBlockOnGround> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
BuiltInRegistries.BLOCK.holderByNameCodec().fieldOf("block").forGetter(PlaceBlockOnGround::block)
).apply(instance, PlaceBlockOnGround::new));
public PlaceBlockOnGround(Block block) {
this(block.builtInRegistryHolder());
}
@Override
public boolean execute(LevelAccessor levelAccessor, ItemStack projectile, BlockHitResult ray) {
if (levelAccessor.isClientSide())
return true;
BlockPos hitPos = ray.getBlockPos();
if (levelAccessor instanceof Level l && !l.isLoaded(hitPos))
return true;
Direction face = ray.getDirection();
BlockPos placePos = hitPos.relative(face);
if (!levelAccessor.getBlockState(placePos)
.canBeReplaced())
return false;
if (face == Direction.UP) {
levelAccessor.setBlock(placePos, block.value()
.defaultBlockState(), 3);
} else if (levelAccessor instanceof Level level) {
double y = ray.getLocation().y - 0.5;
if (!level.isEmptyBlock(placePos.above()))
y = Math.min(y, placePos.getY());
if (!level.isEmptyBlock(placePos.below()))
y = Math.max(y, placePos.getY());
FallingBlockEntity falling = FallingBlockEntityAccessor.create$callInit(level, placePos.getX() + 0.5, y,
placePos.getZ() + 0.5, block.value().defaultBlockState());
falling.time = 1;
level.addFreshEntity(falling);
}
return true;
}
@Override
public MapCodec<? extends PotatoProjectileBlockHitAction> codec() {
return CODEC;
}
}
}

View file

@ -1,17 +1,49 @@
package com.simibubi.create.content.equipment.potatoCannon;
import java.util.UUID;
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.Create;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.ChorusTeleport;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.CureZombieVillager;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.FoodEffects;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.PotionEffect;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SetOnFire;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SuspiciousStew;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.foundation.codec.CreateCodecs;
import net.createmod.catnip.data.WorldAttached;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.animal.Fox;
import net.minecraft.world.entity.monster.ZombieVillager;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.food.FoodProperties.PossibleEffect;
import net.minecraft.world.food.Foods;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.SuspiciousStewEffects;
import net.minecraft.world.item.component.SuspiciousStewEffects.Entry;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.common.util.FakePlayer;
import net.neoforged.neoforge.event.EventHooks;
import net.neoforged.neoforge.event.entity.EntityTeleportEvent;
public class AllPotatoProjectileEntityHitActions {
public static void init() {
@ -26,4 +58,195 @@ public class AllPotatoProjectileEntityHitActions {
private static void register(String name, MapCodec<? extends PotatoProjectileEntityHitAction> codec) {
Registry.register(CreateBuiltInRegistries.POTATO_PROJECTILE_ENTITY_HIT_ACTION, Create.asResource(name), codec);
}
public record SetOnFire(int ticks) implements PotatoProjectileEntityHitAction {
public static final MapCodec<SetOnFire> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ExtraCodecs.POSITIVE_INT.fieldOf("ticks").forGetter(SetOnFire::ticks)
).apply(instance, SetOnFire::new));
public static SetOnFire seconds(int seconds) {
return new SetOnFire(seconds * 20);
}
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
ray.getEntity()
.setRemainingFireTicks(ticks);
return false;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
public record PotionEffect(Holder<MobEffect> effect, int level, int ticks,
boolean recoverable) implements PotatoProjectileEntityHitAction {
public static final MapCodec<PotionEffect> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
BuiltInRegistries.MOB_EFFECT.holderByNameCodec().fieldOf("effect").forGetter(PotionEffect::effect),
ExtraCodecs.POSITIVE_INT.fieldOf("level").forGetter(PotionEffect::level),
ExtraCodecs.POSITIVE_INT.fieldOf("ticks").forGetter(PotionEffect::ticks),
Codec.BOOL.fieldOf("recoverable").forGetter(PotionEffect::recoverable)
).apply(instance, PotionEffect::new));
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
if (entity.level().isClientSide)
return true;
if (entity instanceof LivingEntity)
applyEffect((LivingEntity) entity, new MobEffectInstance(effect, ticks, level - 1));
return !recoverable;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
public record FoodEffects(FoodProperties foodProperty,
boolean recoverable) implements PotatoProjectileEntityHitAction {
public static final MapCodec<FoodEffects> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
FoodProperties.DIRECT_CODEC.fieldOf("food_property").forGetter(FoodEffects::foodProperty),
Codec.BOOL.fieldOf("recoverable").forGetter(FoodEffects::recoverable)
).apply(instance, FoodEffects::new));
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
if (entity.level().isClientSide)
return true;
if (entity instanceof LivingEntity livingEntity) {
for (PossibleEffect effect : foodProperty.effects()) {
if (livingEntity.getRandom().nextFloat() < effect.probability())
applyEffect(livingEntity, effect.effect());
}
}
return !recoverable;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
public record ChorusTeleport(double teleportDiameter) implements PotatoProjectileEntityHitAction {
public static final MapCodec<ChorusTeleport> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
CreateCodecs.POSITIVE_DOUBLE.fieldOf("teleport_diameter").forGetter(ChorusTeleport::teleportDiameter)
).apply(instance, ChorusTeleport::new));
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
Level level = entity.getCommandSenderWorld();
if (level.isClientSide)
return true;
if (!(entity instanceof LivingEntity livingEntity))
return false;
double entityX = livingEntity.getX();
double entityY = livingEntity.getY();
double entityZ = livingEntity.getZ();
for (int teleportTry = 0; teleportTry < 16; ++teleportTry) {
double teleportX = entityX + (livingEntity.getRandom()
.nextDouble() - 0.5D) * teleportDiameter;
double teleportY = Mth.clamp(entityY + (livingEntity.getRandom()
.nextInt((int) teleportDiameter) - (int) (teleportDiameter / 2)), 0.0D, level.getHeight() - 1);
double teleportZ = entityZ + (livingEntity.getRandom()
.nextDouble() - 0.5D) * teleportDiameter;
EntityTeleportEvent.ChorusFruit event =
EventHooks.onChorusFruitTeleport(livingEntity, teleportX, teleportY, teleportZ);
if (event.isCanceled())
return false;
if (livingEntity.randomTeleport(event.getTargetX(), event.getTargetY(), event.getTargetZ(), true)) {
if (livingEntity.isPassenger())
livingEntity.stopRiding();
SoundEvent soundevent =
livingEntity instanceof Fox ? SoundEvents.FOX_TELEPORT : SoundEvents.CHORUS_FRUIT_TELEPORT;
level.playSound(null, entityX, entityY, entityZ, soundevent, SoundSource.PLAYERS, 1.0F, 1.0F);
livingEntity.playSound(soundevent, 1.0F, 1.0F);
livingEntity.setDeltaMovement(Vec3.ZERO);
return true;
}
}
return false;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
public enum CureZombieVillager implements PotatoProjectileEntityHitAction {
INSTANCE;
private static final FoodEffects EFFECT = new FoodEffects(Foods.GOLDEN_APPLE, false);
private static final GameProfile ZOMBIE_CONVERTER_NAME =
new GameProfile(UUID.fromString("be12d3dc-27d3-4992-8c97-66be53fd49c5"), "Converter");
private static final WorldAttached<FakePlayer> ZOMBIE_CONVERTERS =
new WorldAttached<>(w -> new FakePlayer((ServerLevel) w, ZOMBIE_CONVERTER_NAME));
public static final MapCodec<CureZombieVillager> CODEC = MapCodec.unit(INSTANCE);
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
Level world = entity.level();
if (!(entity instanceof ZombieVillager zombieVillager) || !zombieVillager.hasEffect(MobEffects.WEAKNESS))
return EFFECT.execute(projectile, ray, type);
if (world.isClientSide)
return false;
FakePlayer dummy = ZOMBIE_CONVERTERS.get(world);
dummy.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.GOLDEN_APPLE, 1));
zombieVillager.mobInteract(dummy, InteractionHand.MAIN_HAND);
return true;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
public enum SuspiciousStew implements PotatoProjectileEntityHitAction {
INSTANCE;
public static final MapCodec<SuspiciousStew> CODEC = MapCodec.unit(INSTANCE);
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
if (ray.getEntity() instanceof LivingEntity livingEntity) {
SuspiciousStewEffects stew = projectile.getOrDefault(DataComponents.SUSPICIOUS_STEW_EFFECTS, SuspiciousStewEffects.EMPTY);
for (Entry effect : stew.effects())
livingEntity.addEffect(effect.createEffectInstance());
}
return false;
}
@Override
public MapCodec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
private static void applyEffect(LivingEntity entity, MobEffectInstance effect) {
if (effect.getEffect().value().isInstantenous()) {
effect.getEffect().value()
.applyInstantenousEffect(null, null, entity, effect.getDuration(), 1.0);
} else {
entity.addEffect(effect);
}
}
}

View file

@ -1,16 +1,23 @@
package com.simibubi.create.content.equipment.potatoCannon;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.Create;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.Billboard;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.StuckToEntity;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.TowardMotion;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.Tumble;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import dev.engine_room.flywheel.lib.transform.TransformStack;
import net.createmod.catnip.math.AngleHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Registry;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
public class AllPotatoProjectileRenderModes {
public static void init() {
@ -23,4 +30,96 @@ public class AllPotatoProjectileRenderModes {
private static void register(String name, MapCodec<? extends PotatoProjectileRenderMode> codec) {
Registry.register(CreateBuiltInRegistries.POTATO_PROJECTILE_RENDER_MODE, Create.asResource(name), codec);
}
public enum Billboard implements PotatoProjectileRenderMode {
INSTANCE;
public static final MapCodec<Billboard> CODEC = MapCodec.unit(INSTANCE);
@Override
@OnlyIn(Dist.CLIENT)
public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) {
Minecraft mc = Minecraft.getInstance();
Vec3 p1 = mc.getCameraEntity()
.getEyePosition(pt);
Vec3 diff = entity.getBoundingBox()
.getCenter()
.subtract(p1);
TransformStack.of(ms)
.rotateYDegrees(AngleHelper.deg(Mth.atan2(diff.x, diff.z)) + 180)
.rotateXDegrees(AngleHelper.deg(Mth.atan2(diff.y, Mth.sqrt((float) (diff.x * diff.x + diff.z * diff.z)))));
}
@Override
public MapCodec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
public enum Tumble implements PotatoProjectileRenderMode {
INSTANCE;
public static final MapCodec<Tumble> CODEC = MapCodec.unit(INSTANCE);
@Override
@OnlyIn(Dist.CLIENT)
public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) {
Billboard.INSTANCE.transform(ms, entity, pt);
TransformStack.of(ms)
.rotateZDegrees((entity.tickCount + pt) * 2 * entityRandom(entity, 16))
.rotateXDegrees((entity.tickCount + pt) * entityRandom(entity, 32));
}
@Override
public MapCodec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
public record TowardMotion(int spriteAngleOffset, float spin) implements PotatoProjectileRenderMode {
public static final MapCodec<TowardMotion> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
Codec.INT.fieldOf("sprite_angle_offset").forGetter(i -> i.spriteAngleOffset),
Codec.FLOAT.fieldOf("spin").forGetter(i -> i.spin)
).apply(instance, TowardMotion::new));
@Override
@OnlyIn(Dist.CLIENT)
public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) {
Vec3 diff = entity.getDeltaMovement();
TransformStack.of(ms)
.rotateYDegrees(AngleHelper.deg(Mth.atan2(diff.x, diff.z)))
.rotateXDegrees(270
+ AngleHelper.deg(Mth.atan2(diff.y, -Mth.sqrt((float) (diff.x * diff.x + diff.z * diff.z)))));
TransformStack.of(ms)
.rotateYDegrees((entity.tickCount + pt) * 20 * spin + entityRandom(entity, 360))
.rotateZDegrees(-spriteAngleOffset);
}
@Override
public MapCodec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
public record StuckToEntity(Vec3 offset) implements PotatoProjectileRenderMode {
public static final MapCodec<StuckToEntity> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
Vec3.CODEC.fieldOf("offset").forGetter(i -> i.offset)
).apply(instance, StuckToEntity::new));
@Override
@OnlyIn(Dist.CLIENT)
public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) {
TransformStack.of(ms).rotateYDegrees(AngleHelper.deg(Mth.atan2(offset.x, offset.z)));
}
@Override
public MapCodec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
private static int entityRandom(Entity entity, int maxValue) {
return (System.identityHashCode(entity) * 31) % maxValue;
}
}

View file

@ -3,15 +3,15 @@ package com.simibubi.create.content.equipment.potatoCannon;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlaceBlockOnGround;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlantCrop;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.ChorusTeleport;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.CureZombieVillager;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.FoodEffects;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.PotionEffect;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SetOnFire;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SuspiciousStew;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileBlockHitActions.PlaceBlockOnGround;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileBlockHitActions.PlantCrop;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileEntityHitActions.ChorusTeleport;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileEntityHitActions.CureZombieVillager;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileEntityHitActions.FoodEffects;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileEntityHitActions.PotionEffect;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileEntityHitActions.SetOnFire;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileEntityHitActions.SuspiciousStew;
import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.resources.ResourceKey;
@ -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(BootstrapContext<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(BootstrapContext<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;
@ -56,15 +55,171 @@ import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions;
import net.neoforged.neoforge.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.durability(MAX_DAMAGE));
super(properties);
}
@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().value());
}
@Override
protected void shootProjectile(LivingEntity shooter, Projectile projectile, int index, float velocity, float inaccuracy, float angle, @Nullable LivingEntity target) {}
@Override
protected void shoot(ServerLevel level, LivingEntity shooter, InteractionHand hand, ItemStack weapon, List<ItemStack> projectileItems, float velocity, float inaccuracy, boolean isCrit, @Nullable LivingEntity target) {}
@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, LivingEntity.getSlotForHand(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, TooltipContext context, List<Component> tooltip, TooltipFlag flag) {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) {
super.appendHoverText(stack, context, tooltip, flag);
return;
}
Ammo ammo = getAmmo(player, stack);
if (ammo == null) {
super.appendHoverText(stack, context, tooltip, flag);
return;
}
ItemStack ammoStack = ammo.stack();
PotatoCannonProjectileType type = ammo.type();
HolderLookup.Provider registries = context.registries();
if (registries == null)
return;
HolderLookup<Enchantment> lookup = registries.lookupOrThrow(Registries.ENCHANTMENT);
int power = stack.getEnchantmentLevel(lookup.getOrThrow(Enchantments.POWER));
int punch = stack.getEnchantmentLevel(lookup.getOrThrow(Enchantments.PUNCH));
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)));
}
@Override
@ -72,6 +227,22 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
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 supportsEnchantment(ItemStack stack, Holder<Enchantment> enchantment) {
if (enchantment.is(Enchantments.POWER))
@ -87,11 +258,6 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
return super.supportsEnchantment(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());
@ -107,190 +273,13 @@ 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
protected void shootProjectile(LivingEntity shooter, Projectile projectile, int index, float velocity, float inaccuracy, float angle, @Nullable LivingEntity target) {}
@Override
protected void shoot(ServerLevel level, LivingEntity shooter, InteractionHand hand, ItemStack weapon, List<ItemStack> projectileItems, float velocity, float inaccuracy, boolean isCrit, @Nullable LivingEntity target) {}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
ItemStack stack = player.getItemInHand(hand);
return findAmmoInInventory(level, player, stack).map(ammoIn -> {
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));
ItemStack itemStack = ammoIn.copy();
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()) {
ammoIn.shrink(1);
if (ammoIn.isEmpty())
player.getInventory().removeItem(ammoIn);
}
if (!BacktankUtil.canAbsorbDamage(player, maxUses()))
stack.hurtAndBreak(1, player, LivingEntity.getSlotForHand(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, hand, b, soundPitch, lookVec.normalize(), itemStack));
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, TooltipContext context, List<Component> tooltip, TooltipFlag flag) {
HolderLookup.Provider registries = context.registries();
if (registries != null) {
HolderLookup.RegistryLookup<Enchantment> enchantLookup = registries.lookupOrThrow(Registries.ENCHANTMENT);
int power = stack.getEnchantmentLevel(enchantLookup.get(Enchantments.POWER).orElseThrow().getDelegate());
int punch = stack.getEnchantmentLevel(enchantLookup.get(Enchantments.PUNCH).orElseThrow().getDelegate());
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, context, 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, InteractionHand hand) {
return true;
return false;
}
@Override
@ -307,14 +296,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.neoforged.neoforge.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

@ -17,20 +17,20 @@ import net.neoforged.api.distmarker.OnlyIn;
public class PotatoCannonPacket extends ShootGadgetPacket {
public static final StreamCodec<RegistryFriendlyByteBuf, PotatoCannonPacket> STREAM_CODEC = StreamCodec.composite(
CatnipStreamCodecs.VEC3, packet -> packet.location,
CatnipStreamCodecs.HAND, packet -> packet.hand,
ByteBufCodecs.BOOL, packet -> packet.self,
ByteBufCodecs.FLOAT, packet -> packet.pitch,
CatnipStreamCodecs.VEC3, packet -> packet.motion,
ItemStack.STREAM_CODEC, packet -> packet.item,
PotatoCannonPacket::new
CatnipStreamCodecs.VEC3, packet -> packet.location,
CatnipStreamCodecs.VEC3, packet -> packet.motion,
ItemStack.STREAM_CODEC, packet -> packet.item,
CatnipStreamCodecs.HAND, packet -> packet.hand,
ByteBufCodecs.FLOAT, packet -> packet.pitch,
ByteBufCodecs.BOOL, packet -> packet.self,
PotatoCannonPacket::new
);
private final float pitch;
private final Vec3 motion;
private final ItemStack item;
public PotatoCannonPacket(Vec3 location, InteractionHand hand, boolean self, float pitch, Vec3 motion, ItemStack item) {
public PotatoCannonPacket(Vec3 location, Vec3 motion, ItemStack item, InteractionHand hand, float pitch, boolean self) {
super(location, hand, self);
this.motion = motion;
this.item = item;

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,12 +1,14 @@
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;
import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileRenderModes.StuckToEntity;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.damageTypes.CreateDamageSources;
import com.simibubi.create.foundation.particle.AirParticleData;
@ -56,28 +58,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) {
@ -89,9 +80,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.parseOptional(this.registryAccess(), nbt.getCompound("Item"));
setItem(ItemStack.parseOptional(this.registryAccess(), nbt.getCompound("Item")));
additionalDamageMult = nbt.getFloat("AdditionalDamage");
additionalKnockback = nbt.getFloat("AdditionalKnockback");
recoveryChance = nbt.getFloat("Recovery");
@ -107,6 +107,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
super.addAdditionalSaveData(nbt);
}
@Nullable
public Entity getStuckEntity() {
if (stuckEntity == null)
return null;
@ -118,7 +119,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
public void setStuckEntity(Entity stuckEntity) {
this.stuckEntity = stuckEntity;
this.stuckOffset = position().subtract(stuckEntity.position());
this.stuckRenderer = new PotatoProjectileRenderMode.StuckToEntity(stuckOffset);
this.stuckRenderer = new StuckToEntity(stuckOffset);
this.stuckFallSpeed = 0.0;
setDeltaMovement(Vec3.ZERO);
}
@ -127,27 +128,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();
@ -177,9 +177,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())
@ -202,7 +201,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;
@ -221,12 +220,13 @@ 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(type.dropStack());
}
spawnAtLocation(projectileType.dropStack());
}
if (!(target instanceof LivingEntity livingentity)) {
playHitSound(level(), position());
@ -287,11 +287,13 @@ 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 (random.nextDouble() <= recoveryChance)
if (!type.onBlockHit(level(), stack, ray) && !level().isClientSide) {
if (random.nextDouble() <= recoveryChance) {
recoverItem();
spawnAtLocation(getProjectileType().dropStack());
} else {
spawnAtLocation(getProjectileType().dropStack());
}
}
super.onHitBlock(ray);
kill();

View file

@ -5,7 +5,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.Nullable;
@ -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.component.DataComponents;
import net.minecraft.core.particles.BlockParticleOption;
@ -58,11 +59,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.Vec3;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.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());
@ -81,13 +78,11 @@ 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);
}
@Internal
public static void register(IEventBus eventBus) {
REGISTER.register(eventBus);
public static void init() {
}
@Nullable

View file

@ -9,7 +9,7 @@ import org.jetbrains.annotations.ApiStatus.Internal;
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;
@ -37,6 +37,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.core.component.DataComponents;
import net.minecraft.world.Containers;
@ -59,15 +60,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.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.ItemHandlerHelper;
import net.neoforged.neoforge.items.wrapper.SidedInvWrapper;
import net.neoforged.neoforge.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());
@ -89,12 +86,11 @@ 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);
}
@Internal
public static void register(IEventBus eventBus) {
REGISTER.register(eventBus);
public static void init() {
}
//

View file

@ -27,7 +27,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.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.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

@ -49,7 +49,6 @@ import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.items.ItemHandlerHelper;
import net.neoforged.neoforge.items.ItemStackHandler;
@ -216,6 +215,8 @@ public class PackageItem extends Item {
ItemStack itemstack = contents.getStackInSlot(i);
if (itemstack.isEmpty())
continue;
if (itemstack.getItem() instanceof SpawnEggItem)
continue;
if (visibleNames > 2) {
skippedNames++;
continue;

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.Internal;
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;
@ -20,6 +18,7 @@ import com.simibubi.create.content.logistics.item.filter.attribute.attributes.In
import com.simibubi.create.content.logistics.item.filter.attribute.attributes.ItemNameAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.attributes.ShulkerFillLevelAttribute;
import net.minecraft.core.Registry;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponents;
@ -37,12 +36,9 @@ import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.registries.DeferredRegister;
// TODO - Documentation
public class AllItemAttributeTypes {
private static final DeferredRegister<ItemAttributeType> REGISTER = DeferredRegister.create(CreateRegistries.ITEM_ATTRIBUTE_TYPE, Create.ID);
public static final ItemAttributeType
PLACEABLE = singleton("placeable", s -> s.getItem() instanceof BlockItem),
CONSUMABLE = singleton("consumable", s -> s.has(DataComponents.FOOD)),
@ -103,12 +99,8 @@ 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);
}
@Internal
public static void register(IEventBus modEventBus) {
REGISTER.register(modEventBus);
}
public static void init() {}
}

View file

@ -4,6 +4,7 @@ import java.util.List;
import com.simibubi.create.AllBlockEntityTypes;
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;
@ -29,6 +30,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.ItemInteractionResult;
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;
@ -49,6 +52,7 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
public LerpedFloat animationProgress;
public LerpedFloat anticipationProgress;
public boolean currentlyDepositing;
public boolean goggles;
public boolean sendAnticipate;
@ -56,7 +60,7 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
private boolean failedLastExport;
private FrogportSounds sounds;
private ItemStack deferAnimationStart;
private boolean deferAnimationInward;
@ -70,6 +74,7 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
manualOpenAnimationProgress = LerpedFloat.linear()
.startWithValue(0)
.chase(0, 0.35, Chaser.LINEAR);
goggles = false;
}
public static void registerCapabilities(RegisterCapabilitiesEvent event) {
@ -132,14 +137,14 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
@Override
public void tick() {
super.tick();
if (deferAnimationStart != null) {
startAnimation(deferAnimationStart, deferAnimationInward);
deferAnimationStart = null;
}
if (anticipationProgress.getValue() == 1)
anticipationProgress.updateChaseTarget(0);
anticipationProgress.startWithValue(0);
manualOpenAnimationProgress.updateChaseTarget(openTracker.openCount > 0 ? 1 : 0);
boolean wasOpen = manualOpenAnimationProgress.getValue() > 0;
@ -321,6 +326,8 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
}
if (failedLastExport)
NBTHelper.putMarker(tag, "FailedLastExport");
if (goggles)
NBTHelper.putMarker(tag, "Goggles");
}
@Override
@ -328,6 +335,7 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
super.read(tag, registries, clientPacket);
passiveYaw = tag.getFloat("PlacedYaw");
failedLastExport = tag.getBoolean("FailedLastExport");
goggles = tag.getBoolean("Goggles");
if (!clientPacket)
animatedPackage = null;
if (tag.contains("AnimatedPackage")) {
@ -361,4 +369,22 @@ public class FrogportBlockEntity extends PackagePortBlockEntity implements IHave
sounds.open(level, worldPosition);
}
@Override
public ItemInteractionResult use(Player player) {
if (player == null)
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
ItemStack mainHandItem = player.getMainHandItem();
if (!goggles && AllItems.GOGGLES.isIn(mainHandItem)) {
goggles = true;
if (!level.isClientSide()) {
notifyUpdate();
level.playSound(null, worldPosition, SoundEvents.ARMOR_EQUIP_GOLD.value(), SoundSource.BLOCKS, 0.5f, 1.0f);
}
return ItemInteractionResult.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

@ -22,7 +22,7 @@ import net.minecraft.world.phys.Vec3;
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;
@ -32,6 +32,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);
@ -68,6 +69,8 @@ public class FrogportVisual extends AbstractBlockEntityVisual<FrogportBlockEntit
}
private void animate(float partialTicks) {
updateGoggles();
float yaw = blockEntity.getYaw();
float headPitch = 80;
@ -170,6 +173,28 @@ if (yaw != lastYaw) {
}
}
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

@ -7,9 +7,6 @@ import java.util.Optional;
import javax.annotation.Nullable;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.platform.CatnipServices;
import org.lwjgl.glfw.GLFW;
import com.google.common.collect.ImmutableList;
@ -25,10 +22,12 @@ import com.simibubi.create.foundation.gui.widget.IconButton;
import com.simibubi.create.foundation.gui.widget.ScrollInput;
import com.simibubi.create.foundation.utility.CreateLang;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.animation.LerpedFloat;
import net.createmod.catnip.animation.LerpedFloat.Chaser;
import net.createmod.catnip.gui.UIRenderHelper;
import net.createmod.catnip.gui.element.GuiGameElement;
import net.createmod.catnip.platform.CatnipServices;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
@ -127,13 +126,20 @@ public class StockKeeperCategoryScreen extends AbstractSimiContainerScreen<Stock
ItemStack stackInSlot = menu.proxyInventory.getStackInSlot(0)
.copy();
if (!stackInSlot.isEmpty())
stackInSlot.set(DataComponents.CUSTOM_NAME, Component.literal(editorEditBox.getValue()));
boolean empty = stackInSlot.isEmpty();
if (editingIndex == -1)
schedule.add(stackInSlot);
else
schedule.set(editingIndex, stackInSlot);
if (empty && editingIndex != -1)
schedule.remove(editingIndex);
if (!empty) {
String value = editorEditBox.getValue();
stackInSlot.set(DataComponents.CUSTOM_NAME, value.isBlank() ? null : Component.literal(value));
if (editingIndex == -1)
schedule.add(stackInSlot);
else
schedule.set(editingIndex, stackInSlot);
}
CatnipServices.NETWORK.sendToServer(new GhostItemSubmitPacket(ItemStack.EMPTY, 0));

View file

@ -261,7 +261,7 @@ public class StockKeeperRequestScreen extends AbstractSimiContainerScreen<StockK
extraAreas.add(new Rect2i(0, y + windowHeight - 15 - leftHeight, x, height));
if (encodeRequester)
extraAreas.add(new Rect2i(x + windowWidth, y + windowHeight - 15 - rightHeight, rightHeight, rightHeight));
extraAreas.add(new Rect2i(x + windowWidth, y + windowHeight - 15 - rightHeight, rightHeight + 10, rightHeight));
if (initial) {
playUiSound(SoundEvents.WOOD_HIT, 0.5f, 1.5f);
@ -1078,9 +1078,16 @@ public class StockKeeperRequestScreen extends AbstractSimiContainerScreen<StockK
if (indexOf >= blockEntity.categories.size())
continue;
hiddenCategories.remove(indexOf);
if (!entry.hidden)
if (!entry.hidden) {
hiddenCategories.add(indexOf);
playUiSound(SoundEvents.ITEM_FRAME_ROTATE_ITEM, 0.75f, 1.5f);
}
else {
hiddenCategories.remove(indexOf);
playUiSound(SoundEvents.ITEM_FRAME_ROTATE_ITEM, 0.75f, 0.675f);
}
refreshSearchNextTick = true;
moveToTopNextTick = false;
return true;

View file

@ -18,7 +18,7 @@ public class TableClothFilteringBehaviour extends FilteringBehaviour {
public TableClothFilteringBehaviour(TableClothBlockEntity be) {
super(be, new TableClothFilterSlot(be));
withPredicate(is -> !(is.getItem() instanceof FilterItem));
withPredicate(is -> !(is.getItem() instanceof FilterItem) && !(is.getItem() instanceof ShoppingListItem));
count = 1;
}
@ -68,7 +68,7 @@ public class TableClothFilteringBehaviour extends FilteringBehaviour {
public void setValueSettings(Player player, ValueSettings settings, boolean ctrlDown) {
if (getValueSettings().equals(settings))
return;
count = settings.value();
count = Math.max(1, settings.value());
blockEntity.setChanged();
blockEntity.sendData();
playFeedbackSound(this);

View file

@ -4,7 +4,6 @@ import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.content.kinetics.belt.behaviour.TransportedItemStackHandlerBehaviour;
import com.simibubi.create.content.kinetics.belt.behaviour.TransportedItemStackHandlerBehaviour.TransportedResult;
import com.simibubi.create.content.logistics.box.PackageItem;
import com.simibubi.create.content.redstone.displayLink.DisplayLinkBlockEntity;
import com.simibubi.create.content.redstone.displayLink.DisplayLinkContext;
import com.simibubi.create.content.redstone.displayLink.target.DisplayTargetStats;
@ -12,7 +11,6 @@ import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.item.ItemStack;
@ -42,12 +40,8 @@ public class ItemNameDisplaySource extends SingleLineDisplaySource {
});
ItemStack stack = stackHolder.getValue();
if (stack != null && !stack.isEmpty()) {
Component hoverName = stack.getHoverName();
if (PackageItem.isPackage(stack))
hoverName = Component.literal(PackageItem.getAddress(stack));
combined = combined.append(hoverName);
}
if (stack != null && !stack.isEmpty())
combined = combined.append(stack.getHoverName());
}
return combined;

View file

@ -0,0 +1,68 @@
package com.simibubi.create.content.redstone.displayLink.source;
import com.simibubi.create.content.kinetics.chainConveyor.ChainConveyorBlockEntity;
import com.simibubi.create.content.kinetics.chainConveyor.ChainConveyorPackage;
import com.simibubi.create.content.logistics.box.PackageItem;
import com.simibubi.create.content.redstone.displayLink.DisplayLinkContext;
import com.simibubi.create.content.redstone.displayLink.target.DisplayTargetStats;
import com.simibubi.create.content.redstone.smartObserver.SmartObserverBlock;
import com.simibubi.create.content.redstone.smartObserver.SmartObserverBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.blockEntity.behaviour.inventory.InvManipulationBehaviour;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.neoforged.neoforge.items.IItemHandler;
public class PackageAddressDisplaySource extends SingleLineDisplaySource {
@Override
protected MutableComponent provideLine(DisplayLinkContext context, DisplayTargetStats stats) {
BlockEntity sourceBE = context.getSourceBlockEntity();
if (!(sourceBE instanceof SmartObserverBlockEntity cobe))
return EMPTY_LINE;
InvManipulationBehaviour invManipulationBehaviour = cobe.getBehaviour(InvManipulationBehaviour.TYPE);
FilteringBehaviour filteringBehaviour = cobe.getBehaviour(FilteringBehaviour.TYPE);
IItemHandler handler = invManipulationBehaviour.getInventory();
if (handler == null) {
BlockPos targetPos = cobe.getBlockPos().relative(SmartObserverBlock.getTargetDirection(cobe.getBlockState()));
if (context.level().getBlockEntity(targetPos) instanceof ChainConveyorBlockEntity ccbe)
for (ChainConveyorPackage box : ccbe.getLoopingPackages())
if (filteringBehaviour.test(box.item))
return Component.literal(PackageItem.getAddress(box.item));
return EMPTY_LINE;
}
for (int i = 0; i < handler.getSlots(); i++) {
ItemStack stack = handler.getStackInSlot(i);
if (PackageItem.isPackage(stack) && filteringBehaviour.test(stack))
return Component.literal(PackageItem.getAddress(stack));
}
return EMPTY_LINE;
}
@Override
protected String getTranslationKey() {
return "read_package_address";
}
@Override
protected boolean allowsLabeling(DisplayLinkContext context) {
return true;
}
@Override
protected String getFlapDisplayLayoutName(DisplayLinkContext context) {
return "Default";
}
}

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

@ -6,6 +6,8 @@ import com.simibubi.create.content.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.fluids.PipeConnection.Flow;
import com.simibubi.create.content.kinetics.belt.behaviour.TransportedItemStackHandlerBehaviour;
import com.simibubi.create.content.kinetics.belt.behaviour.TransportedItemStackHandlerBehaviour.TransportedResult;
import com.simibubi.create.content.kinetics.chainConveyor.ChainConveyorBlockEntity;
import com.simibubi.create.content.kinetics.chainConveyor.ChainConveyorPackage;
import com.simibubi.create.content.redstone.DirectedDirectionalBlock;
import com.simibubi.create.content.redstone.FilteredDetectorFilterSlot;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
@ -112,6 +114,16 @@ public class SmartObserverBlockEntity extends SmartBlockEntity {
}
return;
}
// Detect packages looping on a chain conveyor
if (level.getBlockEntity(targetPos) instanceof ChainConveyorBlockEntity ccbe) {
for (ChainConveyorPackage box : ccbe.getLoopingPackages())
if (filtering.test(box.item)) {
activate();
return;
}
return;
}
if (observedInventory.hasInventory()) {
boolean skipInv = invVersionTracker.stillWaiting(observedInventory);

View file

@ -137,6 +137,7 @@ public class ToolSelectionScreen extends Screen {
matrixStack.popPose();
}
RenderSystem.setShaderColor(1, 1, 1, 1);
RenderSystem.disableBlend();
matrixStack.popPose();
}

View file

@ -1,4 +1,4 @@
package com.simibubi.create.foundation.utility;
package com.simibubi.create.foundation;
import java.util.List;

View file

@ -4,6 +4,7 @@ import java.util.function.Supplier;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.ContraptionHandler;
@ -28,6 +29,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;
@ -103,6 +105,7 @@ import net.neoforged.neoforge.client.event.ClientTickEvent;
import net.neoforged.neoforge.client.event.EntityRenderersEvent;
import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent;
import net.neoforged.neoforge.client.event.RegisterGuiLayersEvent;
import net.neoforged.neoforge.client.event.RegisterItemDecorationsEvent;
import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
import net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage;
import net.neoforged.neoforge.client.event.ViewportEvent;
@ -386,6 +389,11 @@ public class ClientEvents {
event.registerAbove(VanillaGuiLayers.HOTBAR, Create.asResource("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.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.client.event.ModelEvent;

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,50 @@
package com.simibubi.create.foundation.utility;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import net.createmod.catnip.platform.CatnipServices;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.core.RegistryAccess;
import net.minecraft.server.MinecraftServer;
import net.neoforged.neoforge.server.ServerLifecycleHooks;
public final class GlobalRegistryAccess {
private static Supplier<@Nullable RegistryAccess> supplier;
static {
CatnipServices.PLATFORM.executeOnClientOnly(() -> () -> 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

@ -0,0 +1,24 @@
package com.simibubi.create.impl.registry;
import org.jetbrains.annotations.ApiStatus.Internal;
import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType;
import com.simibubi.create.api.registry.CreateRegistries;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.common.EventBusSubscriber.Bus;
import net.neoforged.neoforge.registries.DataPackRegistryEvent;
@EventBusSubscriber(bus = Bus.MOD)
public class CreateRegistriesImpl {
@Internal
@SubscribeEvent
public static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) {
event.dataPackRegistry(
CreateRegistries.POTATO_PROJECTILE_TYPE,
PotatoCannonProjectileType.CODEC,
PotatoCannonProjectileType.CODEC
);
}
}

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;

View file

@ -130,6 +130,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.neoforged.neoforge.fluids.FluidStack;
@GameTestGroup(path = "contraptions")
@ -230,4 +232,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;
@ -16,20 +18,26 @@ import com.simibubi.create.infrastructure.gametest.GameTestGroup;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
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.PotionContents;
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.neoforged.neoforge.fluids.FluidStack;
import net.neoforged.neoforge.fluids.FluidType;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
@ -301,4 +309,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 = PotionContents.createItemStack(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

@ -419,4 +419,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

@ -1139,6 +1139,7 @@
"create.display_source.list_items": "List matching Items",
"create.display_source.fluid_amount": "Amount of matching Fluids",
"create.display_source.list_fluids": "List matching Fluids",
"create.display_source.read_package_address": "Read Package Address",
"create.display_source.nixie_tube": "Copy Nixie Tubes",
"create.display_source.fill_level": "Container Fill Level",
"create.display_source.fill_level.display": "Display Format",

View file

@ -2,79 +2,78 @@
# www.blender.org
mtllib conveyor_wheel.mtl
o Cube.002
v 0.965990 0.875000 -0.625000
v 0.965990 0.125000 -0.625000
v 0.034010 0.875000 -0.624999
v 0.034010 0.125000 -0.624999
v 0.000000 0.875000 0.000000
v 0.062500 0.875000 0.000000
v 0.937500 0.875000 0.000000
v 1.000000 0.875000 0.000000
v 0.062500 0.875000 -0.476712
v 0.062500 0.875000 -0.556218
v 0.937500 0.875000 -0.556218
v 0.937500 0.875000 -0.476713
v -0.295495 0.875000 -0.295494
v -0.295495 0.125000 -0.295494
v -0.207106 0.875000 -0.207106
v 1.625000 0.875000 0.965990
v 1.625000 0.125000 0.965990
v 1.624999 0.875000 0.034010
v 1.624999 0.125000 0.034010
v 1.000000 0.875000 0.062500
v 1.000000 0.875000 0.937500
v 1.000000 0.875000 1.000000
v 1.476712 0.875000 0.062500
v 1.556218 0.875000 0.062500
v 1.556218 0.875000 0.937500
v 1.476713 0.875000 0.937500
v 1.295495 0.125000 1.295495
v 1.295494 0.875000 -0.295495
v 1.295494 0.125000 -0.295495
v 1.207106 0.875000 -0.207106
v 0.034010 0.875000 1.625000
v 0.034010 0.125000 1.625000
v 0.965990 0.875000 1.624999
v 0.965990 0.125000 1.624999
v 0.937500 0.875000 1.000000
v 0.062500 0.875000 1.000000
v 0.000000 0.875000 1.000000
v 0.937500 0.875000 1.476712
v 0.937500 0.875000 1.556218
v 0.062500 0.875000 1.556218
v 0.062500 0.875000 1.476713
v -0.295495 0.125000 1.295495
v 1.295494 0.875000 1.295494
v 1.207106 0.875000 1.207106
v -0.625000 0.875000 0.034010
v -0.625000 0.125000 0.034010
v -0.624999 0.875000 0.965990
v -0.624999 0.125000 0.965990
v 0.000000 0.875000 0.937500
v 0.000000 0.875000 0.062500
v -0.476712 0.875000 0.937500
v -0.556218 0.875000 0.937500
v -0.556218 0.875000 0.062500
v -0.476713 0.875000 0.062500
v -0.295494 0.875000 1.295494
v -0.207106 0.875000 1.207106
v 0.500000 0.875000 0.500000
v -0.246859 0.875000 -0.246859
v -0.246859 0.875000 1.246859
v 1.246859 0.875000 1.246859
v 1.246859 0.875000 -0.246859
v -0.499999 0.125000 0.914213
v 0.085787 0.125000 -0.499999
v 1.207106 0.125000 -0.207106
v -0.207106 0.125000 -0.207106
v 1.499999 0.125000 0.085787
v 1.207107 0.125000 1.207107
v 0.914213 0.125000 1.499999
v -0.207107 0.125000 1.207106
v -0.500000 0.125000 0.085787
v 0.914213 0.125000 -0.500000
v 1.500000 0.125000 0.914213
v 0.085787 0.125000 1.500000
v 0.965990 0.870000 -0.625000
v 0.965990 0.130000 -0.625000
v 0.034010 0.870000 -0.624999
v 0.034010 0.130000 -0.624999
v 0.000000 0.870000 -0.000000
v 0.062500 0.870000 -0.000000
v 0.937500 0.870000 -0.000000
v 1.000000 0.870000 -0.000000
v 0.062500 0.870000 -0.476712
v 0.062500 0.870000 -0.556218
v 0.937500 0.870000 -0.556218
v 0.937500 0.870000 -0.476713
v -0.295495 0.870000 -0.295494
v -0.295495 0.130000 -0.295494
v -0.207106 0.870000 -0.207106
v 1.625000 0.870000 0.965990
v 1.625000 0.130000 0.965990
v 1.624999 0.870000 0.034010
v 1.624999 0.130000 0.034010
v 1.000000 0.870000 0.062500
v 1.000000 0.870000 0.937500
v 1.000000 0.870000 1.000000
v 1.476712 0.870000 0.062500
v 1.556218 0.870000 0.062500
v 1.556218 0.870000 0.937500
v 1.476713 0.870000 0.937500
v 1.295495 0.130000 1.295495
v 1.295494 0.870000 -0.295495
v 1.295494 0.130000 -0.295495
v 1.207106 0.870000 -0.207106
v 0.034010 0.870000 1.625000
v 0.034010 0.130000 1.625000
v 0.965990 0.870000 1.624999
v 0.965990 0.130000 1.624999
v 0.937500 0.870000 1.000000
v 0.062500 0.870000 1.000000
v 0.000000 0.870000 1.000000
v 0.937500 0.870000 1.476712
v 0.937500 0.870000 1.556218
v 0.062500 0.870000 1.556218
v 0.062500 0.870000 1.476713
v -0.295495 0.130000 1.295495
v 1.295494 0.870000 1.295494
v 1.207106 0.870000 1.207106
v -0.625000 0.870000 0.034010
v -0.625000 0.130000 0.034010
v -0.624999 0.870000 0.965990
v -0.624999 0.130000 0.965990
v 0.000000 0.870000 0.937500
v 0.000000 0.870000 0.062500
v -0.476712 0.870000 0.937500
v -0.556218 0.870000 0.937500
v -0.556218 0.870000 0.062500
v -0.476713 0.870000 0.062500
v -0.295494 0.870000 1.295494
v -0.207106 0.870000 1.207106
v -0.246859 0.870000 -0.246859
v -0.246859 0.870000 1.246859
v 1.246859 0.870000 1.246859
v 1.246859 0.870000 -0.246859
v -0.499999 0.130000 0.914213
v 0.085787 0.130000 -0.499999
v 1.207106 0.130000 -0.207106
v -0.207106 0.130000 -0.207106
v 1.499999 0.130000 0.085787
v 1.207107 0.130000 1.207107
v 0.914213 0.130000 1.499999
v -0.207107 0.130000 1.207106
v -0.500000 0.130000 0.085787
v 0.914213 0.130000 -0.500000
v 1.500000 0.130000 0.914213
v 0.085787 0.130000 1.500000
v 0.500000 0.112500 0.500000
v -0.499999 0.112500 0.914213
v 0.085787 0.112500 -0.499999
@ -114,25 +113,17 @@ vt 0.031250 0.925857
vt 0.468750 0.925857
vt 0.982995 0.750000
vt 0.982995 0.375000
vt 0.750000 0.937500
vt 0.559359 0.937500
vt 0.727903 0.768956
vt 0.750000 0.791054
vt 0.531250 0.965609
vt 0.750000 0.965610
vt 0.772097 0.768957
vt 0.750000 0.791054
vt 0.031250 0.925857
vt 0.031250 0.687500
vt 0.468750 0.687500
vt 0.468750 0.925857
vt 0.968750 0.965609
vt 0.940641 0.937500
vt 0.750000 0.937500
vt 0.750000 0.965610
vt 0.559360 0.937500
vt 0.531250 0.965610
vt 0.750000 0.965610
vt 0.517005 1.000000
vt 0.750000 1.000000
vt 0.982995 1.000000
@ -145,93 +136,92 @@ vt 0.031250 0.593750
vt 0.031250 0.625000
vt 0.000000 0.625000
vt 0.482995 0.687500
vt 0.042893 0.625000
vt 0.457107 0.625000
vt 0.500000 0.609375
vt 0.000000 0.609375
s 0
s 1
usemtl casing
f 14/1/1 13/2/1 3/3/1 4/4/1
f 10/5/2 11/6/2 1/7/2 3/8/2
f 58/9/2 15/10/2 9/11/2 10/12/2
f 57/9/2 15/10/2 9/11/2 10/12/2
f 10/5/2 9/13/2 12/14/2 11/6/2
f 3/15/3 1/3/3 2/4/3 4/16/3
f 2/16/4 1/15/4 28/2/4 29/1/4
f 30/17/2 12/18/2 7/19/2 8/20/2
f 11/21/2 12/18/2 30/17/2 61/22/2
f 6/23/2 9/11/2 15/10/2 5/24/2
f 9/25/2 6/26/2 7/27/2 12/28/2
f 30/10/2 12/17/2 7/18/2 8/19/2
f 11/20/2 12/17/2 30/10/2 60/9/2
f 6/21/2 9/11/2 15/10/2 5/19/2
f 9/13/2 6/22/2 7/23/2 12/14/2
f 29/1/4 28/2/4 18/3/4 19/4/4
f 24/5/2 25/6/2 16/7/2 18/8/2
f 61/9/2 30/10/2 23/11/2 24/12/2
f 60/9/2 30/10/2 23/11/2 24/12/2
f 24/5/2 23/13/2 26/14/2 25/6/2
f 18/15/5 16/3/5 17/4/5 19/16/5
f 17/16/6 16/15/6 43/2/6 27/1/6
f 44/17/2 26/18/2 21/19/2 22/20/2
f 25/29/2 26/30/2 44/31/2 60/32/2
f 20/23/2 23/11/2 30/10/2 8/24/2
f 23/25/2 20/26/2 21/27/2 26/28/2
f 44/10/2 26/17/2 21/18/2 22/19/2
f 25/24/2 26/25/2 44/10/2 59/9/2
f 20/21/2 23/11/2 30/10/2 8/19/2
f 23/13/2 20/22/2 21/23/2 26/14/2
f 27/1/6 43/2/6 33/3/6 34/4/6
f 39/5/2 40/6/2 31/7/2 33/8/2
f 60/32/2 44/10/2 38/33/2 39/34/2
f 59/9/2 44/10/2 38/26/2 39/27/2
f 39/5/2 38/13/2 41/14/2 40/6/2
f 33/15/7 31/3/7 32/4/7 34/16/7
f 32/16/8 31/15/8 55/2/8 42/1/8
f 56/17/2 41/18/2 36/19/2 37/20/2
f 40/21/2 41/18/2 56/17/2 59/35/2
f 35/23/2 38/11/2 44/10/2 22/24/2
f 38/25/2 35/26/2 36/27/2 41/28/2
f 56/10/2 41/17/2 36/18/2 37/19/2
f 40/20/2 41/17/2 56/10/2 58/9/2
f 35/21/2 38/11/2 44/10/2 22/19/2
f 38/13/2 35/22/2 36/23/2 41/14/2
f 42/1/8 55/2/8 47/3/8 48/4/8
f 52/5/2 53/6/2 45/7/2 47/8/2
f 59/35/2 56/10/2 51/11/2 52/12/2
f 58/9/2 56/10/2 51/11/2 52/12/2
f 52/5/2 51/13/2 54/14/2 53/6/2
f 47/15/9 45/3/9 46/4/9 48/16/9
f 46/16/1 45/15/1 13/2/1 14/1/1
f 15/17/2 54/18/2 50/19/2 5/20/2
f 53/21/2 54/18/2 15/17/2 58/9/2
f 49/23/2 51/11/2 56/10/2 37/24/2
f 51/25/2 49/26/2 50/27/2 54/28/2
f 45/36/2 53/21/2 58/9/2 13/37/2
f 13/37/2 58/9/2 10/12/2 3/38/2
f 55/37/2 59/35/2 52/12/2 47/38/2
f 31/36/2 40/21/2 59/35/2 55/37/2
f 43/37/2 60/32/2 39/34/2 33/36/2
f 16/38/2 25/29/2 60/32/2 43/37/2
f 28/37/2 61/9/2 24/12/2 18/38/2
f 1/36/2 11/21/2 61/22/2 28/37/2
f 4/39/10 63/40/10 65/41/10 14/42/10
f 63/43/3 71/44/3 84/45/3 76/46/3
f 19/47/10 17/39/10 72/48/10 66/49/10
f 72/43/6 67/44/6 80/45/6 85/46/6
f 19/39/10 66/40/10 64/41/10 29/42/10
f 64/43/4 66/44/4 79/45/4 77/46/4
f 34/47/10 32/39/10 73/48/10 68/49/10
f 62/43/9 70/44/9 83/45/9 75/46/9
f 34/39/10 68/40/10 67/41/10 27/42/10
f 73/43/8 69/44/8 82/45/8 86/46/8
f 48/47/10 46/39/10 70/48/10 62/49/10
f 66/43/5 72/44/5 85/45/5 79/46/5
f 48/39/10 62/40/10 69/41/10 42/42/10
f 67/43/6 68/44/6 81/45/6 80/46/6
f 70/43/1 65/44/1 78/45/1 83/46/1
f 65/43/1 63/44/1 76/45/1 78/46/1
f 70/49/10 46/47/10 14/42/10 65/41/10
f 71/43/4 64/44/4 77/45/4 84/46/4
f 73/49/10 32/47/10 42/42/10 69/41/10
f 68/43/7 73/44/7 86/45/7 81/46/7
f 72/49/10 17/47/10 27/42/10 67/41/10
f 69/43/8 62/44/8 75/45/8 82/46/8
f 71/49/10 2/47/10 29/42/10 64/41/10
f 4/47/10 2/39/10 71/48/10 63/49/10
f 76/43/10 84/46/10 74/50/10
f 76/46/10 74/50/10 78/51/10
f 79/43/10 85/46/10 74/50/10
f 79/46/10 74/50/10 77/51/10
f 81/43/10 86/46/10 74/50/10
f 81/46/10 74/50/10 80/51/10
f 75/43/10 83/46/10 74/50/10
f 75/46/10 74/50/10 82/51/10
f 74/50/10 83/43/10 78/51/10
f 74/50/10 86/43/10 82/51/10
f 74/50/10 85/43/10 80/51/10
f 74/50/10 84/43/10 77/51/10
f 15/10/2 54/17/2 50/18/2 5/19/2
f 53/20/2 54/17/2 15/10/2 57/9/2
f 49/21/2 51/11/2 56/10/2 37/19/2
f 51/13/2 49/22/2 50/23/2 54/14/2
f 45/28/2 53/20/2 57/9/2 13/29/2
f 13/29/2 57/9/2 10/12/2 3/30/2
f 55/29/2 58/9/2 52/12/2 47/30/2
f 31/28/2 40/20/2 58/9/2 55/29/2
f 43/29/2 59/9/2 39/27/2 33/28/2
f 16/30/2 25/24/2 59/9/2 43/29/2
f 28/29/2 60/9/2 24/12/2 18/30/2
f 1/28/2 11/20/2 60/9/2 28/29/2
f 4/31/10 62/32/10 64/33/10 14/34/10
f 62/35/3 70/36/3 83/37/3 75/38/3
f 19/39/10 17/31/10 71/32/10 65/40/10
f 71/35/6 66/36/6 79/37/6 84/38/6
f 19/31/10 65/32/10 63/33/10 29/34/10
f 63/35/4 65/36/4 78/37/4 76/38/4
f 34/39/10 32/31/10 72/32/10 67/40/10
f 61/35/9 69/36/9 82/37/9 74/38/9
f 34/31/10 67/32/10 66/33/10 27/34/10
f 72/35/8 68/36/8 81/37/8 85/38/8
f 48/39/10 46/31/10 69/32/10 61/40/10
f 65/35/5 71/36/5 84/37/5 78/38/5
f 48/31/10 61/32/10 68/33/10 42/34/10
f 66/35/6 67/36/6 80/37/6 79/38/6
f 69/35/1 64/36/1 77/37/1 82/38/1
f 64/35/1 62/36/1 75/37/1 77/38/1
f 69/40/10 46/39/10 14/34/10 64/33/10
f 70/35/4 63/36/4 76/37/4 83/38/4
f 72/40/10 32/39/10 42/34/10 68/33/10
f 67/35/7 72/36/7 85/37/7 80/38/7
f 71/40/10 17/39/10 27/34/10 66/33/10
f 68/35/8 61/36/8 74/37/8 81/38/8
f 70/40/10 2/39/10 29/34/10 63/33/10
f 4/39/10 2/31/10 70/32/10 62/40/10
f 75/35/10 83/38/10 73/41/10
f 75/38/10 73/41/10 77/42/10
f 78/35/10 84/38/10 73/41/10
f 78/38/10 73/41/10 76/42/10
f 80/35/10 85/38/10 73/41/10
f 80/38/10 73/41/10 79/42/10
f 74/35/10 82/38/10 73/41/10
f 74/38/10 73/41/10 81/42/10
f 73/41/10 82/35/10 77/42/10
f 73/41/10 85/35/10 81/42/10
f 73/41/10 84/35/10 79/42/10
f 73/41/10 83/35/10 76/42/10

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 B

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 B

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 B

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 B

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

After

Width:  |  Height:  |  Size: 6.8 KiB