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

# Conflicts:
#	gradle.properties
#	src/generated/resources/.cache/82992cbf8f2794d83ac94034835eac0acd7915b9
#	src/generated/resources/.cache/dc4224e5ed0ee367217e022442da0b7476174a87
#	src/generated/resources/data/create/advancement/recipes/misc/crafting/kinetics/encased_chain_drive_from_zinc.json
#	src/generated/resources/data/create/recipe/crafting/curiosities/item_copying.json
#	src/generated/resources/data/create/recipe/crafting/kinetics/encased_chain_drive_from_zinc.json
#	src/main/java/com/simibubi/create/api/contraption/storage/item/menu/MountedStorageMenus.java
#	src/main/java/com/simibubi/create/content/decoration/copycat/CopycatPanelBlock.java
#	src/main/java/com/simibubi/create/content/equipment/clipboard/ClipboardBlockItem.java
#	src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelBehaviour.java
#	src/main/java/com/simibubi/create/content/logistics/packager/PackagerBlock.java
#	src/main/java/com/simibubi/create/content/logistics/packagerLink/LogisticallyLinkedBlockItem.java
#	src/main/java/com/simibubi/create/content/logistics/vault/ItemVaultMountedStorage.java
#	src/main/java/com/simibubi/create/content/redstone/link/controller/LecternControllerBlockEntity.java
#	src/main/java/com/simibubi/create/foundation/data/RuntimeDataGenerator.java
#	src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java
#	src/main/java/com/simibubi/create/foundation/events/CommonEvents.java
#	src/main/java/com/simibubi/create/infrastructure/data/CreateRegistrateTags.java
This commit is contained in:
IThundxr 2025-02-07 15:15:09 -05:00
commit 7c670cd7e1
Failed to generate hash of commit
69 changed files with 4234 additions and 4363 deletions

View file

@ -46,6 +46,7 @@ _Now using Flywheel 1.0_
- All links now use a new ingredient item, the transmitter
- New advancement chain for high logistics components
- New ponder scenes and category for high logistics components
- Tracks and Trains now have special integration with FTBChunks and Journeymap
- Depots can now be used as storage blocks on contraptions
- Brass tunnels now try to distribute an item more quickly when it first arrives
- Brass tunnels now always prefer filtered sides over non-filtered sides
@ -55,6 +56,7 @@ _Now using Flywheel 1.0_
- In common cobblegen scenarios, stationary drills now skip breaking blocks and just insert the result items into open
inventories directly below
- Held clipboards can now copy entries from other in-world clipboards
- Filters, Clipboards and Schedules can now be copyied in the crafting table
- Metal ladders no longer require a wall if another ladder block is above them
- Bells assembled to elevator contraptions now activate when arriving at a floor
- Sliding doors placed in front of contraption-mounted sliding doors now open and close automatically
@ -68,6 +70,13 @@ _Now using Flywheel 1.0_
- Wood cutting recipes in mechanical saws
- Added pressing recipes for coarse dirt and rooted dirt which both produce dirt paths (#7186)
- Updated JEI integration and added potion fluids to the JEI sidebar (#6934)
- Chain Drives can now be crafted from zinc nuggets
- Redstone lamps can now be picked up with the wrench
- New compatibility recipes for Immersive Engineering
- Added missing deploying recipes for copper oxidisation
- Framed and tiled glass panes can now be obtained via stonecutting
- Schematicannon on 'replace blocks with empty' now send block updates at the edges after printing
- The player hitbox used in contraption collision is now slightly shorter
#### Bug Fixes
@ -98,7 +107,6 @@ _Now using Flywheel 1.0_
- Fixed mechanical arm interactions with jukeboxes (#5902)
- Fixed toolboxes not giving a comparator output signal (#6973)
- Fixed copper slabs and stairs being missing from the respective tags (#3080)
- Fix incorrect copycat panel culling when framed glass is used and sodium is installed (Fabricators-of-Create#1540)
- Fixed Fix waterlogged bracketed kinetics dropping the bracket (Fabricators-of-Create#1552)
- Switched away from using streams in ContraptionCollider fixing a rare crash (#5043)
- Fixed pumps not placing fluids into flowing fluids of the same type (#5884)
@ -116,6 +124,10 @@ _Now using Flywheel 1.0_
- Fixed inability to mill cactus when Quark is installed (#7215)
- Fixed rare spout crash and offset rendering (#7025)
- Fixed deploying food resulting in missing particles and not returning the correct items (#7288)
- Fixed trains not properly pathfinding to stations with an opposing signal just behind the destination
- Fixed stations voiding schedules when disassembling the train
- Fixed lighting on signal block indicators
- Fixed vaults and tanks rotated in place not updating their multiblock correctly
#### API Changes

View file

@ -6,9 +6,7 @@ org.gradle.caching = true
# Mod Info
# build_info_mod_version is the version that gets filled into CreateBuildInfo.java
mod_version = 6.0.0
build_info_mod_version = 6.0.0-experimental
# Move this into the buildscript
maven_group = com.simibubi.create
build_info_mod_version = 6.0.0
# Mod Dependencies
minecraft_version = 1.21.1

View file

@ -1220,6 +1220,7 @@
"create.gui.factory_panel.left_click_reset": "ʇǝsǝɹ oʇ ʞɔıןƆ-ʇɟǝꞀ",
"create.gui.factory_panel.no_open_promises": "sǝsıɯoɹd uǝdo oN",
"create.gui.factory_panel.no_target_amount_set": "ʇǝs ʇunoɯɐ ʇǝbɹɐʇ ou :ǝʌıʇɔɐuI",
"create.gui.factory_panel.place_item_to_monitor": "ɹoʇıuoW oʇ ɯǝʇI ǝɔɐןԀ",
"create.gui.factory_panel.promise_prevents_oversending": "˙buıpuǝs-ɹǝʌo sʇuǝʌǝɹd sıɥ⟘",
"create.gui.factory_panel.promised_items": "sɯǝʇı pǝsıɯoɹԀ",
"create.gui.factory_panel.promises_do_not_expire": "ǝɹıdxǝ ʇou op sǝsıɯoɹԀ",
@ -1529,7 +1530,7 @@
"create.logistically_linked.protected": "pǝʇɔǝʇoɹԀ sı ʞɹoʍʇǝN sɔıʇsıboꞀ",
"create.logistically_linked.tooltip": "pǝɹnbıɟuoɔ ʎɔuǝnbǝɹℲ",
"create.logistically_linked.tooltip_clear": "ʇǝsǝɹ oʇ pıɹb buıʇɟɐɹɔ uı ǝɔɐןԀ",
"create.logistically_linked.tuned": uıן sıɥʇ oʇ pǝun⟘",
"create.logistically_linked.tuned": ɹoʍʇǝu sıɥʇ oʇ pǝun⟘",
"create.logistics.crafter.click_to_merge": "sǝıɹoʇuǝʌuI ǝbɹǝɯ oʇ ʞɔıןƆ",
"create.logistics.crafter.click_to_separate": "sǝıɹoʇuǝʌuI ǝʇɐɹɐdǝs oʇ ʞɔıןƆ",
"create.logistics.crafter.connected": "sɹǝʇɟɐɹƆ pǝʇɔǝuuoƆ",

View file

@ -1220,6 +1220,7 @@
"create.gui.factory_panel.left_click_reset": "Left-Click to reset",
"create.gui.factory_panel.no_open_promises": "No open promises",
"create.gui.factory_panel.no_target_amount_set": "Inactive: no target amount set",
"create.gui.factory_panel.place_item_to_monitor": "Place Item to Monitor",
"create.gui.factory_panel.promise_prevents_oversending": "This prevents over-sending.",
"create.gui.factory_panel.promised_items": "Promised items",
"create.gui.factory_panel.promises_do_not_expire": "Promises do not expire",
@ -1529,7 +1530,7 @@
"create.logistically_linked.protected": "Logistics Network is Protected",
"create.logistically_linked.tooltip": "Frequency configured",
"create.logistically_linked.tooltip_clear": "Place in crafting grid to reset",
"create.logistically_linked.tuned": "Tuned to this link",
"create.logistically_linked.tuned": "Tuned to this network",
"create.logistics.crafter.click_to_merge": "Click to merge Inventories",
"create.logistics.crafter.click_to_separate": "Click to separate Inventories",
"create.logistics.crafter.connected": "Connected Crafters",

View file

@ -0,0 +1,35 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_item": {
"conditions": {
"items": [
{
"items": [
"create:andesite_casing"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "create:crafting/kinetics/encased_chain_drive_from_zinc"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"create:crafting/kinetics/encased_chain_drive_from_zinc"
]
},
"sends_telemetry_event": false
}

View file

@ -0,0 +1,4 @@
{
"type": "create:item_copying",
"category": "misc"
}

View file

@ -0,0 +1,21 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
{
"item": "create:andesite_casing"
},
{
"tag": "forge:nuggets/zinc"
},
{
"tag": "forge:nuggets/zinc"
},
{
"tag": "forge:nuggets/zinc"
}
],
"result": {
"item": "create:encased_chain_drive"
}
}

View file

@ -9,6 +9,7 @@
"minecraft:redstone_torch",
"minecraft:repeater",
"minecraft:lever",
"minecraft:redstone_lamp",
"minecraft:comparator",
"minecraft:observer",
"minecraft:redstone_wall_torch",

View file

@ -4,6 +4,8 @@ import com.simibubi.create.content.equipment.blueprint.BlueprintMenu;
import com.simibubi.create.content.equipment.blueprint.BlueprintScreen;
import com.simibubi.create.content.equipment.toolbox.ToolboxMenu;
import com.simibubi.create.content.equipment.toolbox.ToolboxScreen;
import com.simibubi.create.content.logistics.factoryBoard.FactoryPanelSetItemMenu;
import com.simibubi.create.content.logistics.factoryBoard.FactoryPanelSetItemScreen;
import com.simibubi.create.content.logistics.filter.AttributeFilterMenu;
import com.simibubi.create.content.logistics.filter.AttributeFilterScreen;
import com.simibubi.create.content.logistics.filter.FilterMenu;
@ -76,6 +78,9 @@ public class AllMenuTypes {
public static final MenuEntry<RedstoneRequesterMenu> REDSTONE_REQUESTER =
register("redstone_requester", RedstoneRequesterMenu::new, () -> RedstoneRequesterScreen::new);
public static final MenuEntry<FactoryPanelSetItemMenu> FACTORY_PANEL_SET_ITEM =
register("factory_panel_set_item", FactoryPanelSetItemMenu::new, () -> FactoryPanelSetItemScreen::new);
private static <C extends AbstractContainerMenu, S extends Screen & MenuAccess<C>> MenuEntry<C> register(
String name, ForgeMenuFactory<C> factory, NonNullSupplier<ScreenFactory<C, S>> screenFactory) {
return Create.REGISTRATE

View file

@ -158,6 +158,7 @@ public class AllPartialModels {
BOGEY_FRAME = block("track/bogey/bogey_frame"), SMALL_BOGEY_WHEELS = block("track/bogey/bogey_wheel"),
BOGEY_PIN = block("track/bogey/bogey_drive_wheel_pin"), BOGEY_PISTON = block("track/bogey/bogey_drive_piston"),
BOGEY_DRIVE = block("track/bogey/bogey_drive"), LARGE_BOGEY_WHEELS = block("track/bogey/bogey_drive_wheel"),
BOGEY_DRIVE_BELT = block("track/bogey/bogey_drive_belt"),
TRAIN_COUPLING_HEAD = block("track/bogey/coupling_head"),
TRAIN_COUPLING_CABLE = block("track/bogey/coupling_cable"),

View file

@ -33,6 +33,7 @@ import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder.Pro
import com.simibubi.create.content.processing.recipe.ProcessingRecipeSerializer;
import com.simibubi.create.content.processing.sequenced.SequencedAssemblyRecipeSerializer;
import com.simibubi.create.foundation.recipe.IRecipeTypeInfo;
import com.simibubi.create.foundation.recipe.ItemCopyingRecipe;
import net.createmod.catnip.lang.Lang;
import net.minecraft.core.registries.BuiltInRegistries;
@ -72,7 +73,8 @@ public enum AllRecipeTypes implements IRecipeTypeInfo, StringRepresentable {
MECHANICAL_CRAFTING(MechanicalCraftingRecipe.Serializer::new),
SEQUENCED_ASSEMBLY(SequencedAssemblyRecipeSerializer::new),
TOOLBOX_DYEING(() -> new SimpleCraftingRecipeSerializer<>(ToolboxDyeingRecipe::new), () -> RecipeType.CRAFTING, false);
TOOLBOX_DYEING(() -> new SimpleCraftingRecipeSerializer<>(ToolboxDyeingRecipe::new), () -> RecipeType.CRAFTING, false),
ITEM_COPYING(() -> new SimpleCraftingRecipeSerializer<>(ItemCopyingRecipe::new), () -> RecipeType.CRAFTING, false);
public static final Predicate<RecipeHolder<?>> CAN_BE_AUTOMATED = r -> !r.id()
.getPath()

View file

@ -94,6 +94,8 @@ public class AllSpriteShifts {
ANDESIDE_BELT_CASING = get("block/belt/brass_belt_casing", "block/belt/andesite_belt_casing"),
CRAFTER_THINGIES = get("block/crafter_thingies", "block/crafter_thingies");
public static final SpriteShiftEntry BOGEY_BELT = get("block/bogey/belt", "block/bogey/belt_scroll");
static {
populateMaps();
}

View file

@ -34,6 +34,10 @@ public class MountedStorageMenus {
if (rows < 1 || rows > 6)
return null;
// make sure rows are full
if (handler.getSlots() % 9 != 0)
return null;
MenuType<?> type = GENERIC_CHEST_MENUS.get(rows - 1);
Container wrapper = new StorageInteractionWrapper(handler, stillValid, onClose);
MenuConstructor constructor = (id, inv, player) -> new ChestMenu(type, id, inv, wrapper, rows);

View file

@ -57,6 +57,7 @@ import com.simibubi.create.content.kinetics.fan.processing.SplashingRecipe;
import com.simibubi.create.content.kinetics.press.MechanicalPressBlockEntity;
import com.simibubi.create.content.kinetics.press.PressingRecipe;
import com.simibubi.create.content.kinetics.saw.CuttingRecipe;
import com.simibubi.create.content.logistics.factoryBoard.FactoryPanelSetItemScreen;
import com.simibubi.create.content.logistics.filter.AbstractFilterScreen;
import com.simibubi.create.content.logistics.redstoneRequester.RedstoneRequesterScreen;
import com.simibubi.create.content.processing.basin.BasinRecipe;
@ -408,6 +409,7 @@ public class CreateJEI implements IModPlugin {
registration.addGhostIngredientHandler(LinkedControllerScreen.class, new GhostIngredientHandler());
registration.addGhostIngredientHandler(ScheduleScreen.class, new GhostIngredientHandler());
registration.addGhostIngredientHandler(RedstoneRequesterScreen.class, new GhostIngredientHandler());
registration.addGhostIngredientHandler(FactoryPanelSetItemScreen.class, new GhostIngredientHandler());
}
private class CategoryBuilder<T extends Recipe<? extends RecipeInput>> {

View file

@ -162,11 +162,6 @@ public class CopycatPanelBlock extends WaterloggedCopycatBlock {
return false;
}
@Override
public boolean skipRendering(BlockState state, BlockState adjacentState, Direction direction) {
return state.equals(adjacentState) && direction.getAxis().isHorizontal();
}
@Override
public boolean supportsExternalFaceHiding(BlockState state) {
return true;

View file

@ -4,6 +4,8 @@ import javax.annotation.Nonnull;
import com.simibubi.create.AllDataComponents;
import com.simibubi.create.foundation.recipe.ItemCopyingRecipe.SupportsItemCopying;
import net.createmod.catnip.gui.ScreenOpener;
import net.createmod.catnip.platform.CatnipServices;
import net.minecraft.client.Minecraft;
@ -21,7 +23,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
public class ClipboardBlockItem extends BlockItem {
public class ClipboardBlockItem extends BlockItem implements SupportsItemCopying {
public ClipboardBlockItem(Block pBlock, Properties pProperties) {
super(pBlock, pProperties);

View file

@ -13,8 +13,6 @@ import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.NotNull;
import org.joml.Math;
@ -33,6 +31,8 @@ import com.simibubi.create.content.logistics.packager.InventorySummary;
import com.simibubi.create.content.logistics.packager.PackagerBlockEntity;
import com.simibubi.create.content.logistics.packager.PackagingRequest;
import com.simibubi.create.content.logistics.packagerLink.LogisticallyLinkedBehaviour.RequestType;
import com.simibubi.create.content.logistics.packagerLink.LogisticallyLinkedBlockItem;
import com.simibubi.create.content.logistics.packagerLink.LogisticallyLinkedClientHandler;
import com.simibubi.create.content.logistics.packagerLink.LogisticsManager;
import com.simibubi.create.content.logistics.packagerLink.RequestPromise;
import com.simibubi.create.content.logistics.packagerLink.RequestPromiseQueue;
@ -61,13 +61,17 @@ import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.Level;
@ -77,7 +81,7 @@ import net.minecraft.world.phys.BlockHitResult;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
public class FactoryPanelBehaviour extends FilteringBehaviour {
public class FactoryPanelBehaviour extends FilteringBehaviour implements MenuProvider {
public static final BehaviourType<FactoryPanelBehaviour> TOP_LEFT = new BehaviourType<>();
public static final BehaviourType<FactoryPanelBehaviour> TOP_RIGHT = new BehaviourType<>();
@ -284,6 +288,8 @@ public class FactoryPanelBehaviour extends FilteringBehaviour {
tickStorageMonitor();
bulb.updateChaseTarget(redstonePowered || satisfied ? 1 : 0);
bulb.tickChaser();
if (active)
tickOutline();
return;
}
@ -352,7 +358,7 @@ public class FactoryPanelBehaviour extends FilteringBehaviour {
&& promisedSatisfied == shouldPromiseSatisfy && waitingForNetwork == shouldWait)
return;
if (!satisfied && shouldSatisfy) {
if (!satisfied && shouldSatisfy && demand > 0) {
AllSoundEvents.CONFIRM.playOnServer(getWorld(), getPos(), 0.075f, 1f);
AllSoundEvents.CONFIRM_2.playOnServer(getWorld(), getPos(), 0.125f, 0.575f);
}
@ -529,6 +535,7 @@ public class FactoryPanelBehaviour extends FilteringBehaviour {
@Override
public void onShortInteract(Player player, InteractionHand hand, Direction side, BlockHitResult hitResult) {
// Network is protected
if (!Create.LOGISTICS.mayInteract(network, player)) {
player.displayClientMessage(CreateLang.translate("logistically_linked.protected")
.style(ChatFormatting.RED)
@ -536,6 +543,9 @@ public class FactoryPanelBehaviour extends FilteringBehaviour {
return;
}
boolean isClientSide = player.level().isClientSide;
// Wrench cycles through arrow bending
if (targeting.size() + targetedByLinks.size() > 0 && AllItemTags.WRENCH.matches(player.getItemInHand(hand))) {
int sharedMode = -1;
boolean notifySelf = false;
@ -550,7 +560,7 @@ public class FactoryPanelBehaviour extends FilteringBehaviour {
if (sharedMode == -1)
sharedMode = (connection.arrowBendMode + 1) % 4;
connection.arrowBendMode = sharedMode;
if (!player.level().isClientSide)
if (!isClientSide)
at.blockEntity.notifyUpdate();
}
@ -558,7 +568,7 @@ public class FactoryPanelBehaviour extends FilteringBehaviour {
if (sharedMode == -1)
sharedMode = (connection.arrowBendMode + 1) % 4;
connection.arrowBendMode = sharedMode;
if (!player.level().isClientSide)
if (!isClientSide)
notifySelf = true;
}
@ -575,16 +585,34 @@ public class FactoryPanelBehaviour extends FilteringBehaviour {
return;
}
if (player.level().isClientSide)
// Client might be in the process of connecting a panel
if (isClientSide)
if (FactoryPanelConnectionHandler.panelClicked(getWorld(), player, this))
return;
ItemStack heldItem = player.getItemInHand(hand);
if (getFilter().isEmpty()) {
// Open screen for setting an item through JEI
if (heldItem.isEmpty()) {
if (!isClientSide && player instanceof ServerPlayer sp)
sp.openMenu(this, buf -> FactoryPanelPosition.STREAM_CODEC.encode(buf, getPanelPosition()));
return;
}
// Use regular filter interaction for setting the item
super.onShortInteract(player, hand, side, hitResult);
return;
}
if (player.level().isClientSide)
// Bind logistics items to this panels' frequency
if (heldItem.getItem() instanceof LogisticallyLinkedBlockItem) {
if (!isClientSide)
LogisticallyLinkedBlockItem.assignFrequency(heldItem, player, network);
return;
}
// Open configuration screen
if (isClientSide)
CatnipServices.PLATFORM.executeOnClientOnly(() -> () -> displayScreen(player));
}
@ -995,4 +1023,21 @@ public class FactoryPanelBehaviour extends FilteringBehaviour {
public boolean writeToClipboard(@NotNull HolderLookup.Provider registries, CompoundTag tag, Direction side) {
return false;
}
private void tickOutline() {
CatnipServices.PLATFORM.executeOnClientOnly(() -> () -> LogisticallyLinkedClientHandler.tickPanel(this));
}
@Override
public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) {
return FactoryPanelSetItemMenu.create(containerId, playerInventory, this);
}
@Override
public Component getDisplayName() {
return blockEntity.getBlockState()
.getBlock()
.getName();
}
}

View file

@ -15,16 +15,18 @@ import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
import net.createmod.catnip.nbt.NBTHelper;
import net.createmod.catnip.math.VecHelper;
import net.createmod.catnip.nbt.NBTHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
@ -134,6 +136,13 @@ public class FactoryPanelBlockEntity extends SmartBlockEntity {
behaviour.setNetwork(frequency);
redraw = true;
lastShape = null;
if (activePanels() > 1) {
SoundType soundType = getBlockState().getSoundType();
level.playSound(null, worldPosition, soundType.getPlaceSound(), SoundSource.BLOCKS,
(soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F);
}
return true;
}
return false;
@ -145,6 +154,13 @@ public class FactoryPanelBlockEntity extends SmartBlockEntity {
behaviour.disable();
redraw = true;
lastShape = null;
if (activePanels() > 0) {
SoundType soundType = getBlockState().getSoundType();
level.playSound(null, worldPosition, soundType.getBreakSound(), SoundSource.BLOCKS,
(soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F);
}
return true;
}
return false;

View file

@ -591,6 +591,9 @@ public class FactoryPanelScreen extends AbstractSimiScreen {
int x = guiLeft;
int y = guiTop;
if (addressBox.mouseScrolled(mouseX, mouseY, scrollX, scrollY))
return true;
if (craftingActive)
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);

View file

@ -0,0 +1,73 @@
package com.simibubi.create.content.logistics.factoryBoard;
import com.simibubi.create.AllMenuTypes;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.foundation.gui.menu.GhostItemMenu;
import com.simibubi.create.foundation.utility.CreateLang;
import net.minecraft.client.Minecraft;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.MenuType;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.items.ItemStackHandler;
import net.neoforged.neoforge.items.SlotItemHandler;
public class FactoryPanelSetItemMenu extends GhostItemMenu<FactoryPanelBehaviour> {
public FactoryPanelSetItemMenu(MenuType<?> type, int id, Inventory inv, FactoryPanelBehaviour contentHolder) {
super(type, id, inv, contentHolder);
}
public FactoryPanelSetItemMenu(MenuType<?> type, int id, Inventory inv, RegistryFriendlyByteBuf extraData) {
super(type, id, inv, extraData);
}
public static FactoryPanelSetItemMenu create(int id, Inventory inv, FactoryPanelBehaviour be) {
return new FactoryPanelSetItemMenu(AllMenuTypes.FACTORY_PANEL_SET_ITEM.get(), id, inv, be);
}
@Override
protected ItemStackHandler createGhostInventory() {
return new ItemStackHandler(1);
}
@Override
protected boolean allowRepeats() {
return true;
}
@Override
@OnlyIn(Dist.CLIENT)
protected FactoryPanelBehaviour createOnClient(RegistryFriendlyByteBuf extraData) {
FactoryPanelPosition pos = FactoryPanelPosition.STREAM_CODEC.decode(extraData);
return FactoryPanelBehaviour.at(Minecraft.getInstance().level, pos);
}
@Override
protected void addSlots() {
int playerX = 13;
int playerY = 112;
int slotX = 74;
int slotY = 28;
addPlayerSlots(playerX, playerY);
addSlot(new SlotItemHandler(ghostInventory, 0, slotX, slotY));
}
@Override
protected void saveData(FactoryPanelBehaviour contentHolder) {
if (!contentHolder.setFilter(ghostInventory.getStackInSlot(0))) {
player.displayClientMessage(CreateLang.translateDirect("logistics.filter.invalid_item"), true);
AllSoundEvents.DENY.playOnServer(player.level(), player.blockPosition(), 1, 1);
return;
}
player.level()
.playSound(null, contentHolder.getPos(), SoundEvents.ITEM_FRAME_ADD_ITEM, SoundSource.BLOCKS, .25f, .1f);
}
}

View file

@ -0,0 +1,69 @@
package com.simibubi.create.content.logistics.factoryBoard;
import java.util.Collections;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.gui.menu.AbstractSimiContainerScreen;
import com.simibubi.create.foundation.gui.widget.IconButton;
import com.simibubi.create.foundation.utility.CreateLang;
import net.createmod.catnip.gui.element.GuiGameElement;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
public class FactoryPanelSetItemScreen extends AbstractSimiContainerScreen<FactoryPanelSetItemMenu> {
private IconButton confirmButton;
private List<Rect2i> extraAreas = Collections.emptyList();
public FactoryPanelSetItemScreen(FactoryPanelSetItemMenu container, Inventory inv, Component title) {
super(container, inv, title);
}
@Override
protected void init() {
int bgHeight = AllGuiTextures.FACTORY_GAUGE_SET_ITEM.getHeight();
int bgWidth = AllGuiTextures.FACTORY_GAUGE_SET_ITEM.getWidth();
setWindowSize(bgWidth, bgHeight + AllGuiTextures.PLAYER_INVENTORY.getHeight());
super.init();
clearWidgets();
int x = getGuiLeft();
int y = getGuiTop();
confirmButton = new IconButton(x + bgWidth - 40, y + bgHeight - 25, AllIcons.I_CONFIRM);
confirmButton.withCallback(() -> minecraft.player.closeContainer());
addRenderableWidget(confirmButton);
extraAreas = List.of(new Rect2i(x + bgWidth, y + bgHeight - 30, 40, 20));
}
@Override
protected void renderBg(GuiGraphics pGuiGraphics, float pPartialTick, int pMouseX, int pMouseY) {
int x = getGuiLeft();
int y = getGuiTop();
AllGuiTextures.FACTORY_GAUGE_SET_ITEM.render(pGuiGraphics, x - 5, y);
renderPlayerInventory(pGuiGraphics, x + 5, y + 94);
ItemStack stack = AllBlocks.FACTORY_GAUGE.asStack();
Component title = CreateLang.translate("gui.factory_panel.place_item_to_monitor")
.component();
pGuiGraphics.drawString(font, title, x + imageWidth / 2 - font.width(title) / 2 - 5, y + 4, 0x3D3C48, false);
GuiGameElement.of(stack)
.scale(3)
.render(pGuiGraphics, x + 180, y + 48);
}
@Override
public List<Rect2i> getExtraAreas() {
return extraAreas;
}
}

View file

@ -15,6 +15,7 @@ import com.simibubi.create.AllKeys;
import com.simibubi.create.content.logistics.box.PackageItem;
import com.simibubi.create.content.logistics.item.filter.attribute.ItemAttribute;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.recipe.ItemCopyingRecipe.SupportsItemCopying;
import com.simibubi.create.foundation.utility.CreateLang;
import net.minecraft.ChatFormatting;
@ -40,7 +41,7 @@ import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.items.ItemStackHandler;
public class FilterItem extends Item implements MenuProvider {
public class FilterItem extends Item implements MenuProvider, SupportsItemCopying {
private FilterType type;

View file

@ -76,6 +76,13 @@ public class PackageFilterScreen extends AbstractFilterScreen<PackageFilterMenu>
return super.mouseClicked(pMouseX, pMouseY, pButton);
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
if (addressBox.mouseScrolled(mouseX, mouseY, scrollX, scrollY))
return true;
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
}
@Override
public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) {
if (pKeyCode == GLFW.GLFW_KEY_ENTER)

View file

@ -85,6 +85,8 @@ public class PackagerBlock extends WrenchableDirectionalBlock implements IBE<Pac
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
if (AllBlocks.STOCK_LINK.isIn(stack) && !state.getValue(LINKED))
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
if (AllBlocks.PACKAGE_FROGPORT.isIn(stack))
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
if (onBlockEntityUseItemOn(level, pos, be -> {
if (be.heldBox.isEmpty()) {

View file

@ -88,13 +88,7 @@ public class LogisticallyLinkedBlockItem extends BlockItem {
if (!link.mayInteractMessage(player))
return InteractionResult.SUCCESS;
CompoundTag tag = stack.getOrDefault(DataComponents.BLOCK_ENTITY_DATA, CustomData.EMPTY).copyTag();
tag.putUUID("Freq", link.freqId);
player.displayClientMessage(CreateLang.translateDirect("logistically_linked.tuned"), true);
BlockEntity.addEntityType(tag, ((IBE<?>) this.getBlock()).getBlockEntityType());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
assignFrequency(stack, player, link.freqId);
return InteractionResult.SUCCESS;
}
@ -107,4 +101,14 @@ public class LogisticallyLinkedBlockItem extends BlockItem {
return useOn;
}
public static void assignFrequency(ItemStack stack, Player player, UUID frequency) {
CompoundTag tag = stack.getOrDefault(DataComponents.BLOCK_ENTITY_DATA, CustomData.EMPTY).copyTag();
tag.putUUID("Freq", frequency);
player.displayClientMessage(CreateLang.translateDirect("logistically_linked.tuned"), true);
BlockEntity.addEntityType(tag, ((IBE<?>) ((BlockItem) stack.getItem()).getBlock()).getBlockEntityType());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
}
}

View file

@ -4,6 +4,8 @@ import java.util.UUID;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.content.logistics.factoryBoard.FactoryPanelBehaviour;
import com.simibubi.create.content.logistics.factoryBoard.FactoryPanelConnectionHandler;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import net.createmod.catnip.animation.AnimationTickHolder;
@ -19,7 +21,11 @@ import net.minecraft.world.phys.shapes.VoxelShape;
public class LogisticallyLinkedClientHandler {
private static UUID previouslyHeldFrequency;
public static void tick() {
previouslyHeldFrequency = null;
LocalPlayer player = Minecraft.getInstance().player;
if (player == null)
return;
@ -33,6 +39,8 @@ public class LogisticallyLinkedClientHandler {
return;
UUID uuid = tag.getUUID("Freq");
previouslyHeldFrequency = uuid;
for (LogisticallyLinkedBehaviour behaviour : LogisticallyLinkedBehaviour.getAllPresent(uuid, false, true)) {
SmartBlockEntity be = behaviour.blockEntity;
VoxelShape shape = be.getBlockState()
@ -46,7 +54,8 @@ public class LogisticallyLinkedClientHandler {
.size(); i++) {
AABB aabb = shape.toAabbs()
.get(i);
Outliner.getInstance().showAABB(Pair.of(behaviour, i), aabb.inflate(-1 / 128f)
Outliner.getInstance()
.showAABB(Pair.of(behaviour, i), aabb.inflate(-1 / 128f)
.move(be.getBlockPos()), 2)
.lineWidth(1 / 32f)
.disableLineNormals()
@ -56,4 +65,24 @@ public class LogisticallyLinkedClientHandler {
}
}
public static void tickPanel(FactoryPanelBehaviour fpb) {
if (previouslyHeldFrequency == null)
return;
if (!previouslyHeldFrequency.equals(fpb.network))
return;
LocalPlayer player = Minecraft.getInstance().player;
if (player == null)
return;
if (!player.blockPosition()
.closerThan(fpb.getPos(), 64))
return;
Outliner.getInstance()
.showAABB(fpb, FactoryPanelConnectionHandler.getBB(fpb.blockEntity.getBlockState(), fpb.getPanelPosition())
.inflate(-1.5 / 128f))
.lineWidth(1 / 32f)
.disableLineNormals()
.colored(AnimationTickHolder.getTicks() % 16 < 8 ? 0x708DAD : 0x90ADCD);
}
}

View file

@ -28,6 +28,7 @@ import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.items.SlotItemHandler;
public class RedstoneRequesterScreen extends AbstractSimiContainerScreen<RedstoneRequesterMenu> {
@ -177,6 +178,9 @@ public class RedstoneRequesterScreen extends AbstractSimiContainerScreen<Redston
int x = getGuiLeft();
int y = getGuiTop();
if (addressBox.mouseScrolled(mouseX, mouseY, scrollX, scrollY))
return true;
for (int i = 0; i < amounts.size(); i++) {
int inputX = x + 27 + i * 20;
int inputY = y + 28;

View file

@ -6,15 +6,19 @@ import com.mojang.serialization.MapCodec;
import com.simibubi.create.AllMountedStorageTypes;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.contraption.storage.item.WrapperMountedItemStorage;
import com.simibubi.create.content.contraptions.Contraption;
import com.simibubi.create.foundation.utility.CreateCodecs;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.neoforged.neoforge.items.ItemStackHandler;
public class ItemVaultMountedStorage extends WrapperMountedItemStorage<ItemStackHandler> {
@ -37,6 +41,12 @@ public class ItemVaultMountedStorage extends WrapperMountedItemStorage<ItemStack
}
}
@Override
public boolean handleInteraction(ServerPlayer player, Contraption contraption, StructureBlockInfo info) {
// vaults should never be opened.
return false;
}
@Override
public boolean providesFuel() {
return false;

View file

@ -0,0 +1,63 @@
package com.simibubi.create.content.processing.burner;
import org.joml.Quaternionf;
import org.joml.Quaternionfc;
import dev.engine_room.flywheel.api.instance.InstanceHandle;
import dev.engine_room.flywheel.api.instance.InstanceType;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import net.createmod.catnip.render.SpriteShiftEntry;
import net.minecraft.core.Vec3i;
public class ScrollTransformedInstance extends TransformedInstance {
public float speedU;
public float speedV;
public float offsetU;
public float offsetV;
public float diffU;
public float diffV;
public float scaleU;
public float scaleV;
public ScrollTransformedInstance(InstanceType<? extends TransformedInstance> type, InstanceHandle handle) {
super(type, handle);
}
public ScrollTransformedInstance setSpriteShift(SpriteShiftEntry spriteShift) {
return setSpriteShift(spriteShift, 0.5f, 0.5f);
}
public ScrollTransformedInstance setSpriteShift(SpriteShiftEntry spriteShift, float factorU, float factorV) {
float spriteWidth = spriteShift.getTarget()
.getU1()
- spriteShift.getTarget()
.getU0();
float spriteHeight = spriteShift.getTarget()
.getV1()
- spriteShift.getTarget()
.getV0();
scaleU = spriteWidth * factorU;
scaleV = spriteHeight * factorV;
diffU = spriteShift.getTarget().getU0() - spriteShift.getOriginal().getU0();
diffV = spriteShift.getTarget().getV0() - spriteShift.getOriginal().getV0();
return this;
}
public ScrollTransformedInstance speed(float speedU, float speedV) {
this.speedU = speedU;
this.speedV = speedV;
return this;
}
public ScrollTransformedInstance offset(float offsetU, float offsetV) {
this.offsetU = offsetU;
this.offsetV = offsetV;
return this;
}
}

View file

@ -69,7 +69,7 @@ public class LecternControllerBlockEntity extends SmartBlockEntity {
}
public ItemStack getController() {
return getController();
return createLinkedController();
}
public boolean hasUser() { return user != null; }

View file

@ -4,6 +4,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllPartialModels;
import com.simibubi.create.AllSpriteShifts;
import com.simibubi.create.content.kinetics.simpleRelays.ShaftBlock;
import net.createmod.catnip.render.CachedBuffers;
@ -14,6 +15,7 @@ import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.Blocks;
public class StandardBogeyRenderer implements BogeyRenderer {
@ -59,6 +61,9 @@ public class StandardBogeyRenderer implements BogeyRenderer {
}
public static class Large extends StandardBogeyRenderer {
public static final float BELT_RADIUS_PX = 5f;
public static final float BELT_RADIUS_IN_UV_SPACE = BELT_RADIUS_PX / 16f;
@Override
public void render(CompoundTag bogeyData, float wheelAngle, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int light, int overlay, boolean inContraption) {
super.render(bogeyData, wheelAngle, partialTick, poseStack, bufferSource, light, overlay, inContraption);
@ -83,6 +88,22 @@ public class StandardBogeyRenderer implements BogeyRenderer {
.overlay(overlay)
.renderInto(poseStack, buffer);
float spriteSize = AllSpriteShifts.BOGEY_BELT.getTarget()
.getV1()
- AllSpriteShifts.BOGEY_BELT.getTarget()
.getV0();
float scroll = BELT_RADIUS_IN_UV_SPACE * Mth.DEG_TO_RAD * wheelAngle;
scroll = scroll - Mth.floor(scroll);
scroll = scroll * spriteSize * 0.5f;
CachedBuffers.partial(AllPartialModels.BOGEY_DRIVE_BELT, Blocks.AIR.defaultBlockState())
.scale(1 - 1 / 512f)
.light(light)
.overlay(overlay)
.shiftUVScrolling(AllSpriteShifts.BOGEY_BELT, scroll)
.renderInto(poseStack, buffer);
CachedBuffers.partial(AllPartialModels.BOGEY_PISTON, Blocks.AIR.defaultBlockState())
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)))
.light(light)

View file

@ -6,6 +6,9 @@ import org.jetbrains.annotations.Nullable;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllPartialModels;
import com.simibubi.create.AllSpriteShifts;
import com.simibubi.create.content.processing.burner.ScrollTransformedInstance;
import com.simibubi.create.foundation.render.AllInstanceTypes;
import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
@ -15,6 +18,7 @@ import dev.engine_room.flywheel.lib.model.Models;
import net.createmod.catnip.math.AngleHelper;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Mth;
public class StandardBogeyVisual implements BogeyVisual {
private final TransformedInstance shaft1;
@ -139,6 +143,7 @@ public class StandardBogeyVisual implements BogeyVisual {
private final TransformedInstance secondaryShaft1;
private final TransformedInstance secondaryShaft2;
private final TransformedInstance drive;
private final ScrollTransformedInstance belt;
private final TransformedInstance piston;
private final TransformedInstance wheels;
private final TransformedInstance pin;
@ -152,6 +157,9 @@ public class StandardBogeyVisual implements BogeyVisual {
drive = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.BOGEY_DRIVE))
.createInstance();
belt = ctx.instancerProvider()
.instancer(AllInstanceTypes.SCROLLING_TRANSFORMED, Models.partial(AllPartialModels.BOGEY_DRIVE_BELT))
.createInstance();
piston = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.BOGEY_PISTON))
.createInstance();
@ -161,6 +169,8 @@ public class StandardBogeyVisual implements BogeyVisual {
pin = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.BOGEY_PIN))
.createInstance();
belt.setSpriteShift(AllSpriteShifts.BOGEY_BELT);
}
@Override
@ -183,6 +193,10 @@ public class StandardBogeyVisual implements BogeyVisual {
drive.setTransform(poseStack)
.scale(1 - 1/512f)
.setChanged();
belt.offset(0, StandardBogeyRenderer.Large.BELT_RADIUS_IN_UV_SPACE * Mth.DEG_TO_RAD * wheelAngle)
.setTransform(poseStack)
.scale(1 - 1/512f)
.setChanged();
piston.setTransform(poseStack)
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)))
.setChanged();
@ -205,6 +219,7 @@ public class StandardBogeyVisual implements BogeyVisual {
secondaryShaft2.setZeroTransform().setChanged();
wheels.setZeroTransform().setChanged();
drive.setZeroTransform().setChanged();
belt.setZeroTransform().setChanged();
piston.setZeroTransform().setChanged();
pin.setZeroTransform().setChanged();
}
@ -216,6 +231,7 @@ public class StandardBogeyVisual implements BogeyVisual {
secondaryShaft2.light(packedLight).setChanged();
wheels.light(packedLight).setChanged();
drive.light(packedLight).setChanged();
belt.light(packedLight).setChanged();
piston.light(packedLight).setChanged();
pin.light(packedLight).setChanged();
}
@ -227,6 +243,7 @@ public class StandardBogeyVisual implements BogeyVisual {
consumer.accept(secondaryShaft2);
consumer.accept(wheels);
consumer.accept(drive);
consumer.accept(belt);
consumer.accept(piston);
consumer.accept(pin);
}
@ -238,6 +255,7 @@ public class StandardBogeyVisual implements BogeyVisual {
secondaryShaft2.delete();
wheels.delete();
drive.delete();
belt.delete();
piston.delete();
pin.delete();
}

View file

@ -749,7 +749,7 @@ public class Navigation {
if (station.canApproachFrom(newNode) && stationTest.test(newDistance, newDistance + newPenalty, reachedVia,
Pair.of(Couple.create(node2, newNode), newEdge), station)) {
hasDestination = true;
continue;
break;
}
if (!isOwnStation)
newPenalty += Train.Penalties.STATION;

View file

@ -11,6 +11,7 @@ import com.simibubi.create.content.trains.entity.CarriageContraptionEntity;
import com.simibubi.create.content.trains.entity.Train;
import com.simibubi.create.content.trains.schedule.destination.DestinationInstruction;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.recipe.ItemCopyingRecipe.SupportsItemCopying;
import com.simibubi.create.foundation.utility.CreateLang;
import net.createmod.catnip.data.Couple;
@ -37,7 +38,7 @@ import net.minecraft.world.level.Level;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
public class ScheduleItem extends Item implements MenuProvider {
public class ScheduleItem extends Item implements MenuProvider, SupportsItemCopying {
public ScheduleItem(Properties pProperties) {
super(pProperties);

View file

@ -12,6 +12,7 @@ import com.simibubi.create.foundation.blockEntity.renderer.SafeBlockEntityRender
import dev.engine_room.flywheel.lib.transform.TransformStack;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.render.CachedBuffers;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
@ -34,6 +35,7 @@ public class SignalRenderer extends SafeBlockEntityRenderer<SignalBlockEntity> {
float renderTime = AnimationTickHolder.getRenderTime(be.getLevel());
if (signalState.isRedLight(renderTime))
CachedBuffers.partial(AllPartialModels.SIGNAL_ON, blockState)
.light(LightTexture.FULL_BLOCK)
.renderInto(ms, buffer.getBuffer(RenderType.solid()));
else
CachedBuffers.partial(AllPartialModels.SIGNAL_OFF, blockState)

View file

@ -442,7 +442,7 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
if (!train.disassemble(getAssemblyDirection(), trackPosition.above()))
return false;
dropSchedule(sender);
dropSchedule(sender, train);
return true;
}
@ -473,12 +473,10 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
return true;
}
public void dropSchedule(@Nullable ServerPlayer sender) {
public void dropSchedule(@Nullable ServerPlayer sender, @Nullable Train train) {
GlobalStation station = getStation();
if (station == null)
return;
Train train = station.getPresentTrain();
if (train == null)
return;

View file

@ -61,9 +61,12 @@ public class StationEditPacket extends BlockEntityConfigurationPacket<StationBlo
Level level = be.getLevel();
BlockPos blockPos = be.getBlockPos();
BlockState blockState = level.getBlockState(blockPos);
GlobalStation station = be.getStation();
if (dropSchedule) {
be.dropSchedule(player);
if (station == null)
return;
be.dropSchedule(player, station.getPresentTrain());
return;
}
@ -84,8 +87,7 @@ public class StationEditPacket extends BlockEntityConfigurationPacket<StationBlo
return;
if (tryAssemble) {
be.assemble(player.getUUID());
assemblyComplete = be.getStation() != null && be.getStation()
.getPresentTrain() != null;
assemblyComplete = station != null && station.getPresentTrain() != null;
} else {
if (be.tryDisassembleTrain(player) && be.tryEnterAssemblyMode())
be.refreshAssemblyInfo();

View file

@ -1,11 +1,16 @@
package com.simibubi.create.foundation.recipe;
package com.simibubi.create.foundation.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jetbrains.annotations.ApiStatus;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.JsonElement;
import com.mojang.serialization.JsonOps;
import com.simibubi.create.Create;
@ -14,12 +19,15 @@ import com.simibubi.create.content.processing.recipe.ProcessingRecipe;
import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
import com.simibubi.create.content.processing.recipe.ProcessingRecipeSerializer;
import com.simibubi.create.foundation.pack.DynamicPack;
import com.simibubi.create.foundation.recipe.IRecipeTypeInfo;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.createmod.catnip.codecs.CatnipCodecUtils;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagEntry;
import net.minecraft.tags.TagFile;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.crafting.Recipe;
@ -30,6 +38,7 @@ import net.neoforged.neoforge.common.conditions.WithConditions;
public class RuntimeDataGenerator {
private static final Pattern STRIPPED_WOODS_REGEX = Pattern.compile("stripped_(\\w*)_(log|wood|stem|hyphae)");
private static final Pattern NON_STRIPPED_WOODS_REGEX = Pattern.compile("(?!stripped_)([a-z]+)_(log|wood|stem|hyphae)");
private static final Multimap<ResourceLocation, TagEntry> TAGS = HashMultimap.create();
private static final Object2ObjectOpenHashMap<ResourceLocation, JsonElement> JSON_FILES = new Object2ObjectOpenHashMap<>();
public static void insertIntoPack(DynamicPack dynamicPack) {
@ -37,17 +46,22 @@ public class RuntimeDataGenerator {
cuttingRecipes(itemId);
Create.LOGGER.info("Created {} recipes which will be injected into the game", JSON_FILES.size());
JSON_FILES.forEach(dynamicPack::put);
Create.LOGGER.info("Created {} tags which will be injected into the game", TAGS.size());
for (Map.Entry<ResourceLocation, Collection<TagEntry>> tags : TAGS.asMap().entrySet()) {
TagFile tagFile = new TagFile(new ArrayList<>(tags.getValue()), false);
dynamicPack.put(tags.getKey().withPrefix("tags/items/"), TagFile.CODEC.encodeStart(JsonOps.INSTANCE, tagFile).result().orElseThrow());
}
JSON_FILES.clear();
JSON_FILES.trim();
TAGS.clear();
}
// logs/woods -> stripped variants
// logs/woods both stripped and non stripped -> planks
// planks -> stairs, slabs, fences, fence gates, doors, trapdoors, pressure plates, buttons and signs
// also adds stripped logs and woods into the create tag for those
private static void cuttingRecipes(ResourceLocation itemId) {
String path = itemId.getPath();
@ -64,7 +78,8 @@ public class RuntimeDataGenerator {
if (hasFoundMatch) {
String type = match.group(2);
ResourceLocation base = itemId.withPath(match.group(1) + "_");
ResourceLocation matched = itemId.withPath(match.group(1));
ResourceLocation base = matched.withSuffix("_");
ResourceLocation nonStrippedId = base.withSuffix(type);
ResourceLocation planksId = base.withSuffix("planks");
ResourceLocation stairsId = base.withSuffix("stairs");
@ -80,8 +95,12 @@ public class RuntimeDataGenerator {
if (!noStrippedVariant) {
simpleWoodRecipe(nonStrippedId, itemId);
simpleWoodRecipe(itemId, planksId, 6);
} else {
simpleWoodRecipe(TagKey.create(Registries.ITEM, nonStrippedId.withSuffix("s")), planksId, 6);
} else if (BuiltInRegistries.ITEM.containsKey(planksId)) {
ResourceLocation tag = Create.asResource("runtime_generated/compat/" + matched.getPath());
insertIntoTag(tag, itemId);
insertIntoTag(tag, nonStrippedId);
simpleWoodRecipe(TagKey.create(Registries.ITEM, tag), planksId, 6);
}
if (!path.contains("_wood") && !path.contains("_hyphae") && BuiltInRegistries.ITEM.containsKey(planksId)) {
@ -98,6 +117,11 @@ public class RuntimeDataGenerator {
}
}
private static void insertIntoTag(ResourceLocation tag, ResourceLocation itemId) {
if (BuiltInRegistries.ITEM.containsKey(itemId))
TAGS.put(tag, TagEntry.optionalElement(itemId));
}
private static void simpleWoodRecipe(ResourceLocation inputId, ResourceLocation outputId) {
simpleWoodRecipe(inputId, outputId, 1);
}

View file

@ -18,10 +18,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import net.createmod.catnip.registry.RegisteredObjectsHelper;
import org.jetbrains.annotations.NotNull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.jetbrains.annotations.Nullable;
@ -39,12 +35,13 @@ import com.simibubi.create.content.decoration.palettes.AllPaletteBlocks;
import com.simibubi.create.content.decoration.palettes.AllPaletteStoneTypes;
import com.simibubi.create.content.equipment.toolbox.ToolboxDyeingRecipe;
import com.simibubi.create.foundation.mixin.accessor.MappedRegistryAccessor;
import com.simibubi.create.foundation.recipe.ItemCopyingRecipe;
import com.simibubi.create.infrastructure.codec.CombiningCodec;
import com.tterrag.registrate.util.entry.BlockEntry;
import com.tterrag.registrate.util.entry.ItemEntry;
import com.tterrag.registrate.util.entry.ItemProviderEntry;
import net.createmod.catnip.platform.CatnipServices;
import net.createmod.catnip.registry.RegisteredObjectsHelper;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementHolder;
@ -85,6 +82,7 @@ import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.neoforged.neoforge.common.Tags;
import net.neoforged.neoforge.common.conditions.ICondition;
import net.neoforged.neoforge.common.conditions.ModLoadedCondition;
@ -231,6 +229,7 @@ public class StandardRecipeGen extends CreateRecipeProvider {
.pattern(" L ")),
TOOLBOX_DYEING = createSpecial(ToolboxDyeingRecipe::new, "crafting", "toolbox_dyeing"),
ITEM_COPYING = createSpecial(ItemCopyingRecipe::new, "crafting", "item_copying"),
MINECART_COUPLING = create(AllItems.MINECART_COUPLING).unlockedBy(I::andesiteAlloy)
.viaShaped(b -> b.define('E', I.andesiteAlloy())
@ -888,6 +887,12 @@ public class StandardRecipeGen extends CreateRecipeProvider {
.requires(I.ironNugget())
.requires(I.ironNugget())),
ENCASED_CHAIN_DRIVE_ZINC = create(AllBlocks.ENCASED_CHAIN_DRIVE).withSuffix("_from_zinc").unlockedBy(I::andesiteCasing)
.viaShapeless(b -> b.requires(I.andesiteCasing())
.requires(I.zincNugget())
.requires(I.zincNugget())
.requires(I.zincNugget())),
FLYWHEEL = create(AllBlocks.FLYWHEEL).unlockedByTag(I::brass)
.viaShaped(b -> b.define('C', I.brass())
.define('A', I.shaft())

View file

@ -51,11 +51,11 @@ import com.simibubi.create.content.redstone.displayLink.DisplayLinkBlockEntity;
import com.simibubi.create.content.redstone.link.controller.LinkedControllerServerHandler;
import com.simibubi.create.content.trains.entity.CarriageEntityHandler;
import com.simibubi.create.content.trains.station.StationBlockEntity;
import com.simibubi.create.foundation.data.RuntimeDataGenerator;
import com.simibubi.create.foundation.map.StationMapDecorationRenderer;
import com.simibubi.create.foundation.pack.DynamicPack;
import com.simibubi.create.foundation.pack.DynamicPackSource;
import com.simibubi.create.foundation.recipe.RecipeFinder;
import com.simibubi.create.foundation.recipe.RuntimeDataGenerator;
import com.simibubi.create.foundation.utility.ServerSpeedProvider;
import com.simibubi.create.foundation.utility.TickBasedCache;
import com.simibubi.create.infrastructure.command.AllCommands;
@ -70,6 +70,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.gui.map.RegisterMapDecorationRenderersEvent;

View file

@ -156,6 +156,7 @@ public enum AllGuiTextures implements ScreenElement, TextureSheetSegment {
FACTORY_GAUGE_RECIPE("factory_gauge", 32, 0, 192, 96),
FACTORY_GAUGE_RESTOCK("factory_gauge", 32, 112, 192, 40),
FACTORY_GAUGE_BOTTOM("factory_gauge", 32, 176, 200, 64),
FACTORY_GAUGE_SET_ITEM("requester", 16, 160, 184, 88),
STOCK_KEEPER_REQUEST_HEADER("stock_keeper", 0, 0, 256, 36),
STOCK_KEEPER_REQUEST_BODY("stock_keeper", 0, 48, 256, 20),

View file

@ -0,0 +1,98 @@
package com.simibubi.create.foundation.recipe;
import javax.annotation.Nullable;
import com.simibubi.create.AllRecipeTypes;
import net.createmod.catnip.data.IntAttached;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CraftingInput;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
public class ItemCopyingRecipe extends CustomRecipe {
public static interface SupportsItemCopying {
public default ItemStack createCopy(ItemStack original, int count) {
ItemStack copyWithCount = original.copyWithCount(count);
copyWithCount.remove(DataComponents.ENCHANTMENTS);
copyWithCount.remove(DataComponents.STORED_ENCHANTMENTS);
return copyWithCount;
}
public default boolean canCopyFromItem(ItemStack item) {
return item.isComponentsPatchEmpty();
}
public default boolean canCopyToItem(ItemStack item) {
return !item.isComponentsPatchEmpty();
}
}
public ItemCopyingRecipe(CraftingBookCategory category) {
super(category);
}
@Override
public boolean matches(CraftingInput input, Level level) {
return copyCheck(input) != null;
}
@Override
public ItemStack assemble(CraftingInput input, HolderLookup.Provider registries) {
IntAttached<ItemStack> copyCheck = copyCheck(input);
if (copyCheck == null)
return ItemStack.EMPTY;
ItemStack itemToCopy = copyCheck.getValue();
if (!(itemToCopy.getItem() instanceof SupportsItemCopying sic))
return ItemStack.EMPTY;
return sic.createCopy(itemToCopy, copyCheck.getFirst() + 1);
}
@Nullable
private IntAttached<ItemStack> copyCheck(CraftingInput input) {
ItemStack itemToCopy = ItemStack.EMPTY;
int copyTargets = 0;
for (int j = 0; j < input.size(); ++j) {
ItemStack itemInSlot = input.getItem(j);
if (itemInSlot.isEmpty())
continue;
if (!itemToCopy.isEmpty() && itemToCopy.getItem() != itemInSlot.getItem())
return null;
if (!(itemInSlot.getItem() instanceof SupportsItemCopying sic))
continue;
if (sic.canCopyFromItem(itemInSlot)) {
if (!itemToCopy.isEmpty())
return null;
itemToCopy = itemInSlot;
continue;
}
if (sic.canCopyToItem(itemInSlot))
copyTargets++;
}
if (itemToCopy.isEmpty() || copyTargets == 0)
return null;
return IntAttached.with(copyTargets, itemToCopy);
}
public RecipeSerializer<?> getSerializer() {
return AllRecipeTypes.ITEM_COPYING.getSerializer();
}
public boolean canCraftInDimensions(int width, int height) {
return width >= 2 && height >= 2;
}
}

View file

@ -9,6 +9,7 @@ import org.lwjgl.system.MemoryUtil;
import com.simibubi.create.content.kinetics.base.RotatingInstance;
import com.simibubi.create.content.processing.burner.ScrollInstance;
import com.simibubi.create.content.processing.burner.ScrollTransformedInstance;
import dev.engine_room.flywheel.api.instance.InstanceType;
import dev.engine_room.flywheel.api.layout.FloatRepr;
@ -87,6 +88,41 @@ public class AllInstanceTypes {
})
.build();
// TODO: Switch everything using SCROLLING to this? Right now this is only used for bogey belts.
// This takes a decent few more bytes to represent but perhaps it can be packed
// down into 96 by sacrificing precision
public static final InstanceType<ScrollTransformedInstance> SCROLLING_TRANSFORMED = SimpleInstanceType.builder(ScrollTransformedInstance::new)
.cullShader(asResource("instance/cull/scrolling_transformed.glsl"))
.vertexShader(asResource("instance/scrolling_transformed.vert"))
.layout(LayoutBuilder.create()
.matrix("pose", FloatRepr.FLOAT, 4)
.vector("color", FloatRepr.NORMALIZED_UNSIGNED_BYTE, 4)
.vector("light", IntegerRepr.SHORT, 2)
.vector("overlay", IntegerRepr.SHORT, 2)
.vector("speed", FloatRepr.FLOAT, 2)
.vector("diff", FloatRepr.FLOAT, 2)
.vector("scale", FloatRepr.FLOAT, 2)
.vector("offset", FloatRepr.FLOAT, 2)
.build())
.writer((ptr, instance) -> {
ExtraMemoryOps.putMatrix4f(ptr, instance.pose);
MemoryUtil.memPutByte(ptr + 64, instance.red);
MemoryUtil.memPutByte(ptr + 65, instance.green);
MemoryUtil.memPutByte(ptr + 66, instance.blue);
MemoryUtil.memPutByte(ptr + 67, instance.alpha);
ExtraMemoryOps.put2x16(ptr + 68, instance.light);
ExtraMemoryOps.put2x16(ptr + 72, instance.overlay);
MemoryUtil.memPutFloat(ptr + 76, instance.speedU);
MemoryUtil.memPutFloat(ptr + 80, instance.speedV);
MemoryUtil.memPutFloat(ptr + 84, instance.diffU);
MemoryUtil.memPutFloat(ptr + 88, instance.diffV);
MemoryUtil.memPutFloat(ptr + 92, instance.scaleU);
MemoryUtil.memPutFloat(ptr + 96, instance.scaleV);
MemoryUtil.memPutFloat(ptr + 100, instance.offsetU);
MemoryUtil.memPutFloat(ptr + 104, instance.offsetV);
})
.build();
public static void init() {
// noop
}

View file

@ -105,7 +105,7 @@ public class CreateRegistrateTags {
.addTag(BlockTags.WOOL);
prov.tag(AllBlockTags.WRENCH_PICKUP.tag)
.add(Blocks.REDSTONE_WIRE, Blocks.REDSTONE_TORCH, Blocks.REPEATER, Blocks.LEVER,
.add(Blocks.REDSTONE_WIRE, Blocks.REDSTONE_TORCH, Blocks.REPEATER, Blocks.LEVER, Blocks.REDSTONE_LAMP,
Blocks.COMPARATOR, Blocks.OBSERVER, Blocks.REDSTONE_WALL_TORCH, Blocks.PISTON, Blocks.STICKY_PISTON,
Blocks.TRIPWIRE, Blocks.TRIPWIRE_HOOK, Blocks.DAYLIGHT_DETECTOR, Blocks.TARGET, Blocks.HOPPER)
.addTag(BlockTags.BUTTONS)

View file

@ -0,0 +1,5 @@
#include "flywheel:util/matrix.glsl"
void flw_transformBoundingSphere(in FlwInstance i, inout vec3 center, inout float radius) {
transformBoundingSphere(i.pose, center, radius);
}

View file

@ -0,0 +1,12 @@
#include "flywheel:util/matrix.glsl"
void flw_instanceVertex(in FlwInstance instance) {
flw_vertexPos = instance.pose * flw_vertexPos;
flw_vertexNormal = mat3(transpose(inverse(instance.pose))) * flw_vertexNormal;
vec2 scroll = fract(instance.speed * flw_renderTicks + instance.offset) * instance.scale;
flw_vertexTexCoord = flw_vertexTexCoord + instance.diff + scroll;
flw_vertexOverlay = instance.overlay;
flw_vertexLight = max(vec2(instance.light) / 256., flw_vertexLight);
}

View file

@ -1044,7 +1044,7 @@
"create.display_link.display_on": "Write data to:",
"create.display_link.display_on_multiline": "Start writing at:",
"create.logistically_linked.tuned": "Tuned to this link",
"create.logistically_linked.tuned": "Tuned to this network",
"create.logistically_linked.new_network_started": "New link network started",
"create.logistically_linked.connected": "Connected to existing network successfully",
"create.logistically_linked.tooltip": "Frequency configured",
@ -1094,6 +1094,7 @@
"create.gui.factory_panel.address_missing": "Inactive: missing a target address",
"create.gui.factory_panel.no_target_amount_set": "Inactive: no target amount set",
"create.gui.factory_panel.has_link_connections": "Gauge has connected links",
"create.gui.factory_panel.place_item_to_monitor": "Place Item to Monitor",
"create.gui.redstone_requester.allow_partial": "Allow partial orders",
"create.gui.redstone_requester.dont_allow_partial": "Must send all items",

View file

@ -1,8 +1,5 @@
# Blender MTL File: 'Bogey.blend'
# Material Count: 4
newmtl Belts
map_Kd #belt
# Blender 4.3.2 MTL File: 'Bogey.blend'
# www.blender.org
newmtl Bogey_Body
map_Kd #bogey

View file

@ -0,0 +1,6 @@
{
"parent": "create:block/track/bogey/textures",
"loader": "forge:obj",
"flip_v": true,
"model": "create:models/block/track/bogey/bogey_drive_belt.obj"
}

View file

@ -0,0 +1,5 @@
# Blender 4.3.2 MTL File: 'Bogey.blend'
# www.blender.org
newmtl Belts
map_Kd #belt

View file

@ -0,0 +1,140 @@
# Blender 4.3.2
# www.blender.org
mtllib bogey_drive_belt.mtl
o Cube.030
v 0.500000 1.000000 -1.062500
v 0.687500 1.000000 -1.062500
v 0.500000 1.310930 -0.311848
v 0.687500 1.310930 -0.311848
v -0.500000 1.000000 -1.062500
v -0.687500 1.000000 -1.062500
v -0.500000 1.310930 -0.311848
v -0.687500 1.310930 -0.311848
v 0.500000 1.000000 1.062500
v 0.687500 1.000000 1.062500
v 0.500000 1.310930 0.311848
v 0.687500 1.310930 0.311848
v -0.500000 1.000000 1.062500
v -0.687500 1.000000 1.062500
v -0.500000 1.310930 0.311848
v -0.687500 1.310930 0.311848
v 0.500000 0.656250 0.939394
v 0.687500 0.656250 0.939394
v 0.500000 0.656250 -1.060606
v 0.687500 0.656250 -1.060606
v 0.500000 0.656250 -0.060606
v 0.687500 0.656250 -0.060606
v -0.500000 0.593750 0.939394
v -0.687500 0.593750 0.939394
v -0.500000 0.593750 -1.060606
v -0.687500 0.593750 -1.060606
v -0.500000 0.593750 -0.060606
v -0.687500 0.593750 -0.060606
v 0.500000 0.942259 -1.038583
v 0.687500 0.942259 -1.038583
v 0.500000 1.253189 -0.287931
v 0.687500 1.253189 -0.287931
v -0.500000 0.942259 -1.038583
v -0.687500 0.942259 -1.038583
v -0.500000 1.253189 -0.287931
v -0.687500 1.253189 -0.287931
v 0.500000 0.942259 1.038583
v 0.687500 0.942259 1.038583
v 0.500000 1.253189 0.287931
v 0.687500 1.253189 0.287931
v -0.500000 0.942259 1.038583
v -0.687500 0.942259 1.038583
v -0.500000 1.253189 0.287931
v -0.687500 1.253189 0.287931
v 0.500000 0.593750 0.939394
v 0.687500 0.593750 0.939394
v 0.500000 0.593750 -1.060606
v 0.687500 0.593750 -1.060606
v 0.500000 0.593750 -0.060606
v 0.687500 0.593750 -0.060606
v -0.500000 0.531250 0.939394
v -0.687500 0.531250 0.939394
v -0.500000 0.531250 -1.060606
v -0.687500 0.531250 -1.060606
v -0.500000 0.531250 -0.060606
v -0.687500 0.531250 -0.060606
vn -0.0000 0.9239 -0.3827
vn -0.0000 0.9239 0.3827
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.9239 0.3827
vn -0.0000 -0.9239 -0.3827
vn -0.0000 -1.0000 -0.0000
vn -0.0000 -0.3827 -0.9239
vn -0.0000 0.3827 0.9239
vn 1.0000 -0.0000 -0.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.3827 0.9239
vn -0.0000 0.3827 -0.9239
vn -0.0000 -0.0000 1.0000
vn -0.0000 -0.0000 -1.0000
vt 0.312500 0.187500
vt 0.312500 1.000000
vt 0.125000 1.000000
vt 0.125000 0.187500
vt 0.687500 0.187500
vt 0.875000 0.187500
vt 0.875000 1.000000
vt 0.687500 1.000000
vt 0.312500 0.000000
vt 0.125000 0.000000
vt 0.187500 0.187500
vt 0.187500 1.000000
vt 0.250000 1.000000
vt 0.250000 0.187500
vt 0.812500 1.000000
vt 0.812500 0.187500
vt 0.750000 0.187500
vt 0.750000 1.000000
vt 0.250000 0.000000
vt 0.187500 0.000000
s 0
usemtl Belts
f 1/1/1 3/2/1 4/3/1 2/4/1
f 5/5/1 6/6/1 8/7/1 7/8/1
f 9/2/2 10/3/2 12/4/2 11/1/2
f 13/8/2 15/5/2 16/6/2 14/7/2
f 21/9/3 22/10/3 20/3/3 19/2/3
f 17/9/3 18/10/3 22/3/3 21/2/3
f 27/2/3 25/9/3 26/10/3 28/3/3
f 23/9/3 27/2/3 28/3/3 24/10/3
f 29/1/4 30/4/4 32/3/4 31/2/4
f 33/5/4 35/8/4 36/7/4 34/6/4
f 37/2/5 39/1/5 40/4/5 38/3/5
f 41/8/5 42/7/5 44/6/5 43/5/5
f 49/9/6 47/2/6 48/3/6 50/10/6
f 45/9/6 49/2/6 50/3/6 46/10/6
f 55/2/6 56/3/6 54/10/6 53/9/6
f 51/9/6 52/10/6 56/3/6 55/2/6
f 1/1/7 2/4/7 30/4/7 29/1/7
f 4/3/8 3/2/8 31/2/8 32/3/8
f 2/11/9 4/12/9 32/3/9 30/4/9
f 3/13/10 1/14/10 29/1/10 31/2/10
f 6/6/7 5/5/7 33/5/7 34/6/7
f 7/8/8 8/7/8 36/7/8 35/8/8
f 8/15/10 6/16/10 34/6/10 36/7/10
f 5/17/9 7/18/9 35/8/9 33/5/9
f 10/3/11 9/2/11 37/2/11 38/3/11
f 11/1/12 12/4/12 40/4/12 39/1/12
f 12/11/9 10/12/9 38/3/9 40/4/9
f 9/13/10 11/14/10 39/1/10 37/2/10
f 13/8/11 14/7/11 42/7/11 41/8/11
f 16/6/12 15/5/12 43/5/12 44/6/12
f 14/15/10 16/16/10 44/6/10 42/7/10
f 15/17/9 13/18/9 41/8/9 43/5/9
f 17/19/10 21/13/10 49/2/10 45/9/10
f 18/10/13 17/9/13 45/9/13 46/10/13
f 20/12/9 22/20/9 50/10/9 48/3/9
f 19/2/14 20/3/14 48/3/14 47/2/14
f 21/19/10 19/13/10 47/2/10 49/9/10
f 22/12/9 18/20/9 46/10/9 50/3/9
f 27/13/9 23/19/9 51/9/9 55/2/9
f 23/9/13 24/10/13 52/10/13 51/9/13
f 28/12/10 26/20/10 54/10/10 56/3/10
f 26/10/14 25/2/14 53/2/14 54/10/14
f 25/13/9 27/19/9 55/9/9 53/2/9
f 24/20/10 28/12/10 56/3/10 52/10/10

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 B

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 B

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB